递归法

合集下载
相关主题
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
因此求这n!种排布就被转为 求1~n的全排列
提高
n皇后问题
求1~n的全排列,我们可以采用这样的思路, 假设当前生成全排列中的第k个数字,如果k>n, 则说明生成了一个全排列,否则:
1. 从1~n依次尝试,如果此数字前面已经出现,则取下 一个数字,直到找到第一个没有出现的数字;
2. 把这个数字标记未已出现,然后以同样的方法生成全 排列中的下一个数字;
if n>0 then begin hanoi(n-1,a,c,b); writeln('Move ',a,' to ',c); hanoi(n-1,b,a,c); end;
end; begin
a:='A';b:='B';c:='C'; write('N=');readln(n); hanoi(n,a,b,c); end.
递归算法的效率往往很低,费时和费内存空间。但是递归也有其长处, 它能使一个蕴含递归关系且结构复杂的程序简洁精炼,增加可读性。 特别是在难于找到从边界到解的全过程的情况下,如果把问题进一步, 其结果仍维持原问题的关系,则采用递归
典型例题 递归的适用范围
① 数据的定义形式按递归定义。 如裴波那契数列的定义:
function a1(x:integer):integer; begin 调用b1; end;
function b1(x:integer):integer; begin 调用a1; end;
竞赛指导P201 黄皮书108
函数或过程调用它本身,称为递归.直接调用a本身,称为直接 递归,函数或过程a调用函数或过程b,b又调用a ,称间接递归. 我们一般碰到的问题都是直接递归
printf(“\n”);
}
}
}
void main()
{
a[0]=3;
comb(5,3);
}
提高
例题三:n皇后问题
在一个n×n的棋盘上放置n个国际象棋中 的皇后,要求所有的皇后之间都不形成 攻击。请你给出所有可能的排布方案数。
输入:4 输出:2
n
4
5
6
7
8
总数
2
10
4
40
92
提高
n皇后问题
1n 0
fn


