ImageVerifierCode 换一换
格式:PPT , 页数:54 ,大小:201.50KB ,
资源ID:6546749      下载积分:10 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.docduoduo.com/d-6546749.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录   微博登录 

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(循环递归算法设计.ppt)为本站会员(hyngb9260)主动上传,道客多多仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知道客多多(发送邮件至docduoduo@163.com或直接QQ联系客服),我们立即给予删除!

循环递归算法设计.ppt

1、第三章,算法设计,第三章 算法基本工具和优化技巧,利用算法的基本机制循环和递归设计算法 利用算法的基本操作提高算法效率的技巧 利用数组提高算法质量 建立高效的数学模型31 循环与递归 33 算法优化基本技巧 32 算法与数据结构 34 优化算法的数学模型,311 循环设计要点 312 递归设计要点 313 递归与循环的比较,31 循环与递归,311 循环设计要点 1设计中要注意算法的效率 2“自顶向下”的设计方法 3由具体到抽象设计循环结构,循环体的特点是:“以不变应万变”。 所谓“不变”是指循环体内运算的表现形式是不变的,而每次具体的执行内容却是不尽相同的。在循环体内用不变的运算表现形式去描

2、述各种相似的重复运算。,1循环设计中要注意算法的效率,【例1】求1/1!-1/3!+1/5!-1/7!+(-1)n+1/(2n-1)! 分析:此问题中既有累加又有累乘,准确地说累加的对象是累乘的结果。 数学模型1:Sn=Sn-1+(-1)n+1/(2n-1)! 算法设计1:多数初学者会直接利用题目中累加项通式,构造出循环体不变式为: S=S+(-1)n+1/(2n-1)! 需要用二重循环来完成算法,算法1如下:,算法如下:,求(-1)n+1for(j=1;j=i+1;j=j+1) sign = sign *(-1); s=s+ sign/t; print(“Snm=”,s); ,main( )

3、 int i,n,j,sign=1; float s,t=1;input(n); s=1;for(i=2;i=n;i=i+1)t=1; 求阶乘for(j=1;j=2*i-1;j=j+1) t=t*j; sign =1;,算法分析:以上算法是二重循环来完成 ,但算法的效率却太低是O(n2)。其原因是,当前一次循环已求出7!,当这次要想求9!时,没必要再从1去累乘到9,只需要充分利用前一次的结果,用7!*8*9即可得到9!,模型为An=An-1*1/(2*n-2)*(2*n-1)。另外运算sign = sign *(-1);总共也要进行n*(n-1)/2次乘法,这也是没有必要的。下面我们就进行改进

4、。,数学模型2:Sn=Sn-1+(-1)n+1An; An=An-1 *1/(2*n-2)*(2*n-1),main( ) int i,n, sign;float s,t=1;input(n); s=1; sign=1; for(i=2;i=n;i=i+1) 或 for(i=1;i=n-1;i=i+1)sign=-sign; sign=-sign; t= t*(2*i-2)*(2*i-1); t= t*2*i*(2*i+1); s=s+ sign/t; s=s+ sign/t; print(“Sum=”,s); ,算法说明2: 构造循环不变式时,一定要注意循环变量的意义,如当i不是项数序号时(

5、右边的循环中)有关t的累乘式与i是项数序号时就不能相同。 算法分析:按照数学模型2,只需一重循环就能解决问题 算法的时间复杂性为O(n)。,2“自顶向下”的设计方法 自顶向下的方法是从全局走向局部、从概略走向详尽的设计方法。自上而下是系统分解和细化的过程。,【例2】编算法找出1000以内所有完数 例如,28的因子为1、2、4、7,14,而28=1+2+4+7+14。因此28是“完数”。编算法找出1000之内的所有完数,并按下面格式输出其因子:28 its factors are 1,2,4,7,14。,1)这里不是要质因数,所以找到因数后也无需将其从数据中“除掉”。 2)每个因数只记一次,如8

