1、算 法 设 计 与 分 析 论 文题 目 0-1 背包问题的算法设计策略对比与分析专 业 班 级 学 号 姓 名 引言对于计算机科学来说,算法(Algorithm)的概念是至关重要的。算法是一系列解决问题的清晰指令,也就是说,能够对一定规范的输入,在有限时间内获得所要求的输出。如果一个算法有缺陷,或不适合于某个问题,执行这个算法将不会解决这个问题。不同的算法可能用不同的时间、空间或效率来完成同样的任务。一个算法的优劣可以用空间复杂度与时间复杂度来衡量。算法可以理解为有基本运算及规定的运算顺序所构成的完整的解题步骤。或者看成按照要求设计好的有限的确切的计算序列,并且这样的步骤和序列可以解决一类问
2、题。算法可以使用自然语言、伪代码、流程图等多种不同的方法来描述。一个算法应该具有以下五个重要的特征:有穷性:一个算法必须保证执行有限步之后结束;确切性:算法的每一步骤必须有确切的定义; 输入:一个算法有 0 个或多个输入,以刻画运算对象的初始情况,所谓 0 个输入是指算法本身定除了初始条件; 输出:一个算法有一个或多个输出,以反映对输入数据加工后的结果。没有输出的算法是毫无意义的; 可行性:算法原则上能够精确地运行,而且人们用笔和纸做有限次运算后即可完成。计算机科学家尼克劳斯-沃思曾著过一本著名的书数据结构十算法= 程序,可见算法在计算机科学界与计算机应用界的地位。1 算法复杂性分析的方法介绍
3、算法的复杂性是算法效率的度量,是评价算法优劣的重要依据。一个算法的复杂性的高低体现在运行该算法所需要的计算机资源的多少上面,所需的资源越多,我们就说该算法的复杂性越高;反之,所需的资源越低,则该算法的复杂性越低。 计算机的资源,最重要的是时间和空间(即存储器)资源。因而,算法的复杂性有时间复杂性和空间复杂性之分。 不言而喻,对于任意给定的问题,设计出复杂性尽可能地的算法是我们在设计算法是追求的一个重要目标;另一方面,当给定的问题已有多种算法时,选择其中复杂性最低者,是我们在选用算法适应遵循的一个重要准则。因此,算法的复杂性分析对算法的设计或选用有着重要的指导意义和实用价值。 关于算法的复杂性,
4、有两个问题要弄清楚:用怎样的一个量来表达一个算法的复杂性;对于给定的一个算法,怎样具体计算它的复杂性。让我们从比较两对具体算法的效率开始。1.1 比较两对算法的效率考虑问题 1:已知不重复且已经按从小到大排好的 m 个整数的数组 A1.m(为简单起见。还设 m=2 k,k 是一个确定的非负整数) 。对于给定的整数 c,要求寻找一个下标 i,使得 Ai=c;若找不到,则返回一个 0。问题 1 的一个简单的算法是:从头到尾扫描数组 A。照此,或者扫到 A 的第 i 个分量,经检测满足 Ai=c;或者扫到 A 的最后一个分量,经检测仍不满足 Ai=c。我们用一个函数 Search 来表达这个算法:F
5、unction Search (c:integer):integer;Var J:integer; BeginJ:=1; 初始化 在还没有到达 A 的最后一个分量且等于 c 的分量还没有找到时,查找下一个分量并且进行检测 While (Aic,则 c 只可能在 A1,A2,.,Am/2-1之中,因而下一步只要在 A1, A2, . ,Am/2-1中继续查找;如果 Am/2=L 时,继续查找While (not Found) and (U=L) doBeginI:=(U+L) div 2;找数组的中间分量If c=AI then Found:=Tureelse if cAI then L:=I+
6、1 else U:=I-1;End;If Found then B_Search:=1else B_Search:=0;End;容易理解,在最坏的情况下最多只要测 A 中的 k+1(k=logm,这里的 log 以 2为底,下同)个分量,就判断 c 是否在 A 中。算法 Search 和 B_Search 解决的是同一个问题,但在最坏的情况下(所给定的 c 不在 A 中) ,两个算法所需要检测的分量个数却大不相同,前者要 m=2 k个,后者只要 k+1 个。可见算法 B_Search 比算法 Search 高效得多。以上例子说明:解同一个问题,算法不同,则计算的工作量也不同,所需的计算时间随之
7、不同,即复杂性不同。上图是运行这两种算法的时间曲线。该图表明,当 m 适当大(mm0 )时,算法 B_Search 比算法 Search 省时,而且当 m 更大时,节省的时间急剧增加。不过,应该指出:用实例的运行时间来度量算法的时间复杂性并不合适,因为这个实例时间与运行该算法的实际计算机性能有关。换句话说,这个实例时间不单纯反映算法的效率而是反映包括运行该算法的计算机在内的综合效率。我们引入算法复杂性的概念是为了比较解决同一个问题的不同算法的效率,而不想去比较运行该算法的计算机的性能。因而,不应该取算法运行的实例时间作为算法复杂性的尺度。我们希望,尽量单纯地反映作为算法精髓的计算方法本身的效率
8、,而且在不实际运行该算法的情况下就能分析出它所需要的时间和空间。1.2 复杂性的计量算法的复杂性是算法运行所需要的计算机资源的量,需要的时间资源的量称作时间复杂性,需要的空间(即存储器)资源的量称作空间复杂性。这个量应该集中反映算法中所采用的方法的效率,而从运行该算法的实际计算机中抽象出来。换句话说,这个量应该是只依赖于算法要解的问题的规模、算法的输入和算法本身的函数。如果分别用 N、I 和 A 来表示算法要解问题的规模、算法的输入和算法本身,用 C 表示算法的复杂性,那么应该有:C =F(N,I,A)其中 F(N,I,A)是 N,I,A 的一个确定的三元函数。如果把时间复杂性和空间复杂性分开
9、,并分别用 T 和 S 来表示,那么应该有:T =T(N,I,A) (2.1)和 S =S(N,I,A) (2.2)通常,我们让 A 隐含在复杂性函数名当中,因而将( 2.1)和(2.2)分别简写为T =T(N,I)和 S =S(N,I)由于时间复杂性和空间复杂性概念类同,计算方法相似,且空间复杂性分析相对地简单些,所以下文将主要地讨论时间复杂性。下面以 T(N,I)为例,将复杂性函数具体化。根据 T(N,I)的概念,它应该是算法在一台抽象的计算机上运行所需的时间。设此抽象的计算机所提供的元运算有 k 种,他们分别记为 O1,O2 ,.,Ok;再设这些元运算每执行一次所需要的时间分别为 t1,
10、t2,.,tk 。对于给定的算法 A,设经过统计,用到元运算 Oi 的次数为 ei,i=1,2,.,k ,很明显,对于每一个 i,1=i=k,ei 是 N 和 I 的函数,即 ei=ei(N,I)。那么有:(2.3)其中 ti,i=1,2,.,k, 是与 N,I 无关的常数。显然,我们不可能对规模 N 的每一种合法的输入 I 都去统计 ei(N,I),i=1,2,k。因此 T(N,I)的表达式还得进一步简化,或者说,我们只能在规模为 N 的某些或某类有代表性的合法输入中统计相应的 ei , i=1,2,k,评价时间复杂性。下面只考虑三种情况的复杂性,即最坏情况、最好情况和平均情况下的时间复杂性
11、,并分别记为 Tmax(N )、Tmin(N) 和 Tavg(N )。在数学上有:(2.4)(2.5)(2.6)其中,DN 是规模为 N 的合法输入的集合;I *是 DN 中一个使 T(N,I *)达到 Tmax(N)的合法输入, 是 DN 中一个使 T(N, )到 Tmin(N)的合法输入;而P(I)是在算法的应用中出现输入 I 的概率。以上三种情况下的时间复杂性各从某一个角度来反映算法的效率,各有各的用处,也各有各的局限性。但实践表明可操作性最好的且最有实际价值的是最坏情况下的时间复杂性。下面我们将把对时间复杂性分析的主要兴趣放在这种情形上。一般来说,最好情况和最坏情况的时间复杂性是很难计
12、量的,原因是对于问题的任意确定的规模 N 达到了 Tmax(N)的合法输入难以确定,而规模 N 的每一个输入的概率也难以预测或确定。我们有时也按平均情况计量时间复杂性,但那时在对 P(I)做了一些人为的假设(比如等概率)之后才进行的。所做的假设是否符合实际总是缺乏根据。因此,在最好情况和平均情况下的时间复杂性分析还仅仅是停留在理论上。2 常见的算法分析设计策略介绍我们一般常见的几种算法分析设计策略主要有:动态规划、贪心算法、回溯法、分支限界法。接下来我主要介绍一下这几种算法。1.1 动态规划动态规划程序设计是对解最优化问题的一种途径、一种方法,而不是一种特殊算法。不象前面所述的那些搜索或数值计
13、算那样,具有一个标准的数学表达式和明确清晰的解题方法。动态规划程序设计往往是针对一种最优化问题,由于各种问题的性质不同,确定最优解的条件也互不相同,因而动态规划的设计方法对不同的问题,有各具特色的解题方法,而不存在一种万能的动态规划算法,可以解决各类最优化问题。因此读者在学习时,除了要对基本概念和方法正确理解外,必须具体问题具体分析处理,以丰富的想象力去建立模型,用创造性的技巧去求解。我们也可以通过对若干有代表性的问题的动态规划算法进行分析、讨论,逐渐学会并掌握这一设计方法。动态规划算法通常用于求解具有某种最优性质的问题。在这类问题中,可能会有许多可行解。每一个解都对应于一个值,我们希望找到具
14、有最优值的解。动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。与分治法不同的是,适合于用动态规划求解的问题,经分解得到子问题往往不是互相独立的。若用分治法来解这类问题,则分解得到的子问题数目太多,有些子问题被重复计算了很多次。如果我们能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,这样就可以避免大量的重复计算,节省时间。我们可以用一个表来记录所有已解的子问题的答案。不管该子问题以后是否被用到,只要它被计算过,就将其结果填入表中。这就是动态规划法的基本思路。具体的动态规划算法多种多样,但它们具有相同的填表格式。
15、在编程中常用解决最长公共子序列问题、矩阵连乘问题、凸多边形最优三角剖分问题、电路布线等问题。1.2 贪心算法所谓贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的仅是在某种意义上的局部最优解。贪心算法不是对所有问题都能得到整体最优解,但对范围相当广泛的许多问题他能产生整体最优解或者是整体最优解的近似解。贪心算法的基本思路:a.建立数学模型来描述问题。b.把求解的问题分成若干个子问题。c.对每一子问题求解,得到子问题的局部最优解。d.把子问题的解局部最优解合成原来解问题的一个解。实现该算法的过程: a.从问题的某一初始解出发
16、;b.while 能朝给定总目标前进一步 doc.求出可行解的一个解元素;d.由所有解元素组合成问题的一个可行解。e.下面是一个可以试用贪心算法解的题目,贪心解的确不错,可惜不是最优解。1.3 回溯法回溯法是一个既带有系统性又带有跳跃性的的搜索算法。它在包含问题的所有解的解空间树中,按照深度优先的策略,从根结点出发搜索解空间树。算法搜索至解空间树的任一结点时,总是先判断该结点是否肯定不包含问题的解。如果肯定不包含,则跳过对以该结点为根的子树的系统搜索,逐层向其祖先结点回溯。否则,进入该子树,继续按深度优先的策略进行搜索。回溯法在用来求问题的所有解时,要回溯到根,且根结点的所有子树都已被搜索遍才
17、结束。而回溯法在用来求问题的任一解时,只要搜索到问题的一个解就可以结束。这种以深度优先的方式系统地搜索问题的解的算法称为回溯法,它适用于解一些组合数较大的问题。回溯法的基本思想:确定了解空间的组织结构后,回溯法就从开始结点(根结点)出发,以深度优先的方式搜索整个解空间。这个开始结点就成为一个活结点,同时也成为当前的扩展结点。在当前的扩展结点处,搜索向纵深方向移至一个新结点。这个新结点就成为一个新的活结点,并成为当前扩展结点。如果在当前的扩展结点处不能再向纵深方向移动,则当前扩展结点就成为死结点。换句话说,这个结点不再是一个活结点。此时,应往回移动(回溯)至最近的一个活结点处,并使这个活结点成为
18、当前的扩展结点。回溯法即以这种工作方式递归地在解空间中搜索,直至找到所要求的解或解空间中已没有活结点时为止。用回溯法解题的一般步骤:(1)针对所给问题,定义问题的解空间; (2)确定易于搜索的解空间结构; (3)以深度优先方式搜索解空间,并在搜索过程中用剪枝函数避免无效搜索。1.4 分支限界法分支定界 (branch and bound) 搜索法是一种在问题的解空间树上搜索问题的解的方法。但与回溯算法不同,分支定界算法采用广度优先或最小耗费优先的方法搜索解空间树,并且,在分支定界算法中,每一个活结点只有一次机会成为扩展结点。分支定界法的思想是:首先确定目标值的上下界,边搜索边减掉搜索树的某些支
19、,提高搜索效率。解题步骤:(1)在问题的边带权的解空间树中进行广度优先搜索 (2)找一个叶结点使其对应路径的权最小(最大)(3)当搜索到达一个扩展结点时,一次性扩展它的所有儿子(4)将满足约束条件且最小耗费函数目标函数限界的儿子,插入活结点表中(5)从活结点表中取下一结点同样扩展(6)直到找到所需的解或活动结点表为空为止3 0-1 背包问题的几种算法背包问题是一类具有广泛的实际应用背景的经典 NP-hard 组合优化问题,在解决大量的复杂组合优化问题时,它常常作为一个子问题出现,从实际观点看,许多问题可以用背包问题来描述,如装箱问题,货仓装载,预算控制,存储分配,项目选择决策等等,都是典型的应用例子。随着网络技术的不断发展,背包公钥密码在电子商务中的公钥设计中也起着重要的作用。然而当问题的