2n 1
f n1 f n2 n 2
② 数据之间的关系(即数据结构)按递归定义。如树的遍历,图的搜索等。 ③ 问题解法按递归算法实现。例如回溯法等。
课堂练习
用递归算法求X ^n
分析:因为x^n=x*x^(n-1),可以化成求x^(x-1)的问题………… 即fn(n)=x*f(n-1)
子程序的调用 递归的定义 经典例题 课后练习
第一课
函数和过程能够被主程序调用,也能够相互调用,都应遵循”先定义,后调用” 的原则
一般情况下,后定义的子程序能够直接调用先定义的子程序,如果先定义的 子程序要调用后定义的子程序,用使用保留字forward(一般情况下都不太 使用)
function b1(x:integer); forward;
n!=n*(n-1)! F(n)=F(n-1)+F(n-2)
递归算法通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问 题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算, 大大地减少了程序的代码量。
一般来说,能够用递归解决的问题应该满足 以下三个条件:
对于n皇后问题而言,我们很难找出很合适 的方法来快速的得到解,因此,我们只能 采取最基本的枚举法来求解。
但我们知道,在n×n的棋盘上放置n个棋子 的所有放置方案有 Cnn2种,而这个数字是否 庞大,直接枚举肯定会超时。
提高
n皇后问题
但是考虑到皇后攻击的特性,所有的皇后 不能同行、同列,因此,我们可以人为的 限定所有的皇后不同行、同列,这样的话, 所有的可能性就只有n!种了。
动画演示
典型例题
递归调用示例图
n=0,f(0)=1 n=1,f(1)=1*f(0) n=2,f(2)=2*f(1) n=3,f(3)=3*f(2)
典型例题
递归是从函数(过程)自身出发来达到边界条件,在通过边界条件的递归调 用过程中,系统用堆栈把每次调用的中间结果(局部变量和返回地址) 保存起来,直至求出递归边界值f(0)=a。然后返回调用函数。返回的过 程中,中间结果相继出栈恢复,f(1)=g(1,a)f(2)=g(2,f(1))…… 直至求出f(n)=g(n,f(n-1))。
典型例题
分析:这个移动过程很复杂与烦琐,但规律性却很强。使用递归调用技术来解决这个 移动过程,先得找到一个递归调用模型。想要得到汉诺塔问题的简单解法, 着眼点应该是移动A杆最底部的大盘,而不是其顶部的小盘。不考虑64个盘而考虑 N个盘的一般情况。要想将A杆上的N个盘移至C杆,我们可以这样设想:
1.以C盘为临时杆,从A杆将1至N-1号盘移至B杆。 2.将A杆中剩下的第N号盘移至C杆。 3.以A杆为临时杆,从B杆将1至N-1号盘移至C杆。 我们看到,步骤2只需移动一次就可以完成;步骤1与3的操作则完全相同, 唯一区别仅在于各杆的作用有所不同。这样,原问题被转换为与原问题相同性质的、 规模小一些的新问题(图4)。即: HANOI(N,A,B,C) 可转化为 HANOI(N-1,A,C,B)与 HANOI(N-1,B,A,B)
# definewk.baidu.comMAXN 100
int
a[MAXN];
void comb(int m,int k)
{
int i,j;
for (i=m;i>=k;i--)
{
a[k]=i;
if (k>1)
comb(i-1,k-1);
else
{
for (j=a[0];j>0;j--)
printf(“%4d”,a[j]);
边界条件
n!
1 n(n 1)!
n0 n0
递归方程
典型例题
program Factorial; var N: Integer; T: Longint; function Fac(N: Integer): Longint; begin if N = 0 then Fac := 1 else Fac := N * Fac(N - 1) end; begin Write('N = '); Readln(N); T := Fac(N); Writeln('N! = ',T); end.
第一课时练习
一 完成阶乘,菲波拉契,汉诺塔三个问题的递归程序,彻底弄清楚递归的执行过程
二 已知:
f(x,n)= n (n 1) (n 2) 1 x
计算x=3.1,n=15以及x=8.1,n=10 时的值。
三 顺序读入字符,以’?’结束,然后以和输入相反的次序输出读入的字符,用递归规程 做
1. 需要解决的问题可以化为一个或多个子问题来 求解,而这些子问题的求解方法与原来的问题 完全相同,只是在数量规模上不同;
2. 递归调用的次数必须是有限的;
3. 必须有结束递归的条件(边界条件)来终止递 归。
典型例题
例1 利用递归调用手段编程计算N!。
分析:根椐数学知识,0!=1,正整数N的阶乘为:N*(N-1)*(N-2)*…*2*1, 该阶乘序列可转换为求N*(N-1)!,而(N-1)!可以转换为(N-1)*(N-2)!,……, 直至转换为0!,而0!=1
四 用递归法求两个数的最大公约数(辗转相除)
提高
第二课时
例题一 铺骨牌问题
有1×n的一个长方形,用1×1、1×2、1×3的骨牌铺满方格。 例如当n=3时为1×3的方格,此时用1×1,1×2,1×3的骨牌铺满方格, 共有四种铺法。如图
●●●


输入 n(0<=n<=30);
输出 铺法总数
提高
归纳分析:
输出:5 4 3 542 541 532 531 521 432 431 421 321
total=10 {组合的总数}
提高练习
[解]分析所提示的10组数。首先固定第一位数(如5),其后是在另4个数中 再“组合”2个数。这就将“5个数中3个数的组合”推到了“4个数中2个数的组合 上去了。第一位数可以是n r(如5 3),n个数中r个数组合递推到n-1个数中r-1 个数有组合,这是一个递归的算法。即:
Procedure fn(x, n: integer); {过程fn(x, n)求xn } begin if n=0 then tt:=1 else begin fn(x, n-1); {递归调用过fn(x,n-1)求x n-1} tt:=tt*x end;
end;
典型例题
请写出菲波拉契数列的递归算法
我们只要枚举出所有这n!种排布,找出其中 满足任意两皇后都不共对角线的所有情况 即可。
提高
n皇后问题
那么如何来枚举这n!种排布呢?
既然所有的皇后都不能同行或同列,那么不妨 我们先人为规定第k个皇后在第k行,这样的话 我们只要给出每个皇后所处的列号就可以描述 皇后的位置了。如图放置方式就可以被表示为: 2、4、1、3 即第1个皇后在第2行,第2个 皇后在第4行,……
if n=1 then f:=0; if n=2 then f:=1;
if n>2 then f:=f(n-1)+f(n-2); end; begin write('N=');readln(n); a[1]:=1;a[2]:=1; writeln('F(',n,')=',f(n)); end.
典型例题
典型例题
相传在古印度的布拉玛婆罗门圣庙的僧侣在进行一种被称为汉诺塔的游戏, 其装置是一块铜板,上面有三根杆(编号A、B、C),A杆上自下而上、由大到小按 顺序串上64个金盘(如图3)。游戏的目标是把 A杆上的金盘全部移到C杆上, 并仍原有顺序叠好。条件是每次只能移动一个盘,并且在每次移动都不允许大盘 移到小盘之上。现要求利用递归调用技术给出N个盘从A杆移到C杆的移动过程。
Procedure comb(n,r:integer); var i:integer; begin for i:=n downto r do
begin {固定i的输出位置} comb(i-1,r-1); {原过程递推到i-1个数的r-1个数组合}
end; end; 再考虑打印输出格式。
提高练习
# include <stdio.h>
1n 0
fn


2n 1
f n1 f n2 n 2
典型例题
program dg_2; var
n:byte; a:array[1..100] of longint; function f(n:byte):longint; var i:longint; begin
3. 把这个数字标记未未出现。
其中HANOI中的参数分别表示需移动的盘数、起始盘、临时盘与终止盘, 这种转换直至转入的盘数为0为止,因为这时已无盘可移了。 这就是需要找的递归调用模型
典型例题
program ex11_12; var
a,b,c:char; n:byte; procedure hanoi(n:byte;a,b,c:char); begin
典型例题
如果说例1与例题的无法体现递归算法的独特优点,那么,例3的解法则很能说明问题, 因为一般的算法是很难解决这个问题的,而过程HONOI只用了4个语句就解决这个难题。 不过要说明的是,按照汉诺塔的移动原则,将N个盘从A杆移动到C 杆需要移动盘的次数是 2 的 N 次幂减 1 , 那么 64 个盘移动次数就是 18446744073709511615,近19亿亿次。这是一个天文数字,即使一台功能很强的现 代计算机来解汉诺塔问题,恐怕也需要很长的时间,因此要想得到结果,在运行程序时, 输入的N可不能太大。据说布拉玛婆罗门圣庙的僧侣声称, 汉诺塔游戏结束就标志着世界末日的到来,现在看来确实是有道理的。因为如果每秒 移动一次,64个盘则大约需近5800亿年,而据科学家以能源角度推算,太阳系的寿命 只不过150 亿年而已。
仔细分析最后一个格的铺法,发现无非是用1×1,1× 2,1× 3三种铺 法, 很容易就可以得出
f(n)=f(n-1)+f(n-2)+f(n-3); 其中f(1)=1,f(2)=2,f(3)=4
习题:一人上走楼梯,一次可上1,2,3阶. 输入:台阶数N; 输出:可能的走法.
提高练习
例题二:数的组合
找n个数的r个数的组合。要求: 输入:n,r=5 3
相关文档
最新文档