6、的因数为1,2,4而不是1,2,2,2,4。(注:本题限定因数不包括这个数本身),1)顶层算法,for(i=0;in;i=i+1)找第i行上最小的元素t及所在列minj;检验t是否第minj 列的最大值,是则输出这个鞍点;,2)找第i行上最小的元素t及所在列minj,t=ai0; minj=1; for(j=1;jn;j=j+1) if(aijt) t=aij;minj=j;,3)进一步细化判断i是否“完数”算法,s=1 for(j=2;ji;j=j+1)if (i mod j=0) (j是i的因素) s=s+j; if (s=i) i是“完数”;,4)考虑输出格式判断i是否“完数”算法考虑到

7、要按格式输出结果,应该开辟数组存储数据i的所有因子,并记录其因子的个数,因此算法细化如下:,定义数组a,s=1; k=0; for(j=2;ji;j=j+1)if (i mod j=0) (j是i的因素) s=s+j; ak=j;k=k+1; if (s=i) 按格式输出结果,算法如下:,main( ) int i,k,j,s,a20; for(i=1;i=1000;i+)s=1; /*两个赋初值语句s=1,k=0k=0; 一定要位于外部循环的内部*/for(j=2;ji;j+)if (i mod j)=0)s=s+j; ak=j; k+;if(i=s)print(s, “its factor

8、s are :”,1);for(j=0;ik;j+)print(“,”,ak);,【例3】求一个矩阵的鞍点 (即在行上最小而在列上最大的点)。 算法设计:,1)在第一行找最小值,并记录其列号。2)然后验证其是否为所在列的最大值,如果是,则找到问题的解;否则,则继续在下一行找最小值 。,for(i=0;in;i=i+1)找第i行上最小的元素t及所在列minj;检验t是否第minj 列的最大值,是则输出这个鞍点;,t=ai0; minj=1; for(j=1;jn;j=j+1) if(aijt) t=aij;minj=j;,1)顶层算法,2)找第i行上最小的元素t及所在列minj,3)检验t是否第

9、minj 列的最大值,是,则输出这个鞍点;,for(k=0;kt) break; if(kn) continue; print(“the result is a“,i ,“” ,minj, “=”,t);,考虑到会有无解的情况,设置标志量kz,kz=0代表无解,找到一个解后,kz被赋值为1,就不再继续找鞍点的工作。请读者考虑是否有多解的可能性吗?若有,请改写算法,找出矩阵中所有的鞍点。,算法如下:,buck( ) int a1010; int i,j,k,minj,t,n=10,kz=0; /*minj代表当前行中最小值的列下标;设置标志量kz*/ readmtr(a,n); prntmtr(

10、a,n); for(i=0;iaiminj) break;if(kn) continue;print(“the result is a“,i ,“”,minj,“=”,aiminj); kz=1;break; if(kz=0) print(“no solution!”); ,对于不太熟悉的问题,其数学模型或“机械化操作步骤”的不易抽象,下面看一个由具体到抽象设计循环细节的例题。 【例4】编写算法:打印具有下面规律的图形。1 5 2 8 6 3 10 9 7 4,3由具体到抽象设计循环结构,算法设计:容易发现图形中自然数在矩阵中排列的规律,题目中1,2,3,4所在位置我们称为第1层(主对角线),

11、例图中5,6,7所在位置我们称为第二层,。一般地,第一层有n个元素,第二层有n-1个元素 基于以上数据变化规律,以层号作为外层循环,循环变量为i(范围为1n);以层内元素从左上到右下的序号作为内循环,循环变量为j(范围为1n+1-i)。这样循环的执行过程正好与“摆放”自然数的顺序相同。用一个变量k模拟要“摆放”的数据,下面的问题就是怎么样将数据存储到对应的数组元素。,数组元素的存取,只能是按行、列号操作的。所以下面用由具体到抽象设计循环的“归纳法”,找出数组元素的行号、列号与层号i及层内序号j的关系: 1.每层内元素的列号都与其所在层内的序号j是相同的。因为每层的序号是从第一列开始向右下进行。

