noip讲义5-递归法(小学程度)
合集下载
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
例1
阶乘函数可递归地定义为:
阶乘函数
n0 1 n! n(n 1)! n 0
边界条件
递归方程
边界条件与递归方程是递归函数的二个要素,递归函 数只有具备了这两个要素,才能在有限次计算后得出结 果。
程序:
program p1(input,output); var n:integer;s: longint; function fac(a:integer):longint; begin if a=0 then fac:=1 else fac:=a*fac(a-1); end; begin readln(n); s:=fac(n); writeln(n,‘!=’,s) end.
例8 再探1: 用递归方法求两个数x和y的最大公约 数。(x>0,y>0) 解2 求两个数的最大公约数可以用辗转相减法
f(x,y)=f(y,x-y)
避免了大整数的取模运算。但迭代次数太多。
f(48,28)=f(28,20)=f(20,8)=f(12,8)=f(8,4)=f(4,0)
分析: 1.如果y=k*y1,x=k*x1, 有f(y,x)=k*f(y1,x1)。
递归函数或过程通常带有一些局部变量(如本例中的n),只有当 整个函数体或过程体执行完毕时,这些局部变量才失去意义。每 1 递归调用一次,就必须生成一组‘新’的局部变量,虽然这些新 22 的局部变量与原来的局部变量分别具有相同的名字,但其分配的 333 存储空间不同,其值也完全无关。
写出运行结果。
4444 var n:integer; procedure p(n:integer); 55555 var i:integer; begin if n>0 then begin p(n-1); for i:=1 to n do write(n); writeln; end end; begin n:=5; P(5)->P(4)->P(3)->P(2)->P(1)->P(0) p(n); end.
begin
if m=1 then fic:=0
else if m=2 then fic:=1
else fic:=fic(m-1)+fic(m-2) {递归调用} end;
递归过程
当一个问题可以不断的通过一种有规律 的增加或递减转化为一个新问题,而解决 Байду номын сангаас问题的方法和原问题相同时,可以考虑 过程的递归调用,注意这种“不断的增加 或递减”是有尽头的。
例5、由m个A,n个B组成若干个排列。从某个排列的 位置1开始数,数到任意位置时都能保证A的个数不 少于B的个数,则称该排列为合理排列。 例如:当m=2,n=2时排列有 AABB(合理)ABAB(合理 ) ABBA(不合理) BBAA(不合理) 合理排列数有2种 输入:只有一行两个整数m,n(1≤n≤m≤12) (用空格分隔) 输出:一个整数(所有的合理排列数) 【样例】 输入 输出 32 5
输入一个非负数,输出这个数的倒序数。 Procedrue begin 输出n的最右边的一个数字; if 还有数字 then 将余下的“数字倒序” end
else Procedrue reverse(n:integer); var nr,nl:integer; begin nr:=n mod 10; write(nr); nl:=n div 10; if nl<>0 then reverse(nl) end;
先以三个盘的移动为例,看一下移动过程。
运行结果:Enter the number of disks in Hanoi tower: 3 A→C A→B C→B A→C B→A B→C A→C 分析:首先将A柱上方的n-1个盘子从A柱移到B柱, 此过程中C柱为中间柱;接着将 A柱剩下的一个盘子 移到 C柱;最后再将 B柱上的n-1个盘子移到 C柱,此 过程中 A 柱为中间柱,这就变成了移动 n-1 个盘子的 问题了。定义过程hanoi,实现这一递归算法: 若n=1,则A→C 若n>=2,则 hanoi(n-1,A,C,B) A→C hanoi(n-1,B,A,C)
分析:模拟排队的情况,从第1个人开始,第1 人只能 是A,第2个可以是A也可以是B,再其后的人要保证任 意位置时都能保证A的个数不少于B的个数,递归求有 多少个排列。 Var m,n,t:LongInt;
Procedure pd(i,j:LongInt); Begin (i=m) And If (i=m) And(j=n) (j=nThen t:=t+1{已生成一种排列} Else Begin If i<m Then pd(i+1,j); pd(i+1,j);{增加1个A} (j<n) And If (j<n) And(j<i) (j<i) Then pd(i,j+1); End; {增加1个B} End; Begin t:=0; Read(m,n);pd(1,0);Writeln(t); End.
递归在计算机中的实现
计算机执行递归算法时,是通过栈来实现的。在递归 过程(或函数)开始运行时,系统首先为递归建立一个栈, 在每次执行递归调用语句之前,自动把本算法中所使用的 值参和局部变量的当前值以及调用后的返回地址压栈(称 为“保存现场”,以便需要时“恢复现场”返回到某一状 态),在每次递归调用结束后,又自动把栈顶元素的值分 别赋给相应的值参和局部变量(出栈),以便使它们恢复 到调用前的值,接着无条件转向由返回地址所指定的位置 继续执行算法。
在调用过程或函数之前,系统需完成三件事: ⑴为被调用过程的局部变量分配存储区; ⑵将所有的实在参数、返回地址等信息传递给被调 用过程保存; ⑶将控制转移到被调过程的入口。 从被调用过程返回调用过程之前,系统也应完成三 件工作: ⑴保存被调过程的计算结果; ⑵释放被调过程的数据区; ⑶依照被调过程保存的返回地址将控制转移到调用 过程。
递归过程分析-阶乘函数
阶乘函数可递归地定义为:
n0 1 n! n(n 1)! n 0
边界条件
递归方程
例2: 用递归方法求两个数x和y的最大公约数。 (x>0,y>0)
解1 求两个数的最大公约数可以用辗转相除法,即求m与 n的最大公约数等价于求(x mod y)的值与y的最大公约
解决方法:在递归算法中消除递归调用,使其转 递归的缺点:递归算法的效率往往很低,费时和费内 化为非递归算法。 存空间。Free Pascal理论上可以使用4GB 1、采用一个用户定义的栈来模拟系统的递归调用 (2^32byte)的内存,因此实际上几乎可以使用 工作栈。该方法通用性强,但本质上还是递归, 系统中的所有剩余内存(但有时赛题中有内存限 只不过人工做了本来由编译器做的事情,优化效 果不明显。 制),这是因为Free Pascal使用的是32位的编译 2、用递推来实现递归函数。 器。但大量数据的处理过程将会耗时,有时将出现 3、通过变换能将一些递归转化为尾递归,从而迭 超时。 代求出结果。 后两种方法在时空复杂度上均有较大改善, 但其适用范围有限。
数,此时的y可以当作新的x ,而(x mod y)的值当作新
的y ,所以原问题的求解又变成求新的m与n的最大公约数 问题,继续下去,直至(x mod y)为0,最大公约数就是最 终存放在y中的值。
递归公式:
function gcd(x,y:longint):longint;
var r:integer; begin var r:=m mod n; r: integer ; if r=0 then gcd :=n begin else gcd:=gcd(n, r);{递归调用} r:=x mod y; end;
例7、汉诺塔(tower of Hanoi)问题。有n个大小 不等的中空圆盘,按照从小到大的顺序迭套在立柱 A上,另有两根立柱B和C。现要求把全部圆盘从A柱 (源柱)移到C柱(目标柱),移动过程中可借助 B 柱(中间柱)。移动时有如下的要求: ①一次只移动一个盘; ②不允许把大盘放在小盘上边; ③可使用任意一根立柱暂存圆盘。
var n:integer; procedure hanoi(n:integer;x,y,z:char); begin if n=1 then writeln(x, ‘->’,n, ‘->’,z) else begin hanoi(n-1,x,z,y); writeln(x, ‘->’,n, ‘->’,z); hanoi(n-1,y,x,z) end end; begin {主程序) readln(n); hanoi(n,‘A’,‘B’,‘C’) end.
2.如果x=p*x1,假设p是素数,并且y不能被p整除,
那么f(x,y)=f(p*x1,y)=f(x1,y)。
例9 再探Fibonacci数列 function fic(m:integer):longint; begin if m=1 then fic:=0 else if m=2 then fic:=1 else fic:=fic(m-1)+fic(m-2)
递归
如果函数体或过程体中出现调用其自 身的语句,称为递归。
从下图可知,递归过程的执行总是一个过程体未执行完, 就带着本次执行的结果又进入另一轮过程体的执行,……,如 此反复,不断深入,直到某次过程的执行遇到终止递归的边界 递归过程的执行流程 条件时,则不再深入,而执行本次的过程体余下的部分,然后 又返回到上一次调用的过程体中,执行其余下的部分,……, 如此反复,直到回到起始位置上,才最终结束整个递归过程的 执行,得到相应的执行结果。
if r=0 then gcd :=y else gcd:=gcd(y, r);{递归调用} end;
思考: 0,1,1,2,3,5,8,13,21,34, 55……从第三项起,每一项都是紧挨着的前两项 的和。写出计算斐波那切数列的任意一个数据 项递归函数形式。
function fic(m:integer):longint;
例3:输入一串以‘!’结束的字符,按逆序输出。 program p4(input,output); procedure rever; 程序中, c 是过程 rever var c:char; 的局部变量。每一次递 begin 归调用,都要为局部变 read(c); if c<>'!' then rever; else 量重新分配单元,因此 write(c); 各层的变量c实际上是不 end; begin {主程序} 同的量,它们分别用于 rever; 保存执行本层调用时的 end. 运行: 输入值。 输入 hey! 输出 !yeh。
递归过程分析—数字倒序
例4: 用递归算法把任一给定的十进制正整数 (<=32000)转换成八进制数输出。 分析:利用短除法不断除以8取余数这个重复过程, 将原数据不断缩小,形成递归关系,当数据规模 缩小至0时,递归结束。 procedure tran(n:longint); {递归过程} var k:longint; begin k:=n mod 8; {取除以8以后的余数} n:=n div 8; {取除以8以后的商} if n<>0 then tran(n); {直到商为0,结束递归过程} write(k:1) end;
{递归调用}
end;
优化?
递归要素:完成递归必须考虑的因素有两个。
(1)边界条件。也就是所描述问题的最简单情况,它 本身不再使用递归的定义。如阶乘,当n=0时,f(n)=1, 不使用f(n-1)来定义。 (2)递归关系。使问题向边界条件转化的规则。递 归定义必须能使问题的规模越来越简单。 递归的优点:长处是,它能使一个蕴含递归关系且结构 复杂的程序简介精炼,增加可读性。 特别是在难于找 到从边界到解的全过程的情况下,如果把问题推进一步, 其结果仍维持原问题的关系,则采用递归算法编程比较 合适。
如何设计递归算法
一个问题要用递归方法来解决必须符合两个条件: 1、可以把一个问题转化成一个新的问题,而新 问题的解法和原问题的解法完全相同,只是处理 对象的规模不同。 2、必顺要有一个明确的递归结束条件。