递推算法.docx
- 文档编号:28083020
- 上传时间:2023-07-08
- 格式:DOCX
- 页数:16
- 大小:171.14KB
递推算法.docx
《递推算法.docx》由会员分享,可在线阅读,更多相关《递推算法.docx(16页珍藏版)》请在冰豆网上搜索。
递推算法
递推算法典型例题
一、教学目标
1、由浅入深,了解递推算法
2、掌握递推算法的经典例题
二、重点难点分析
1、重点:
递推关系的建立
2、难点:
如何将所求问题转化为数学模型
三、教具或课件
微机
四、主要教学过程
(一)引入新课
客观世界中的各个事物之间或者一个事物的内部各元素之间,往往存在(隐藏)着很多本质上的关联。
我们设计程序前.应该要通过细心的观察、丰富的联想、不断的尝试推理.尽可能先归纳总结出其内在规律,然后再把这种规律性的东西抽象成数学模型,最后再去编程实现。
递推关系和递归关系都是一种简洁高效的常见数学模型,我们今天先来深入研究一下递推算法如何实现。
(二)教学过程设计
递推法是一种重要的数学方法,在数学的各个领域中都有广泛的运用,也是计算机用于数值计算的一个重要算法。
这种算法特点是:
一个问题的求解需一系列的计算,在已知条件和所求问题之间总存在着某种相互联系的关系,在计算时,如果可以找到前后过程之间的数量关系(即递推式),那么,这样的问题可以采用递推法来解决。
从已知条件出发,逐步推出要解决的问题,叫顺推;从问题出发逐步推到已知条件,此种方法叫逆推。
无论顺推还是逆推,其关键是要找到递推式。
这种处理问题的方法能使复杂运算化为若干步重复的简单运算,充分发挥出计算机擅长于重复处理的特点。
递推算法的首要问题是得到相邻的数据项间的关系(即递推关系)。
递推算法避开了通项公式的麻烦,把一个复杂的问题的求解,分解成了连续的若干步简单运算。
一般说来可以将递推算法看成是一种特殊的迭代算法。
(在解题时往往还把递推问题表现为迭代形式,用循环处理。
所谓“迭代”,就是在程序中用同一个变量来存放每一次推算出来的值,每一次循环都执行同一个语句,给同一变量赋以新的值,即用一个新值代替旧值,这种方法称为迭代。
)
1.递推关系的定义和求解递推关系的方法
有一类试题,每相邻两项数之间的变化有一定的规律性,我们可将这种规律归纳成如下简捷的递推关系式:
fn=g(fn-1)或者fn-1=g'(fn)
这样就在数的序列中,建立起后项和前项之间的关系。
然后从初始条件(或最终结果)入手,一步步地按递推关系式递推,直至求出最终结果(或初始值)。
很多程序就是按这样的方法逐步求解的。
如果对一个试题,我们要是能找到后一项数与前一项数的关系并清楚其起始条件(或最终结果),问题就比较容易解决,让计算机一步步计算就是了。
让高速的计算机从事这种重复运算,可真正起到“物尽其用”的效果。
递推分倒推法和顺推法两种形式。
一般分析思路:
If求解初始条件f1
thenbegin{倒推}
由题意(或递推关系)确定最终结果fn;
求出倒推关系式fi-1=g'(fi);
fori←ndownto2dofi-1←g(fi);{从最终结果fn出发进行倒推}
输出倒推结果fl;
end{then}
elsebegin{顺推}
由题意(或递推关系)确定初始值f1(边界条件);
求出顺推关系式fi=g(fi-1):
fori←2tondofi←g(fi-1);{由边界条件f1出发进行顺推}
输出顺推结果fn;
end;{else}
由此可见,递推算法的时间复杂度一般为W(n)。
我们之所以将递推法划入归纳策略,是因为初始条件(或最终结果)除试题已明确给定外,都是通过对问题的整理与化简而确定的,其递推式也是对实际问题的分析与归纳而得到的,因此递推本质上属于归纳。
2.递推关系的建立
递推关系中存在着三大基本问题:
如何建立递推关系,已给出的递推关系有何性质,以及如何求解递推关系。
其中核心问题是如何建立递推关系。
建立递推关系的关键在于寻找第n项与前面(或后面)几项的关系式,以及初始项的值(或最终结果值)。
它不是一种抽象的概念,而是针对某一具体题目或一类题目而言的。
3、问题举例
【例1】有2×n的一个长方形方格,用一个1×2的骨牌铺满方格。
例如n=3时,为2×3方格。
此时用一个1×2的骨牌铺满方格,共有3种铺法:
编写一个程序,试对给出的任意一个n(n>0),输出铺法总数。
【问题分析】
(1)面对上述问题,如果思考方法不恰当,要想获得问题的解答是相当困难的。
可以用递推方法归纳出问题解的一般规律。
(2)当n=1时,只能是一种铺法
如左图,铺法总数表示为X1=1;
(3)当N=2时:
骨牌可以两个并列竖排,也可以并列横排,再无其他方法,如下左图所示,因此,铺法总数表示为X2=2;
(4)当N=3时:
骨牌可以全部竖排,也可以认为在方格中已经有一个竖排骨牌,则需要在方格中排列两个横排骨牌(无重复方法),若已经在方格中排列两个横排骨牌,则必须在方格中排列一个竖排骨牌。
如题图,再无其他排列方法,因此铺法总数表示为x3=3.由此可以看出,当n=3时的排列骨牌的方法数是n=1和n=2排列方法数的和。
(5)推出一般规律:
对一般的n,要求Xn可以这样来考虑,若第一个骨牌是竖排列放置,剩下有n-1个骨牌需要排列,这时排列方法数为Xn-1;若第一个骨牌是横排列,整个方格至少有2个骨牌是横排列(1*2骨牌),因此剩下n-2个骨牌需要排列,这是骨牌排列方法数为Xn-2。
从第一骨牌排列方法考虑,只有这两种可能,所以有:
Xn=Xn-1+Xn-2(N>2)
X1=1
X2=2
以上就是问题求解的递推公式。
任给N都可以从中获得解答。
例如N=5,
X3=X2+X1=3
X4=X3+X2=5
X5=X4+X3=8
下面是输入N,输出X1~Xn的Pascal程序:
programp12_20;
var x,y,z:
longint;
i,n:
integer;
begin
write('Inputn:
');
read(n);
x:
=0;
y:
=1;
fori:
=1tondo
begin
z:
=y+x;
writeln('x[',i:
2,']=',z);
x:
=y;y:
=z;
end;
end.
下面是运行程序输入n=30,输出的结果:
inputn:
30
x[1]=1
x[2]=2
x[3]=3
x[4]=5
x[5]=8
...............................
x[28]=514229
x[29]=832040
x[30]=1346269
问题的结果就是有名的斐波那契(Fibonacci)数列问题,F
(1)=0,F
(2)=1,在n>2时有:
F(n)=F(n-1)+F(n-2)。
【例2】用迭代方法求Y=X1/3的值。
X由键盘输入。
利用下列迭代公式计算:
yn+1=2/3yn+x/(3y2n),初始值y0=x,误差要求ε=10-4。
【问题分析】
(1)迭代法即反复代入法。
在上式中,将Yn代入公式的右端,可以计算出Yn+1,然后将Yn+1作为新的Yn代入右端,以计算出新的Yn+1,如此重复直到|Yn+1-Yn|<ε为止。
初始值Y0=X,意味着么一次代入公式右端的Yn的取值为X。
(2)本题算法特点:
循环,变量迭代,直到前后两次的计算误差小于10-4结束并输出结果。
程序如下:
programp12_21;
conste=0.0001;
varx,y0,y1,y2:
real;
begin
write('Inputx:
');
read(x);writeln;
y1:
=x;y2:
=x;
repeat
y1:
=y2;
y2:
=2/3*y1+x/(3*y1*y1);
untilabs(y2-y1) writeln(x,': 3x=',y2); end. 当X=8则输出结果: 8: 3X=2.000000011E+00 当X=27则输出结果: 27: 3X=3.0000000018E+00 【例3】过河卒(NOIP2002初中组第四题) 【问题描述】棋盘上A点有一个过河卒,需要走到目标B点。 卒行走的规则: 可以向下、或者向右。 同时在棋盘上的任一点有一个对方的马(如C点).该马所在的点和所有跳跃一步可达的点称为对方马的拄制点(如下图中的c点和P1,P2,…,P8)。 卒不能通过对方马的控制点.棋盘用坐标表示,A点(0,0)、B点(n,m)(n,m为不超过20的整数).同样马的位置坐标是需要给出的C(x,y).C≠AC≠B。 现在从键盘输入n,m.,要你计算出卒从A点能够到达B点的路径的条数。 【问题分析】跳马一般是在学习回溯或搜索等算法的时候.很多书上也有类似的题目,一些比赛中也经常出现这一问题的变形(如NOIPl997初中组第三题)。 有些同学一看到这种类型的题目就去盲目搜索,但事实证明: 当n,m=15就会超时。 其实,对本题稍加分析就能发现,要到达棋盘上的一个点,只能从左边过来(我们称之为左点)或是从上面过来(我们称之为上点)。 根据加法原理.到达某一点的路径数目,就等于到达其相邻的上点和左点的路径数目之和.因此我们可以使用逐列(或逐行)递推的方法来求出起点到终点的路径数目。 障碍点(马的控制点)也完全适用,只要将到达该点的路径数目设置为0即可。 假设用F[i,j]表示到达点(i,j)的路径数目,用g[i,j]表示点(i,j)是否是对方马的控制点g[i,j]=0表示不是对方马的控制点,g[i,j]=1表示是对方马的控制点。 则,我们可以得到如下的递推关系式: F[0,0]=1 F[i,j]=0{g[x,y]=1] F[i,0]=F[i-1,0]{i>0,g[x,y]=0} F[0,j]=F[0,j-1]{j>0,g[x,y]=0} F[i,j]=F[i-1,j]+F[i,j-1]{i>0,j>0.G[x,y]=0} 上述递推关系式的边界为: F[0,0]=l。 考虑到最大情况下;n=20,m=20.路径条数可能会超出长整数范围,所以要使用int64类型计数或高精度运算。 【参考程序】 programp2_1(input,output); const dx: array[1..8]ofShortint=(-2,-1,1,2,2,1,-1,-2); dy: array[1..8]ofShortint=(1,2,2,1,-1,-2,-2,-1); var n,m,x,y,i,j: Byte; g: array[0..20,0..20]ofByte; f: array[0..20,0..20]ofint64; begin Readln(n,m,x,y); Fillchar(g,Sizeof(g),0); g[x,y]: =1; fori: =1to8do if(x+dx[i]>=0)and(x+dx[i]<=n)and (y+dy[i]>=0)and(y+dy[i]<=m)then g[x+dx[i],y+dy[i]]: =1; f[0,0]: =1; fori: =1tondo ifg[i,0]=0thenf[i,0]: =f[i-1,0]; fori: =1tomdo ifg[0,i]=0thenf[0,i]: =f[0,i-1]; fori: =1tondo forj: =1tomdo ifg[i,j]=0thenf[i,j]: =f[i-1,j]+f[i,j-1]; writeln(f[n,m]) end. 解决递推类型问题有三个重点: 一是如何建立正确的递推关系式.二是递推关系有何性质,三是递推关系式如何求解。 递推按照我们推导问题的方向,常分为顺推法和倒推法。 所谓顺推法,就是由问题的边界条件(初始状态,已知的、隐含的、推导出的)出发,通过递推关系式依次从前往后递推出问题的解;所谓倒推法.就是在不知道问题的初始状态(边界条件)下,从问题的最终解(目标状态或某个中间状态.已知的或者经过简单推理得到的)出发.反过来推导出问题的初始状态。 下面分别举例给予说明。 【例4】储油点 【问题描述】一辆卡车欲穿过1000km的沙漠.卡车耗油为1L/km,卡车总栽油能力为500L,显然卡车装一次油是过不了沙漠的。 因此司机必须设法在沿途建立几个贮油点.使卡车能顺利穿越沙漠,试问司机如何建立这些贮油点? 每一贮油点应存多少油,才能使卡车以消耗最少汽油的代价通过沙漠(结果保留小数点后两位)? 编程计算及打印建立的贮油点序号.各贮油点距沙漠边沿出发的距离以及存油量。 No.distance(km)oil(L) 1×××××× 2×××××× 3×××××× 【问题分析】 如果从起点出发,则我们无法确定第1个贮油点的位置及贮油量。 既然顺推不行,我们就换个方向,采用倒推法试试看;先从终点出发倒推最后一个储油点的位置及储油量,然后再把最后一个储油点作为终点,倒推倒数第2个储油点的位置及储油量,……。 设dis[i]表示第i个贮油点至终点(i=0)的距离,oil[i]表示第一个贮油点的贮油量。 从终点向始点倒推,逐一求出每个贮油点的位置及存油量的示意图如下。 从贮油点i向贮油点i+1倒推的策略是,卡车在点i和点i+l间往返若干次。 卡车每次返回i+1处时正好耗尽500L汽油,而每次从i+l处出发时又必须装足500L汽油。 两点之间的距离必须满足在耗油最少的条件下使i点贮足500iL汽油的要求(0≤i≤n-1)。 具体地讲,第一个贮油点i=1应距终点i=0处500km且在该处贮藏500L汽油,这样才能保证卡车能由i=1处到达终点i=0处;这就是说: dis[1]=500;oil[1]=500。 为了在i=1处贮藏500L汽油,卡车至少从i=2处开两趟满载油的车至i=1处。 所以i=2处至少存贮2*500L汽油,即oil[2]=500*2=1000。 另外,再加上从i=1返回至i=2处的一趟空载,合计往返3次。 三次往返路程的耗油量按最省要求只能为500L。 ,即d1,2=500/3km。 dis[2]=dis[1]+d1,2=dis[1]+500/3 为了在i=2处贮存1000L汽油,卡车至少从i=3处开三趟满载油的车至i=2处。 所以i=3处至少存贮3*500L汽油,即oil[3]=500*3=1500。 加上i=2至i=3处的二趟返程空车,合计5次。 路途耗油量亦应500L,即d2,3=500/5km。 dis[3]=dis[2]+d2,3=dis[2]+500/5 依此类推,为了在i=k处贮藏k*500L,汽油,卡车至少从i=k+1处开k+1趟满载车至i=k处,即oil[k+1]=(k+1)*500=oil[k]+500,加上从i=k返回i=k+1的k趟返程空车,合计2k+1次。 这2k+1次总耗油量按最省要求为500L,即dk,k+1=500/(2k+1)km。 dis[k+1]=dis[k]+dk,k+1=dis[k]+500/(2k+1) 最后,i=n至始点的距离为1000-dis[n],oil[n]=500*n。 为了在i=n处取得500*nL汽油,卡车至少从始点开n+1次满载车至i=n,加上从i=n返回始点的n趟返程空车.合计2n+1次,2n+1趟的总耗油量应正好为(1000-dis[n])×(2n+1).即始点藏油为oil[n]+(1000-dis[n])×(2n+1)。 【算法分析2】 由于问题要求卡车以最少的油耗通过沙漠,因此,我们可以得到,车在到达终点的时候应该刚好把油全部用完,也就是说在终点的时候油为0。 这样的话我们就知道了问题的最终状态。 而问题又要我们求的是最终状态之前的所有状态的情况,因此,本题只能从最终状态出发,反推前面的状态,即我们所说的倒推法。 同时又由于油都是要从起点运送过去的,因此储油站应该尽可能的靠近起点,这样才能省油。 那么如何来倒推并找到递推公式呢? 根据我们上面的分析,既然储油站要尽可能的靠近起点且最终车内要无油,因此,离终点最近的那个储油站就应该是在汽车装满油的情况下所能走到的最远距离,即离开终点500公里。 而这个点应该储油的量就是500升。 为了能在这个点储油500升,卡车从上一个点出发肯定不能一次就完成运油任务(因为卡车本身要耗油,所以它从倒数第二个储油站加满油出发到倒数第一个储油站的时候,车上肯定不会有500升油,因此还必须返回在运一次),于是,我们可以知道,卡车在这个区间内至少要跑一个半来回,而为了使储油站尽量的分开,因此,这一个半来回应该也耗掉500升油。 也就是说,倒数第二个储油站应该储油500+500=1000升,距离倒数第一个储油站为500/3公里。 同理,我们可以继续往前推导直到起点。 因此我们可以知道,从终点开始数,第k个储油站应满足: 储油: oil[k]=oil[k-1]+500=500*k 距终点的位置: dis[k]=dis[k-l]+500/(2*k-1) 而起点的位置不一定恰好就在上述某个储油站上,因此,在运算的时候需要运算到第n+1个储油站使得dis[n+1]>=1000,然后计算起点应该的储油量oil[n]+(1000-dis[n])*(2n+1)。 算法框架如下; k: =1;d: =500;{从i=1处开始向始点倒推) dis[1]: =500;oil[l]: =500; repeat k: =k+l;d: =d+500/(2*k-1); dis[k]: =d; oil[k]: =oil[k-1]+500; untild>1000; dis[k];=1000;{置始点至终点的距离值) dl: =1000-dis[k-1];{求贮油点k处至始点的距离} oil[k]: =dl*(2*k十1)+oil[k-1];{求始点藏油量} fori: =0tokdo输出第i个贮油点的距离为1000-dis[k-i],藏油量为oil[k-i]; 【例5】Catalan数 在一个凸n边形中,通过不相交于n边形内部的对角线,把n边形拆分成若干三角形,不同的拆分数目用hn表之,hn即为Catalan数。 例如五边形有如下五种拆分方案(图3),故h5=5。 求对于一个任意的凸n边形相应的hn。 【问题分析】 如果纯粹从h3=1,h4=2,h5=5,….慢慢去归纳,恐怕很难找到问题的递推关系式.更不要说找到问题的本质了。 所以我们换个思维角度.就像解方程中的降幂思想一样.从一般情况出发去“降n”。 因为多边形的任意一条边必定属于某一个三角形,所以我们以某一条边为准.以这条边的两个顶点为起点,再去找任意一个多边形的顶点,来构成一个三角形,用这个三角形把一个凸多边形剖分成两个凸多边形。 因为凸多边形的任意一点都可以引出n-3条对角线,但这两点引出的两组对角线并不是任意的,因为要剖分成若干个三角形.且“对角线要互不相交”。 如上图所示,我们以P1Pn这条边为基准边.再找Pk(2≤k≤n-1)来构成三角形,则原凸n边形被剖解成了△P1PkPn和两个凸多边形,其中一个是由P1,P2,…Pk构成的凸k边形,另一个是由Pk,Pk+1…Pn构成的凸n-k+1边形.根据乘法原理,选择Pk这个顶点的分解方案为hk*hn-k+1种。 而k可以选2到n-1.所以.再根据加法原理,得出总的方案数应该为: n-1 hn=∑hk*hn-k+1 k=2 注意.就这个递推关系式而言,临界值应该设为h2=1.而不是h3=1,否则递推关系就不能得到正确解,这与原问题的实际情况可能不符(即两边形),其实这只是理解上的差异。 用这个递推式求出的数列: h1,h2,h3……,就是著名的catalan数列,它会经常出现在组合计数的问题中。 还要注意的是,这个数列同样很大.当n=22时,hn就超过了长整数范围,所以.如果n很大就需要用高精度运算去计算。 参考程序 programp2_2(input,output); constmax=21; varc: array[2..max]oflongint; n,i,k: integer; total: longint; begin write('inputn=');readln(n); c[2]: =1; fori: =3tondo begin c[i]: =0; fork: =2toi-1doc[i]: =c[i]+c[k]*c[i-k+1]; end; writeln('catalan=',c[n]); end. 【例6】极值问题 【问题描述】已知m,n为整数,且满足下列两个条件: ①m,n∈{1,2,…,k),即1≤m,n≤k ②(n2-mn-m2)2=1 你的任务是: 编程由键盘输入正整数k(1≤k≤109),求一组满足上述两个条件的m、n,并且使m2+n2的值最大。 例如,从键盘输入k=1995,则输出: m=987,n=1597。 【问题分析】这是一道典型的数学题。 如果我们就从条件②出发,用求根公式,加上限制条件去解方程的话.从数学意义上讲是一定可以得出正确的解。 但是我们疏漏了一个重要的条件1≤k≤l09。 可以验证,如果k值超过105。 上述算法决不可能在竞赛限定的时间内出解.更不要说l09了。 所以要提高算法效率,必须对问题进行一些推理和变换,使问题更直观,同时挖掘出问题的本质。 首先,我们对表达式(n2-mn-m2)2=1作如下数学变换: (n2-mn-m2)2 =(m2+nm-n2)2 =[(n+m)2-n(n+m)-n2]2 =[(n')2-m'n'-(m')2]2 其中;n'=m+n,m'=n。 虽然从形式上看,表达式并没有什么变化,但从上述数学变换式可以看出: 如果m和n为一组满足条件①和条件②的解.那么m'和n'也是一组满足条件①和条件②的解,这样我们就可以用“迭代法”求解。 所以,我们令m=1,n=l,发现满足条件①和条件②,即是问题的一组小解。 因此,我们可以将所有满足条件①和条件②的m和n按递增顺序排列出来.即1.1,2,3,5,8,……,大家发现这正是一个Fibonacci数列(这也正是问题的本质)数列中小于k的最大两个相邻数即为试题所要求的一组m和n。 所以算法就很简单了,描述如下: 输入k; m: =l;n
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 算法