12、 2.元素的行与其所在的层号及在层内的序号均有关系,具体地: 第一层行号1n,行号与j同; 第二层行号2n,行号比j大1; 第三层行号3n,行号比j大2; 行号起点随层号i增加而增加,层内其它各行的行号又随层内序号j增加而增加,由于编号起始为1,i层第j个数据的列下标为i-1+j。 综合以上分析,i层第 j个数据对应的数组元素是ai-1+jj。,main( ) int i,j,a100100,n,k;input(n);k=1; for(i=1;i=n;i=i+1)for( j=1;j=n+1-i;j=j+1)ai-1+jj=k;k=k+1; for(i=1;i=n;i=i+1)print( “

13、换行符”); for( j=1;j=i;j=j+1) print(aij); ,312 递归设计要点,递归算法是一个模块(函数、过程)除了可调用其它模 块(函数、过程)外,还可以直接或间接地调用自身的算法。,递归是一种比迭代循环更强、更好用的循环结构。,只需要找出递归关系和最小问题的解。,递归方法只需少量的步骤就可描述出解题过程所需要的多次重复计算,大大地减少了算法的代码量。,递归的关键在于找出递归方程式和递归终止条件。,递归定义:使问题向边界条件转化的规则。递归定义必须能使问题越来越简单。 递归边界条件:也就是所描述问题的最简单情况,它本身不再使用递归的定义。,递归算法解题通常有三个步骤:

14、1)分析问题、寻找递归:找出大规模问题与小规模问题的关系,这样通过递归使问题的规模逐渐变小。 2)设置边界、控制递归:找出停止条件,即算法可解的最小规模问题。 3)设计函数、确定参数:和其它算法模块一样设计函数体中的操作及相关参数。,两个经典的递归例题:,【例1】汉诺塔问题 【例2】整数的分划问题,【例1】汉诺塔问题描述:,古代有一个梵塔,塔内有3个基座A、B、C,开始时A基座上有64个盘子,盘子大小不等,大的在下,小的在上。有一个老和尚想把这64个盘子从A座移到B座,但每次只允许移动一个盘子,且在移动过程中在3个基座上的盘子都始终保持大盘在下,小盘在上。在移动过程中可以利用C基座做辅助。请编

15、程打印出移动过程 。,算法设计:,用归纳法解此题,约定盘子自上而下的编号为1,2,3,n。首先看一下2阶汉诺塔问题的解,不难理解以下移动过程:初始状态为 A(1,2) B() C()第一步后 A(2) B() C(1)第二步后 A() B(2) C(1)第三步后 A() B(1,2) C(),如何找出大规模问题与小规模问题的关系,从而实现递归呢?,把n个盘子抽象地看作“两个盘子”,上面“一个”由1n-1号组成,下面“一个”就是n号盘子。移动过程如下:第一步:先把上面“一个”盘子以a基座为起点借助b基座移到c基座。第二步:把下面“一个”盘子从a基座移到b基座。第三步:再把c基座上的n-1盘子借助

16、a基座移到b基座。,把n阶的汉诺塔问题的模块记作hanoi(n,a,b,c)a代表每一次移动的起始基座,b代表每一次移动的终点基座,c代表每一次移动的辅助基座 则汉诺塔问题hanoi(n,a,b,c)等价于以下三步:第一步,hanoi(n-1,a,c,b); 第二步,把下面“一个”盘子从a基座移到b基座; 第三步, hanoi(n-1,c,b,a)。 至此找出了大规模问题与小规模问题的关系。,2,算法如下:,hanoi (int n,char a,char b,char c) / a,b,c 初值为”A”,”B”,”C”/1) if(n0) /*0阶的汉诺塔问题当作停止条件*/2) hanoi

