资源描述
递归程序程序设计循环及递归n循环程序n用于描述需要重复进行计算n高级语言里,也常见用递归来实现重复的计算。n递归,n函数或过程调用自身nC语言允许递归,可以在函数内调用自身,常常使程序更简单清晰。1.阶乘和乘幂n例:定义计算整数阶乘的函数n12(n-1)nn本例中,乘的次数依赖于n,计算所需的次数定义时无法确定。n这是一种典型循环情况n计算“次数”依赖某些参数的值。程序1(n),i;(=1,i=1;i=n;)*=i;阶乘函数的精确定义n是一种递归定义的形式n要解决规模为n的问题,要先解决规模为1的子问题,依此类推。n如果高级语言允许递归定义函数,就可以直接翻译为程序。nC允许递归定义n在函数定义内直接或间接地调用被定义函数本身。写成递归函数(n)n0?1:n*(1);(n)(n=1)1;n*(n-1);(1)(1=1)1;1*(1-1);(2)(2=1)1;2*(2-1);(3)(3=1)1;3*(3-1);()(“”,(3);蓝线:函数调用线路黄线:函数内部执行路线红线:函数执行结束返回主调函数的路线(n)(n1)n1,1,2,3,5,8,13,21,34,65,99,用递归程序实现(n)n2?1:(n-1)+(n-2);问题分析:这个程序好不好?问题分析:这个程序好不好?一方面,很好!程序及数学定义的关系很清晰,正确性容易确认,定义易读易理解一方面,很好!程序及数学定义的关系很清晰,正确性容易确认,定义易读易理解例(5)调用过程(5)(4)(3)(3)(2)(2)(1)(1)(0)(1)(0)(2)(1)(1)(0)存在什么问题?问题n存在大量重复计算,参数越大重复计算越多。n有关系吗?n随着参数增大,计算中重复增长迅速,最快的微机上一分钟大约可以算出(45)n参数加1,多用近一倍时间(指数增长)。最快的微机一小时算不出(55),算(100)要数万年n计算需要时间,复杂计算需要很长时间。这是计算机的本质特征和弱点。说明它不是万能,有些事情“不能”做。计算复杂度n人们发现了许多实际问题,理论上说可用计算机解决(可写出计算它的程序),但对规模大的情况(“大的参数n”),人类永远等不到计算完成。n这时能说问题解决了吗?n计算中有一大类问题被称为的“难解问题”,其中有许多很实际的问题,如规划、调度、优化等。n解决这些问题,需要理论和实际技术的研究。n另外,对于许多问题的实用的有效算法,有极大的理论价值和实际价值。n计算复杂性,难解问题,“P=?”问题。阅读材料:问题nA()a.nA(a).a,a,aP().P,().n,P,PL.1979.P.nA.a.A.为计算过程计时n统计程序或程序片段的计算时间有助于理解程序性质。许多语言或系统都提供了内部计时功能。n有关函数在,统计程序时间时程序头部应写nn在程序里计时,通常写表达式n()/n得到从程序开始到表达式求值时所经历的秒数。确定计算确定计算(45)(45)所需要的时间的程序所需要的时间的程序 (n)(n)n=1?1:(1)(2);n=1?1:(1)(2);()()x;x;x=()/;x=()/;(45);(45);x=()/-x;x=()/-x;(45):.n,x);(45):.n,x);0;0;数的迭代计算n数的递推计算,易见n1)f1和f2是1n2)知道2和1连续两个数,就可算出下一个n递推计算方式n逐个往后推,可用循环实现递推方案 1(n)f1=1,f2=1,f3,i;(n=1)1;(f3=f2+f1,i=2;i n;)f1=f2;f2=f3;f3=f1+f2;f3;做一次递推12程序分析(f3=f2+f1,i=2;in;)f1=f2;f2=f3;f3=f1+f2;循环结束时i等于n,这时c的值是。要得到此结论,可设法证明:每次判断i的值时f3正是。归纳证明n第一次判断时i的值是2,f3的值2,正是(且f1的值是1,f2的值是2)n若某次判断时i值是k(小于n),循环体中的语句使f1变成1,f2变成,f3变成1。ni值增1使我们又有f1为2,f2变成1,f3变成n根据归纳法,每次判断i的值时f3正是。如何保证循环的正确执行n循环实现重复性计算,循环体可能执行多次。如何保证对各种数据都能正确完成计算?n循环中变量不断变化。写循环要考虑变量间的关系,保证某些关系在循环中不变:循环的不变关系。n写循环时最重要的就是想清循环中应维持变量间的什么关系才能保证循环结束时变量能处在所需状态。写完循环后应仔细检查是否满足要求。n循环不变关系(循环不变量)是理解循环、写好循环的关键。问题n本例中用循环的函数比用递归定义的好吗?n新函数在计算时间上有极大优越性。计算时间由循环次数确定。循环体执行次数大致为n。(100)只需约100次循环,几乎察觉不到所花费时间。n新函数定义较复杂,有复杂的循环。要理解程序意义,确认函数对任何参数都算出值,需要借助“循环不变关系”的概念和细致分析。注意:这个例子并不是说明递归比循环的效率低。完全可以写出计算的同样高效的递归定义的函数最大公约数n求两个整数的最大公约数(,),写函数(,)n解法1n从某个数开始,逐个判断当前数是否能同时整除m和n,在这个过程中记录下能同时整除m和n的最大整数。n需要用一个辅助变量k记录当前需要判断的数。n用一个循环实现k顺序取值n初值设为1n每次判断完后增1n直到k大于m和n中其中的一个为止n记下循环过程中出现的新的m和n的公约数,作为新的最大公约数n用变量d表示当前的最大公约数n初值1(是公约数),遇到新的公约数(一定更大)时记入d程序有了d及其初值,k可以从2开始循环。函数定义(m,n)d=1,k=2;(;k=mk=n;)(m%k0n%k0)d=k;d;参数互素时初值1会留下来,能保证正确计算过程示例mnkk=m k=nm%k 0 n%k 0d2082是是12是是是是23是是否否24是是是是45是是否否46是是否否47是是否否48是是否否4904特殊情况处理n一些特殊情况需要处理n1)m和n都为0需特殊处理。令函数返回值0;n2)若m和n中一个为0,是另一个数。函数的返回值正确。也可直接判断处理;n3)m、n为负时函数返回1,可能不对。n应在循环前加语句n(m0n0)0;n(m0)n;n(n0)m;n(m0)m=;n(nn?n:m);把k设为n的较小者nm%k0n%k0;n)n;/*空循环体*/nk;/*循环结束时k是最大公约数*/过程示例mnkm%k 0 n%k 0d2088是是?7是是?6是是?5是是?4否否4两种方式比较n本方法比前一方法简单一些。n两种方法的共同点是重复测试。n这类方法的缺点是效率较低,参数大时循环次数很多。解法2辗转相除法n求有著名的欧几里德算法(欧氏算法,辗转相除法)。最大公约数的递归定义:求有著名的欧几里德算法(欧氏算法,辗转相除法)。最大公约数的递归定义:例n例1n1(70,30)m=70,n=30m%n10n(30,10)m=30,n=10m%n0n例2n1(65,15)m=65,n=15m%n5n1(15,5)m=15,n=5m%n0递归程序解决n函数定义及数学定义直接对应n(m,n)nnm%n0?n:(n,m%n);nn假设第二个参数非0,且参数都不小于0。n对欧氏算法的研究保证了本函数能结束,对较大的数计算速度也很快,远远优于顺序检查。加入特殊情况处理(m,n)(m 0)m=;(n n,);()(n1)(,);(1,);(,);(1,);定义为函数是为了方便。函数调用:定义为函数是为了方便。函数调用:(6,a,b,c);(6,a,b,c);(3,a,b,c);(2,a,c,b);(a,b);(2,c,b,a);(2,a,c,b)(1,a,b,c);(a,c);(1,b,c,a);(1,a,b,c)(a,b);(1,b,c,a)(b,c);(2,c,b,a)(1,c,a,b);(c,b);(1,a,b,c);(1,c,a,b)(c,a);(1,a,b,c)(a,b);abc常见方法n设法确认程序对最基本的情况能正常工作n解决了基本情况后再考虑更复杂的情况n设法找出出错的规律性,检查出错时数据经过的执行流,逐步缩小可疑范围n在程序中加入输出语句,检查重要变量的值的变化情况n利用的排错功能本章要求n掌握递归的概念n递归函数的编写方法n理解递归函数的效率谢谢大家!结结 语语
展开阅读全文