经典算法设计方法大杂烩.doc
《经典算法设计方法大杂烩.doc》由会员分享,可在线阅读,更多相关《经典算法设计方法大杂烩.doc(22页珍藏版)》请在三一办公上搜索。
1、经典算法设计方法大杂烩经典算法设计方法大杂烩2010-03-31 21:56一、什么是算法算法是一系列解决问题的清晰指令,也就是说,能够对一定规范的输入,在有限时间内获得所要求的输出。算法常常含有重复的步骤和一些比较或逻辑判断。如果一个算法有缺陷,或不适合于某个问题,执行这个算法将不会解决这个问题。不同的算法可能用不同的时间、空间或效率来完成同样的任务。一个算法的优劣可以用空间复杂度与时间复杂度来衡量。算法的时间复杂度是指算法需要消耗的时间资源。一般来说,计算机算法是问题规模n的函数f(n),算法执行的时间的增长率与f(n)的增长率正相关,称作渐进时间复杂度(Asymptotic Time C
2、omplexity)。时间复杂度用O(数量级)来表示,称为阶。常见的时间复杂度有:O(1)常数阶;O(log2n)对数阶;O(n)线性阶;O(n2)平方阶。算法的空间复杂度是指算法需要消耗的空间资源。其计算和表示方法与时间复杂度类似,一般都用复杂度的渐近性来表示。同时间复杂度相比,空间复杂度的分析要简单得多。二、算法设计的方法1.递推法递推法是利用问题本身所具有的一种递推关系求问题解的一种方法。设要求问题规模为N的解,当N=1时,解或为已知,或能非常方便地得到解。能采用递推法构造算法的问题有重要的递推性质,即当得到问题规模为i-1的解后,由问题的递推性质,能从已求得的规模为1,2,i-1的一系
3、列解,构造出问题规模为I的解。这样,程序可从i=0或i=1出发,重复地,由已知至i-1规模的解,通过递推,获得规模为i的解,直至得到规模为N的解。【问题】阶乘计算问题描述:编写程序,对给定的n(n 100),计算并输出k的阶乘k!(k=1,2,n)的全部有效数字。由于要求的整数可能大大超出一般整数的位数,程序用一维数组存储长整数,存储长整数数组的每个元素只存储长整数的一位数字。如有m位成整数N用数组a存储:N=am10m-1+am-110m-2+a2101+a1100并用a0存储长整数N的位数m,即a0=m。按上述约定,数组的每个元素存储k的阶乘k!的一位数字,并从低位到高位依次存于数组的第二
4、个元素、第三个元素。例如,5!=120,在数组中的存储形式为:3 02 1首元素3表示长整数是一个3位数,接着是低位到高位依次是0、2、1,表示成整数120。计算阶乘k!可采用对已求得的阶乘(k-1)!连续累加k-1次后求得。例如,已知4!=24,计算5!,可对原来的24累加4次24后得到120。细节见以下程序。#include stdio.h#include malloc.h#define MAXN 1000 void pnext(int a,int k)int*b,m=a0,i,j,r,carry;b=(int*)malloc(sizeof(int)*(m+1);for(i=1;i=m;i
5、+)bi=ai;for(j=1;j=k;j+)for(carry=0,i=1;i=m;i+)r=(i a0?ai+bi:ai)+carry;ai=r%10;carry=r/10;if(carry)a+m=carry;free(b);a0=m;void write(int*a,int k)int i;printf(%4d!=,k);for(i=a0;i 0;i-)printf(%d,ai);printf(nn);void main()int aMAXN,n,k;printf(Enter the number n:);scanf(%d,&n);a0=1;a1=1;write(a,1);for(k=
6、2;k=n;k+)pnext(a,k);write(a,k);getchar();2.递归递归是设计和描述算法的一种有力的工具,由于它在复杂算法的描述中被经常采用,为此在进一步介绍其他算法设计方法之前先讨论它。能采用递归描述的算法通常有这样的特征:为求解规模为N的问题,设法将它分解成规模较小的问题,然后从这些小问题的解方便地构造出大问题的解,并且这些规模较小的问题也能采用同样的分解和综合方法,分解成规模更小的问题,并从这些更小问题的解构造出规模较大问题的解。特别地,当规模N=1时,能直接得解。【问题】编写计算斐波那契(Fibonacci)数列的第n项函数fib(n)。斐波那契数列为:0、1、1
7、、2、3、,即:fib(0)=0;fib(1)=1;fib(n)=fib(n-1)+fib(n-2)(当n 1时)。写成递归函数有:int fib(int n)if(n=0)return 0;if(n=1)return 1;if(n 1)return fib(n-1)+fib(n-2);递归算法的执行过程分递推和回归两个阶段。在递推阶段,把较复杂的问题(规模为n)的求解推到比原问题简单一些的问题(规模小于n)的求解。例如上例中,求解fib(n),把它推到求解fib(n-1)和fib(n-2)。也就是说,为计算fib(n),必须先计算fib(n-1)和fib(n-2),而计算fib(n-1)和f
8、ib(n-2),又必须先计算fib(n-3)和fib(n-4)。依次类推,直至计算fib(1)和fib(0),分别能立即得到结果1和0。在递推阶段,必须要有终止递归的情况。例如在函数fib中,当n为1和0的情况。在回归阶段,当获得最简单情况的解后,逐级返回,依次得到稍复杂问题的解,例如得到fib(1)和fib(0)后,返回得到fib(2)的结果,在得到了fib(n-1)和fib(n-2)的结果后,返回得到fib(n)的结果。在编写递归函数时要注意,函数中的局部变量和参数知识局限于当前调用层,当递推进入简单问题层时,原来层次上的参数和局部变量便被隐蔽起来。在一系列简单问题层,它们各有自己的参数和
9、局部变量。由于递归引起一系列的函数调用,并且可能会有一系列的重复计算,递归算法的执行效率相对较低。当某个递归算法能较方便地转换成递推算法时,通常按递推算法编写程序。例如上例计算斐波那契数列的第n项的函数fib(n)应采用递推算法,即从斐波那契数列的前两项出发,逐次由前两项计算出下一项,直至计算出要求的第n项。【问题】组合问题问题描述:找出从自然数1、2、n中任取r个数的所有组合。例如n=5,r=3的所有组合为:(1)5、4、3(2)5、4、2(3)5、4、1(4)5、3、2(5)5、3、1(6)5、2、1(7)4、3、2(8)4、3、1(9)4、2、1(10)3、2、1分析所列的10个组合,可
10、以采用这样的递归思想来考虑求组合函数的算法。设函数为void comb(int m,int k)为找出从自然数1、2、m中任取k个数的所有组合。当组合的第一个数字选定时,其后的数字是从余下的m-1个数中取k-1数的组合。这就将求m个数中取k个数的组合问题转化成求m-1个数中取k-1个数的组合问题。设函数引入工作数组a存放求出的组合的数字,约定函数将确定的k个数字组合的第一个数字放在ak中,当一个组合求出后,才将a中的一个组合输出。第一个数可以是m、m-1、k,函数将确定组合的第一个数字放入数组后,有两种可能的选择,因还未去顶组合的其余元素,继续递归去确定;或因已确定了组合的全部元素,输出这个组
11、合。细节见以下程序中的函数comb。【程序】#include stdio.h#define MAXN 100 int aMAXN;void comb(int m,int k)int i,j;for(i=m;i=k;i-)ak=i;if(k 1)comb(i-1,k-1);elsefor(j=a0;j 0;j-)printf(%4d,aj);printf(n);void main()a0=3;comb(5,3);3.回溯法回溯法也称为试探法,该方法首先暂时放弃关于问题规模大小的限制,并将问题的候选解按某种顺序逐一枚举和检验。当发现当前候选解不可能是解时,就选择下一个候选解;倘若当前候选解除了还不
12、满足问题规模要求外,满足所有其他要求时,继续扩大当前候选解的规模,并继续试探。如果当前候选解满足包括问题规模在内的所有要求时,该候选解就是问题的一个解。在回溯法中,放弃当前候选解,寻找下一个候选解的过程称为回溯。扩大当前候选解的规模,以继续试探的过程称为向前试探。【问题】组合问题问题描述:找出从自然数1,2,n中任取r个数的所有组合。采用回溯法找问题的解,将找到的组合以从小到大顺序存于a0,a1,ar-1中,组合的元素满足以下性质:(1)ai+1ai,后一个数字比前一个大;(2)ai-i=n-r+1。按回溯法的思想,找解过程可以叙述如下:首先放弃组合数个数为r的条件,候选组合从只有一个数字1开
13、始。因该候选解满足除问题规模之外的全部条件,扩大其规模,并使其满足上述条件(1),候选组合改为1,2。继续这一过程,得到候选组合1,2,3。该候选解满足包括问题规模在内的全部条件,因而是一个解。在该解的基础上,选下一个候选解,因a2上的3调整为4,以及以后调整为5都满足问题的全部要求,得到解1,2,4和1,2,5。由于对5不能再作调整,就要从a2回溯到a1,这时,a1=2,可以调整为3,并向前试探,得到解1,3,4。重复上述向前试探和向后回溯,直至要从a0再回溯时,说明已经找完问题的全部解。按上述思想写成程序如下:【程序】#define MAXN 100 int aMAXN;void comb
14、(int m,int r)int i,j;i=0;ai=1;doif(ai-i=m-r+1if(i=r-1)for(j=0;j r;j+)printf(%4d,aj);printf(n);ai+;continue;elseif(i=0)return;a-i+;while(1)main()comb(5,3);4.贪婪法贪婪法是一种不追求最优解,只希望得到较为满意解的方法。贪婪法一般可以快速得到满意的解,因为它省去了为找最优解要穷尽所有可能而必须耗费的大量时间。贪婪法常以当前情况为基础作最优选择,而不考虑各种可能的整体情况,所以贪婪法不要回溯。例如平时购物找钱时,为使找回的零钱的硬币数最少,不考虑
15、找零钱的所有各种发表方案,而是从最大面值的币种开始,按递减的顺序考虑各币种,先尽量用大面值的币种,当不足大面值币种的金额时才去考虑下一种较小面值的币种。这就是在使用贪婪法。这种方法在这里总是最优,是因为银行对其发行的硬币种类和硬币面值的巧妙安排。如只有面值分别为1、5和11单位的硬币,而希望找回总额为15单位的硬币。按贪婪算法,应找1个11单位面值的硬币和4个1单位面值的硬币,共找回5个硬币。但最优的解应是3个5单位面值的硬币。【问题】装箱问题问题描述:装箱问题可简述如下:设有编号为0、1、n-1的n种物品,体积分别为v0、v1、vn-1。将这n种物品装到容量都为V的若干箱子里。约定这n种物品
16、的体积均不超过V,即对于0in,有0viV。不同的装箱方案所需要的箱子数目可能不同。装箱问题要求使装尽这n种物品的箱子数要少。若考察将n种物品的集合分划成n个或小于n个物品的所有子集,最优解就可以找到。但所有可能划分的总数太大。对适当大的n,找出所有可能的划分要花费的时间是无法承受的。为此,对装箱问题采用非常简单的近似算法,即贪婪法。该算法依次将物品放到它第一个能放进去的箱子中,该算法虽不能保证找到最优解,但还是能找到非常好的解。不失一般性,设n件物品的体积是按从大到小排好序的,即有v0v1vn-1。如不满足上述要求,只要先对这n件物品按它们的体积从大到小排序,然后按排序结果对物品重新编号即可
17、。装箱算法简单描述如下:输入箱子的容积;输入物品种数n;按体积从大到小顺序,输入各物品的体积;预置已用箱子链为空;预置已用箱子计数器box_count为0;for(i=0;i n;i+)从已用的第一只箱子开始顺序寻找能放入物品i的箱子j;if(已用箱子都不能再放物品i)另用一个箱子,并将物品i放入该箱子;box_count+;else将物品i放入箱子j;上述算法能求出需要的箱子数box_count,并能求出各箱子所装物品。下面的例子说明该算法不一定能找到最优解,设有6种物品,它们的体积分别为:60、45、35、20、20和20单位体积,箱子的容积为100个单位体积。按上述算法计算,需三只箱子,
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 经典 算法 设计 方法 大杂烩
![提示](https://www.31ppt.com/images/bang_tan.gif)
链接地址:https://www.31ppt.com/p-5230419.html