17、(n-1,a,c,b);3) 输出 “ Move dise” ,n.”from pile”,a,” to”b);4) haboi(n-1,c,b,a);5) endif ,【例2】整数的分划问题,对于一个正整数n的分划就是把n写成一系列正整数之和的表达式。例如,对于正整数n=6,它可以分划为: 6 5+1 4+2, 4+1+1 3+3, 3+2+1, 3+1+1+1 2+2, 2+2+1+1, 2+1+1+1+1 1+1+1+1+1+1,根据例子发现“包括第一行以后的数据不超过6,包括第二行的数据不超过5,第六行的数据不超过1”。因此,定义一个函数Q(n,m),表示整数n的“任何被加数都不超过

18、m”的分划的数目,n的所有分划的数目P(n)=Q(n,n)。,模型建立:,一般地Q(n.m)有以下递归关系: 1)Q(n,n)=1+Q(n,n-1) (m=n) Q(n,n-1)表示n的所有其他分划,即最大被加数m=n-1的划分。 2)Q(n,m)=Q(n,m-1)+Q(n-m,m) (mn)Q(n,n-1)表示被加数中不包含m的分划的数目;Q(n-m,m)表示被加数中包含(注意不是小于)m的分划的数目,,递归的停止条件: 1)Q(n,1)=1,表示当最大的被加数是1时,该整数n只有一种分划,即n个1相加; 2)Q(1,m)=1,表示整数n=1只有一个分划,不管最大被加数的上限m是多大。,算法

19、如下:,Divinteger(n, m) if (n n ) Error(“输入参数错误”); /*nm Q(n,m)是无意义的*/ else if (n = 1 or m = 1) return(1); else if ( n m ) return Divinteger(n, n) else if (n = m ) return(1 + Divinteger(n, n-1) else return(Divinteger(n,m-1)+Divinteger(n-m,m); ,313 递归与循环的比较,递归与循环都是解决“重复操作”的机制。,递归使一些复杂的问题处理起来简单明了。,就效率而言,递

20、归算法的实现往往要比迭代算法耗费更多的时间(调用和返回均需要额外的时间)与存贮空间(用来保存不同次调用情况下变量的当前值的栈栈空间),也限制了递归的深度。,每个迭代算法原则上总可以转换成与它等价的递归算法;反之不然 。,递归的层次是可以控制的,而循环嵌套的层次只能是固定的,因此递归是比循环更灵活的重复操作的机制。,下面通过几个具体的例子来说明循环和递归的差异和优劣。 【例1】任给十进制的正整数,请从低位到高位逐位输出各位数字。 循环算法设计:从题目中我们并不能获知正整数的位数,再看题目的要求,算法应该从低位到高位逐位求出各位数字并输出。详细设计如下: 1) 求个位数字的算式为 n mod 10

21、 2) 为了保证循环体为“不变式”,求十位数字的算式仍旧为n mod 10,这就要通过算式n=n10,将n的十位数变成个位数。,循环算法如下: f1(n) while(n=10) print( n mod 10);n=n10; print(n); ,递归算法设计: 1)同上,算法从低位到高位逐位求出各位数字并输出,求个位数字的算式为 n mod 10,下一步则是递归地求n10的个位数字。 2)当n10时,n为一位数停止递归。 递归算法如下: f2(n) if(n10)print(n);else print( n mod 10);f(n10);,【例2】任给十进制的正整数,请从高位到低位逐位输出

22、各位数字。 循环算法设计:本题目中要求“从高位到低位”逐位输出各位数字,但由于我们并不知道正整数的位数,算法还是“从低位到高位”逐位求出各位数字比较方便。这样就不能边计算边输出,而需要用数组保存计算的结果,最后倒着输出。,循环算法如下: f3(n) int j,i=0,a16; while(n=10) ai=n mod 10;i=i+1; n=n10;ai=n; for(j=i;j=0;j=j-1) print(n); ,递归算法设计: 与f2不同,递归算法是先递归地求n10的个位数字,然后再求个位数字n的个位数字并输出。这样输出操作是在回溯时完成的。递归停止条件与f2相同为n10。 递归算法

