来自斯坦福网站的Algorithms: Design and Analysis,与目前coursera上的版本内容没有变化,不过时间安排略有不同。

1. Introduction

这里介绍了两个比较经典的算法问题作为引子,在这么课中将会学会解决这两个问题的算法。

1.1 Internet routing

因特网与图这个数据结构非常有关系。因特网本身可以看成一个图,结点是每个主机以及每个路由器,边是每个节点之间相连的网线或者无线网。而网络中还有其它可以看成图的,比如网页图。每个网页看成一个节点,网页间的超链接看作是边。还有社交网络等等。

网络路由问题:
假设从斯坦福的网关路由器发送数据到康奈尔,需要选择一条最短的传播路径,假设传播的跳数最少就是最短。那么这就是图中的一个最短路径问题。算法1中讲到的dijkstra算法可以解决这个问题,但是这个算法需要每个路由器知道全局信息,但这是不可能的。我们需要一个只知道local信息(与其相连的结点信息)就能得到最短路径的算法,这就是贝尔曼福特算法

1.2 sequence alignment

序列对齐问题:假设有两个字符串序列,需要判断它们相似的程度。相似的定义是,两个字符串对齐之后完全相同的字符所占比重。需要插入空格和对齐后的字符不相等都算是penalty。两个字符串对齐之后的penalty越小就说明它们越相似。这个penalty叫做NW score。
斯坦福 算法2 第一周笔记
于是我们需要一个算法来计算两个字符串的对齐。如果有两个长度为500的字符串,使用暴力做法尝试所有可能的对齐方式,这个复杂度是25002^{500},永远不可能实现。但是使用动态规划就可以很快解决这个问题。

2. Greedy Algorithms

2.1 Introduction

这部分介绍了贪心算法。贪心算法是四种算法的设计思想之一,这四个思想为:

  1. 分治法
  2. 随机算法
  3. 贪心算法
  4. 动态规划

贪心算法没有一个严格的定义,大致的意思是说,算法在当前情形下选择目前看来最好的选择,期望这么做到最后得到的结果也是最好的。一个例子就是dijkstra算法。

和分治法有很多不同点:

  1. 分治法对每个问题都需要有具体设计,贪心法是个很容易用到不同问题的方式。
  2. 分治法时间复杂度分析很困难,贪心法一般很简单。
  3. 分治法正确性比较容易证明,贪心法一般很难证明。

DANGER:大部分贪心算法都不正确。
比如虽然dijkstra算法是对的,但在有负权重的情况下继续贪心就会导致错误:
斯坦福 算法2 第一周笔记
正确性证明的几种方式:

  1. Induction(貌似包括演绎法与归纳法):比如dijkstra算法的证明。
  2. exchange argument:貌似指的是反证法,但感觉不完全是。意思就是假设一个最优解,然后与算法得到的解交换一些元素,得到矛盾就是反证法。也有可能会直接推导出算法当前解同样也是最优的。
  3. 其它任何方法。

2.1.1 Optional Caching

缓存的替换机制就是贪心法的一个应用。

缓存的替换不再介绍,但是缓存的替换有一个最优的指导原则,那就是去掉当前缓存中在未来被再次访问时距离当前时间最远的页。最优指的是类似缓存的访问miss最少。

不过算法不会预知未来,这个原则的意义在于:1.可以作为真实算法的指导。比如可以用这个原则知道利用data的locality性质LRU算法会效果更好一些。 2. 可以做一些用来测试的benchmark。

2.2 A SCHEDULING APPLICATION

一个调度问题定义为:有共享的资源(如一个处理器),有很多job需要做(如处理线程)。问题是该如何给这些job进行调度排序。可以假设每个job有一个表示优先度的weight,以及处理时间l。每个任务有个完成时间CjC_{j},表示当前任务以及之前所有任务的时间和。调度任务的目标函数是让完成时间的加权和最小,即minj=1nwjCjmin\sum_{j=1}^{n}w_{j}C_{j}

比如:三个任务,l1=1,l2=2,l3=3,w1=3,w2=2,w3=1l_{1} = 1, l_{2} = 2, l_{3} = 3, w_{1} = 3, w_{2}=2, w_{3}=1,它们顺序执行后完成时间的加权和为15。

算法设计:当每个人物时间相同时,显然让权重大的先执行。而当所有任务权重相同时,让时间少的先执行。但是一般情况下,我们同样需要一个“分数”来给任务进行排序。定义分数为wj/ljw_{j}/l_{j}然后按分数降序排列任务即可。

正确性证明: 我们需要证明的是,按分数wj/ljw_{j}/l_{j}进行降序排列后的顺序即最优。每个任务按照分数降序用[1, 2, 3, 4, … , n]表示。首先假设这个分数是无重复的。用反证法(argument exchange)可以很容易证明。

