递归讲解
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
复习
输入a,b,c,计算m 。已知m=)
,,max(),,max(),,max(c b b a c b b a c b a +⨯+ 请把求三个数的最大数max(x,y,z)定义成函数和过程两种方法作此题。
递 归
为了描述问题的某一状态,必须用到它的上一状态,而描述上一状态,又必须用到它的上一状态……这种用自已来定义自己的方法,称为递归定义。例如:定义函数f(n)为:
/n*f(n -1) (n>0)
f(n)= |
\ 1(n=0)
则当n>0时,须用f(n-1)来定义f(n),用f(n-1-1)来定义f(n-1)……当n=0时,f(n)=1。
由上例我们可看出,递归定义有两个要素:
(1) 递归边界条件。也就是所描述问题的最简单情况,它本身不再使用递归的定义。
如上例,当n=0时,f(n)=1,不使用f(n-1)来定义。
(2) 递归定义:使问题向边界条件转化的规则。递归定义必须能使问题越来越简单。
如上例:f(n)由f(n-1)定义,越来越靠近f(0),也即边界条件。最简单的情况是f(0)=1。
递归算法的效率往往很低, 费时和费内存空间. 但是递归也有其长处, 它能使一个蕴含递归关系且结构复杂的程序简介精炼, 增加可读性. 特别是在难于找到从边界到解的全过程的情况下, 如果把问题推进一步使其结果仍维持原问题的关系, 则采用递归算法编程比较合适.
递归按其调用方式分为: 1. 直接递归, 递归过程P 直接自己调用自己; 2. 间接递归, 即P 包含另一过程 D, 而D 又调用P.
递归算法适用的一般场合为:
1. 数据的定义形式按递归定义.
如裴波那契数列的定义: f(n)=f(n-1)+f(n-2); f(0)=1; f(1)=2.
对应的递归程序为:
Function fib(n : integer) : integer;
Begin
if n = 0 then fib := 1 { 递归边界 }
else if n = 1 then fib := 2
else fib := fib(n-2) + fib(n-1) { 递归 }
End;
这类递归问题可转化为递推算法, 递归边界作为递推的边界条件.
2. 数据之间的关系(即数据结构)按递归定义. 如树的遍历, 图的搜索等.
3. 问题解法按递归算法实现. 例如回溯法等.
从问题的某一种可能出发, 搜索从这种情况出发所能达到的所有可能, 当这一条路走到" 尽头 "的时候, 再倒回出发点, 从另一个可能出发, 继续搜索. 这种不断" 回溯 "寻找解的方法, 称作" 回溯法 ". 例1、给定N (N>=1),用递归的方法计算1+2+3+4+…+(n-1)+n 。
分析与解答 本题是累加问题可以用递归方法求解。本题中,当前和=前一次和+当前项,而前一次和的计算方法与其相同,只是数据不同,即可利用s(n)=s(n-1)+n 来求解,另外递归调用的次数是有限次,且退出的条件是当n=1时s=1,这恰好符合递归算法的使用条件。
程序代码如下:
program p_1(input,output);
var s,t:integer;
function fac(n:integer):integer;
begin
if n=1 then fac:=1 else fac:=fac(n-1)+n;
end;
begin
read(t); s:=fac(t); writeln(‘s=’,s);
end.
0 fac(6)
1 6+fac(5)=21
2 返回
3
4
5
6 1
例2、阶乘n!=1*2*3…(n-1)*n ,可以改写成n!=(n-1)!*n ,这是阶乘用阶乘定义,但是(n-1)!是n!的简单情况。要求n!必须用同样的方法先求简单情况(n-1)!,要求(n-1)!必须用同样的方法先求简单情况(n-2)!,…最终递归到0!而0!=1。因此n!就是一个递归的描述。阶乘的递归定义:
n!=⎩⎨⎧=>-010)!
1(*n n n n
program facn(input,output);
var n:integer;y:real;
function fac(n:integer):real;
begin
if n=0 then fac:=1
else fac:=n*fac(n-1)
end;
begin
read(n); y:=fac(n); writeln(n,’!=’,y);
end.
分析程序是如何执行的?
例3 求m 与n 的最大公约数
讨论:从数学上可以知道求m 与n 的最大公约数等价于求n 与(m mod n )的最大公约数。这时可以把n 当作新的m ,(m mod n )当作新的n ,问题又变成了求新的m 与n 的最大公约数。它又等价于求新的n 与(m mod n )的最大公约数……如此继续,直到新的n=0时,其最大公约数就是新的m 。
例如求24与16的最大公约数,等价于求16与(24 mod 16)的最大公约数,即求16与8的最大公约数。它又等价于求8与(16 mod 8)的最大公约数,即求8与0的最大公约数。此时n=0,最大公约数就是8。此过程可简单地列表为
m n
24 16
16
8 8 0
其一般公式是gcd (m,n)=⎩⎨⎧>=0
)mod ,gcd(0n n m n n m
其中gcd(m,n)代表求m 与n 的最大公约数。按照此公式可以编出如下递归函数的程序。
Program gmn(input,output);
Var m,n,g:integer;
Function gcd(m,n:integer):integer;
Begin
If n=0 then gcd:=m
Else gcd:=gcd(n, m mod n)
End;
Begin
Read(m,n);g:=gcd(m,n);writeln(‘m=’,m,’n=’,n,’gcd=’,g);
End.
例4、写出下面程序运行的结果
procedure p;
begin
write(1);
end;
procedure t(j:integer);
var i:integer;
begin
for i:=1 to 2 do
begin if j=3 then p else t(j+1); end;
end;
begin
t(1); writeln;
end.
例5、相传在古印度的布拉玛婆罗门圣庙的僧侣在进行一种被称为汉诺塔的游戏,其装置是一块铜板,上面有三根杆(编号A 、B 、C ),A 杆上自下而上、由大到小按顺序串上64个金盘。游戏的目标是把 A 杆上的金盘全部移到C 杆上,并仍按原有顺序叠好。条件是每次只能移动一个盘,并且在每次移动都不允许大盘移到小盘之上。现要求
利用递归调用技术给出N 个盘从A 杆移到C 杆的移动过程。
分析:这个移动过程很复杂与烦琐,但规律性却很强。使用递归调用技术来解决这个移动过程,先得找到一个递归调用模型。想要得到汉诺塔问题的简单解法,着眼点应该是移动A 杆最底部的大盘,而不是其顶部的小盘。不考虑64个盘而考虑N 个盘的一般情况。要想将A 杆上的N 个盘移至C 杆,我们可以这