23、如下: f4(n) if(n10)print(n);else f(n10);print( n mod 10); ,由于递归算法的实现包括递归和回溯两步,当问题需要“后进先出”的操作时,还是用递归算法更有效。如数据结构课程中树各种遍历、图的深度优先等算法都是如此。所以不能仅仅从效率上评价两个控制重复机制的好坏。事实上,无论把递归作为一种算法的策略,还是一种实现机制,对我们设计算法都有很好的帮助。看下面的例子:,【例3】找出n个自然数(1,2,3,n)中r个数的组合。,例如,当n=,r=3时,所有组合为:1 2 31 2 41 2 51 3 41 3 51 4 52 3 42 3 52 4 53

24、4 5total=10 组合的总数,算法设计1:,1)n个数中r的组合,其中每r个数中,数不能相同; 2)任何两组组合的数,所包含的数也不应相同。例如,、与、。为此,约定前一个数应小于后一个数。将上述两条作为约束条件; 3)当r=3时,可用三重循环进行枚举。,算法1如下:,constitute1() int n=5,i,j,k,t;t=0;for(i=1;i=n;i-)for(j=1;j=n;j-) for(k=1;k=n;k-)if(ij)and(ik)and(ij)and(jk)t=t+1; print(i,j,k);print(total=,t);,或者 constitute2()int

25、 n=5,r=3,i,j,k,t;t=0;for(i=1;i=n-r+1; i=i+1)for(j=i+1;j= n-r+2;j=j+1)for(k=j+1;k=n-r+3;k=k+1)t=t+1;print(i,j,k);print(total=,t); ,在循环算法设计中,对n=5的实例,每个组合中的数据从小到大排列或从大到小排列一样可以设计出相应的算法。但用递归思想进行设计时,每个组合中的数据从大到小排列却是必须的;因为递归算法设计是要找出大规模问题与小规模问题之间的关系。,用递归法设计此题:,例如,当n=,r=3时,所有组合为: total=10,分析n=5,r=3时的10组组合数。

26、1)首先固定第一个数,其后就是n=4,r=2的组合数,共6个组合。 2)其次固定第一个数4,其后就是n=3,r=2的组合数,共3个组合。 3)最后固定第一个数3,其后就是n=2,r=2的组合数,共1个组合。至此找到了“个数中个数的组合”与“个数中个数的组合、3个数中个数的组合、2个数中个数的组合”的递归关系。,则递归算法的三个步骤为:,1)一般地:n个数中r个数组合递推到“n-1个数中r-1个数有组合,n-2个数中r-1个数有组合,,r-1个数中r-1个数有组合”,共n-r+1 次递归。 2)递归的边界条件是的r=1。 3)函数的主要操作就是输出,每当递归到r=1时就有一组新的组合产生,输出它

27、们和一个换行符。注意n=5,r=3的例子中的递归规律,先固定5,然后要进行多次递归也就是说,数字5要多次输出,所以要用数组存储以备每一次递归到r=1时输出。同样每次向下递归都要用到数组,所以将数组设置为全局变量 。,递归算法如下:,int a100; comb(int m,int k) int i,j; for (i=m;i=k;i-) ak=i; if (k1) comb(i-1,k-1); else for (j=a0;j0;j-) print(aj); print(“换行符”); ,constitute3( ) int n,r;print(“n,r=”);input(n,r);if (rn) print(“Input n,r error!”);elsea0=r;comb(n,r); /调用递归过程/ ,分析:算法2递归的深度是r,每个算法要递归 m-k+1次,所以的时间复杂性是O(r*n)。,递归是一种强有力的算法设计方法。递归是一种比循环更强、更好用的实现“重复操作”的机制。因为递归不需要编程者(算法设计者)自己构造“循环不变式”,而只需要找出递归关系和最小问题的解。递归在很多算法策略中得以运用,如:分治策略、动态规划、图的搜索等算法策略。综合以上讨论,可以得出以下结论:,

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报