假设贪心算法得到的结果为α\alpha,其最优解为α\alpha*。而贪心法的结果α\alpha的执行顺序就是[1,2,3,4,…,n],假设这两者不相同,那么最优解中必然存在两个连续的任务i > j。
斯坦福 算法2 第一周笔记
然后发现交换最优解α\alpha*中的i和j的执行顺序后,得到的目标结果完成时间的加权和更小了(可以自己算算看)。也就是说假设不成立,也就是证明了最优解必须是贪心得到的结果。

这只解决了分数wj/ljw_{j}/l_{j}不重复的情况。如果存在重复时算法正确性依然可以证明。
正确性证明:假设α\alpha为贪心算法,α\alpha*是任意的其它算法。
斯坦福 算法2 第一周笔记
其它算法中必然也存在逆序对。我们将连续的逆序对进行交换,发现交换后的结果只会比交换前更好或一样好。也就是说按照贪心算法得到的结果[1,2,3,…,n]跟任意算法相比都是一样好或更好。也就证明了它必然是最优解。

3. Minimum Spanning Trees

最小生成树的非正式形式的目标是,将一堆点用最小的代价连起来。

最小生成树有两个很快的算法:Prim算法和Kruskal算法。前者与dijkstra很类似。

问题的正式定义如下:
斯坦福 算法2 第一周笔记
最小生成树T有两个特点:1.T中无环。2. T是一个联通图。
在本章中还有两个不影响结果的假设:1.原图G是联通的。 2. 每条边的cost不重复。(为了证明的简化)。

3.1 Prim’s MST Algorithm

斯坦福 算法2 第一周笔记
和dijkstra算法的区别在于每次假如结点考虑的是结点到集合的最短距离,而dijkstra算法是到源点s的最短距离。

3.2 Correctness Proof

第一步:证明Prim算法得到的是生成树。
首先定义图的割(cut)。一个割指的是将图G中的点分成非空两个部分的一个分割。

引理:Empty Cut Lemma,是说一个图G如果存在一个割(A,B),A和B之间没有边相连,那么这个图G是个非联通图。
斯坦福 算法2 第一周笔记
还有两个事实:
斯坦福 算法2 第一周笔记
证明过程如下:
斯坦福 算法2 第一周笔记
第二步:证明prim算法得到的生成树是最小生成树。
利用一个引理,割的性质。
斯坦福 算法2 第一周笔记
利用割的性质,我们知道Prim算法每一步加入的边都是最小生成树的边,而上一步已经证明的Prim算法最后的结果是一个生成树。那么就证明了这个树是一个最小生成树。

The Cut Property的证明(假设每个边的cost唯一):
利用反证法:如果割(A,B)间有一个最小cost的边e不属于MST T*。通过e与T中另外的边进行交换让T的总cost更小来得到矛盾。不过问题在于如何确定T*中与e交换的边。
斯坦福 算法2 第一周笔记
事实上,我们不能随意指定一条边f与e进行交换,这样可能会让交换后的结果无法构成生成树。但是确实存在一个边ee'可以完成与e交换的调剂。

已知T*是一个生成树,因此假如边e之后必然构成一个环。由前面的引理知道,这个环中必然有另一个边e’也跨越这个割(A,B)。因此可以选择将e与这个边e’进行交换。就能够完成The Cut Property的证明。(需要说服自己e与e‘交换之后得到的一定是一个生成树emmm,好的我说服自己了。)

3.3 Fast Implementation

用naive的方法,同样是需要进行n-1次循环,每次循环都要遍历每条边。因此复杂度会是O(mn)O(mn)
斯坦福 算法2 第一周笔记
因为算法与dijkstra算法如此相似,自然能够想到用堆来加速算法。

与dijkstra相比有两个不变的地方:1.堆中的元素依然是V-X集合中的点。2.对于集合V-X中的每个点v,key[v]是最短的边(u,v)的cost,其中uXu\in X。(这里应该变了吧,dijkstra算法里的key[v]是v到源点s的最短的cost吧???)

初始化堆的复杂度O(m+nlogn)=O(mlogn)O(m+nlogn) = O(mlogn)
算法流程如下:
斯坦福 算法2 第一周笔记
用堆加速后的Prim算法,需要在初始化时进行n-1次插入,一共进行n-1次输出最小操作。每条边都可能会触发一次堆的delete与insert操作。一共有O(m)O(m)次的堆操作,因此算法的复杂度O(mlogn)O(mlogn)

相关文章:

  • 2021-12-17
  • 2021-04-27
  • 2021-09-19
  • 2021-09-04
  • 2021-11-21
  • 2021-07-08
  • 2021-05-27
  • 2022-12-23
猜你喜欢
  • 2021-08-01
  • 2021-05-11
  • 2022-01-16
  • 2021-07-01
  • 2021-12-22
  • 2021-11-03
  • 2021-05-03
相关资源
相似解决方案