04.递归算法讲解

04.递归算法讲解
04.递归算法讲解

1.用递归法计算n!

【讲解】

递归是算法设计中的一种基本而重要的算法。递归方法即通过函数或过程调用自身将问题转化为本质相同但规模较小的子问题,是分治策略的具体体现。

递归方法具有易于描述、证明简单等优点,在动态规划、贪心算法、回溯法等诸多算法中都有着极为广泛的应用,是许多复杂算法的基础。

递归概述

一个函数在它的函数体内调用它自身称为递归(recursion)调用。是一个过程或函数在其定义或说明中直接或间接调用自身的一种方法,通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。递归的能力在于用有限的语句来定义对象的无限集合。用递归思想写出的程序往往十分简洁易懂。一般来说,递归需要有边界条件、递归前进段和递归返回段。当边界条件不满足时,递归前进;当边界条件满足时,递归返回。

使用递归要注意以下几点:

(1)递归就是在过程或函数里调用自身;

(2)在使用递增归策略时,必须有一个明确的递归结束条件,称为递归出口。

例如有函数r如下:

int r(int a)

{ b=r(a?1);

return b;

}

这个函数是一个递归函数,但是运行该函数将无休止地调用其自身,这显然是不正确的。为了防止递归调用无终止地进行,必须在函数内有终止递归调用的手段。常用的办法是加条件判断,满足某种条件后就不再作递归调用,然后逐层返回。

构造递归方法的关键在于建立递归关系。这里的递归关系可以是递归描述的,也可以是递推描述的。

例4-1 用递归法计算n!。

n!的计算是一个典型的递归问题。使用递归方法来描述程序,十分简单且易于理解。

(1)描述递归关系

递归关系是这样的一种关系。设{U

1,U

2

,U

3

,…,U

n

,…}是一个序列,如果从某一项k开始,

U

n

和它之前的若干项之间存在一种只与n有关的关系,这便称为递归关系。

注意到,当n≥1时,n!=n*(n?1)!(n=0时,0!=1),这就是一种递归关系。对于特定的k!,它只与k与(k?1)!有关。

(2)确定递归边界

在步骤1的递归关系中,对大于k的U

n 的求解将最终归结为对U

k

的求解。这里的U

k

为递归边界(或递归出口)。在本例中,递归边界为k=0,即0!=1。对于任意给定的N!,程序将最终求解到0!。

确定递归边界十分重要,如果没有确定递归边界,将导致程序无限递归而引起死循环。例如以下程序:

#include

int f(int x)

{ return(f(x?1));}

main()

{ printf(f(5));}

它没有规定递归边界,运行时将无限循环,会导致错误。

(3)写出递归函数并译为代码

将步骤1和步骤2中的递归关系与边界统一起来用数学语言来表示,即

n!= n*(n?1)! 当n>1时

n!= 1 当n=1时

再将这种关系翻译为代码,即一个函数:

long f(int n)

{ long g;

if(n<0) printf("n<0,输入错误!");

else if(n==1) g=1;

else g=n*f(n?1);

return(g);

}

(4)完善程序

主要的递归函数已经完成,设计主程序调用递归函数即可。

#include

long f(int n)

{ long g;

if(n<0) printf("n<0,输入错误!");

else if(n==1) g=1;

else g=n*f(n-1);

return(g);

}

void main()

{ int n;

long y;

printf(" 计算n!,请输入n: ");scanf("%d",&n);

y=f(n);

printf(" %d!=%ld \n",n,y);

}

程序中给出的函数f是一个递归函数。主函数调用f后即进入函数f执行,如果

n<0,n==0或n=1时都将结束函数的执行,否则就递归调用f函数自身。由于每次递归调用的实参为n?1,即把n?1的值赋予形参n,最后当n?1的值为1时再作递归调用,形参n的值也为1,将使递归终止,然后可逐层退回。

下面我们再举例说明该过程。设执行本程序时输入为5,即求 5!。在主函数中的调用语句即为y=f(5),进入f函数后,由于n=5,不等于0或1,故应执行f=f(n?1)*n,即

f=f(5?1)*5。该语句对f作递归调用即f(4)。

进行4次递归调用后,f函数形参取得的值变为1,故不再继续递归调用而开始逐层返回主调函数。f(1)的函数返回值为1,f(2)的返回值为1*2=2,f(3)的返回值为2*3=6,f(4) 的返回值为6*4=24,最后返回值f(5)为24*5=120。

综上,得出构造一个递归方法基本步骤,即描述递归关系、确定递归边界、写出递归函数并译为代码,最后将程序完善。

木桩A 木桩B 木桩C

木桩A 木桩B

木桩C 图1

图35-1 汉诺塔游戏示意图

(1)有三根桩子A 、B 、C 。A 桩上有n 个碟子,最大的一个在底下,其余一个比一个小,依次叠上去。

(2)每次移动一块碟子,小的只能叠在大的上面。

(3)把所有碟子从A 桩全部移到C 桩上,如图35-1所示。

试求解n 个圆盘A 桩全部移到C 桩上的移动次数,并求出n 个圆盘的移动过程。

【讲解】

1. 递归关系

当n=1时,只一个盘,移动一次即完成。

当n=2时,由于条件是一次只能移动一个盘,且不允许大盘放在小盘上面,首先把小盘从A 桩移到B 桩;然后把大盘从A 桩移到C 桩;最后把小盘从B 桩移到C 桩,移动3次完成。

设移动n 个盘的汉诺塔需g(n)次完成。分以下三个步骤:

(1) 首先将n 个盘上面的n-1个盘子借助C 桩从A 桩移到B 桩上,需g(n-1)次;

(2) 然后将A 桩上第n 个盘子移到C 桩上(1次);

(3) 最后,将B 桩上的n-1个盘子借助A 桩移到C 桩上,需g(n-1)次。

因而有递归关系:

g(n)=2*g(n-1)+1

初始条件(递归出口):

g(1)=1.

2. 递归求解汉诺塔移动次数的程序设计

// 汉诺塔n 盘移动次数

#include

void main()

{double g(int m); // 确定初始条件

int n;

printf(" 请输入盘片数n: ");

scanf("%d",&n);

if(n<=40)

printf(" %d盘的移动次数为:%.0f\n",n,g(n));

else

printf(" %d盘的移动次数为:%.4e\n",n,g(n));

}

// 求移动次数的递归函数

double g(int m)

{ double s;

if(m==1)

s=1;

else

s=2*p(m-1)+1;

return s;

}

3. 程序运行示例

请输入盘片数n: 40

40盘的移动次数为:1099511627775

请输入盘片数n: 64

64盘的移动次数为:1.8447e+019

这是一个很大的天文数字,若每一秒移动一次,那么需要数亿个世纪才能完成这64个盘的移动。

2.展示移动过程

1. 求解思路

设递归函数hn(n,a,b,c)展示把n个盘从A桩借助B桩移到C桩的过程,函数mv(a,c)输出从a桩到c桩的过程。

完成hn(n,a,b,c),当n=1时,即mv(a,c)。

当n>1时,分以下三步:

(1) 将A桩上面的n-1个盘子借助C桩移到B桩上,即hn(n-1,a,c,b);

(2) 将A桩上第n个盘子移到C桩上,即mv(a,c);

(3) 将B桩上的n-1个盘子借助A桩移到C桩上,即hn(n-1,b,a,c)。

在主程序中,用hn(m,1,2,3)带实参m,1,2,3调用hn(n,a,b,c),这里m为具体移动盘子的个数。同时设置变量k统计移动的次数。

2. 展示汉诺塔移动过程的程序设计

函数mv(x,y)输出从x桩到y桩的过程,这里x,y分别不同情况取“A”或“B”或“C”,主函数调用hn(m,'A','B','C')。

// 展示汉诺塔移动过程的递归设计

#include

int k=0;

void mv(char x,char y) // 输出函数

{ printf(" %c-->%c ",x,y);

k++; // 统计移动次数

if(k%5==0)

printf("\n");

}

void hn(int m,char a,char b,char c) // 递归函数

{ if(m==1) mv(a,c);

else

{ hn(m-1,a,c,b);

mv(a,c);

hn(m-1,b,a,c);

}

}

#include

void main() // 主函数

{ int n;

printf("\n input n: "); scanf("%d",&n);

hn(n,'A','B','C');

printf("\n k=%d \n",k); // 输出移动次数

}

3. 程序运行示例与分析

运行程序,input n: 4

A-->B A-->C B-->C A-->B C-->A

C-->B A-->B A-->C B-->C B-->A

C-->A B-->C A-->B A-->C B-->C

k=15

(1) 上面的运行结果是实现函数hn(4,A,B,C)的过程,可分解为以下三步:

1)A-->B A-->C B-->C A-->B C-->A C-->B A-->B,这前7步是实施hn(3,A,C,B),即完成把上面3个盘从A桩借助C移到B桩。

2)A-->C 这1步是实施着mv(A,C),即把最下面的盘从A桩移到C桩。

3)B-->C B-->A C-->A B-->C A-->B A-->C B-->C,这后7步是实施hn(3,B,A,C),即完成把B桩的3个盘借助A移到C桩。

(2) 其中实现hn(3,A,C,B)的过程,可分解为以下三步:

1) A-->B A-->C B-->C,这前3步是实施hn(2,A,B,C),即完成把上面两个盘从A桩借助B移到C桩。

2) A-->B,这1步是实施mv(A,B),即把第3个盘从A桩移到B桩。

3) C-->A C-->B A-->B,这后3步是实施hn(2,C,A,B),即完成把C桩的两个盘借助A移到B桩。

从以上的结果分析可进一步帮助对递归的理解。

[选做]3. 把前n2个正整数1,2,...,n2 从左上角开始,由外层至中心按顺时针方向螺旋排列所成的数字矩阵,?称n阶顺转方阵;按逆时针方向螺旋排列所成的称n阶逆转方阵。

下面即为一个5阶顺转方阵与5阶逆转方阵。

1 2 3 4 5 1 16 15 14 13

16 17 18 19 6 2 17 24 23 12

15 24 25 20 7 3 18 25 22 11

14 23 22 21 8 4 19 20 21 10

13 12 11 10 9 5 6 7 8 9

5阶顺转方阵5阶逆转方阵

设计程序选择转向分别构造并打印这二种旋转方阵。

【讲解】

设计要点

设计以顺转展开,设置二维数组a[h][v]存放方阵中第h行第v列的整数。

把n阶方阵从外到内分圈,外圈内是一个n-2阶顺转方阵,只是起始数,具有与原问题相同的特性属性。

因此,设置旋转方阵递归函数t(a,b,s,d),其中a是存储方阵元素的数组;b为每个方阵的起始位置;d是为a数组赋值的整数;s是方阵的阶数。

b赋初值0, 因数组下标从0开始,方阵的起始位置为(0,0)。以后每一圈后进入下一内方阵,起始位置b需增1。

d从1开始递增1取值,分别赋值给数组的各元素,至n2 为止。

s从方阵的阶数n开始,以后每一圈后进入下一内方阵,s需减2。

s=0时返回,作为递归的出口;

s=1时,即方阵只有一个数,显然为a[b][b]=d,返回。

s>1时,在函数t(a,b,s,d)中还需调用t(a,b+1,s-2,d)。

递归函数t(a,b,s,d)中对方阵的每一圈的各边的各个元素赋值:

1)一圈的上行从左至右递增

for(j=1;j

{a[h][v]=d;v++;d++;} // 行号h不变,列号v递增,数d递增2)一圈的右列从上至下递增

for(j=1;j

{a[h][v]=d;h++;d++;} // 列号v不变,行号v递增,数d递增3)一圈的下行从右至左递增

for(j=1;j

{a[h][v]=d;v--;d++;} // 行号h不变,列号v递减,数d递增4)一圈的左行从下至上递增

for(j=1;j

{a[h][v]=d;h--;d++;} // 列号v不变,行号h递减,数d递增经以上4步,完成一圈的赋值。

主程序中,只要带实参调用递归函数t(a,0,n,1)即可。

方阵按所选的转向以二维形式输出:

p=1为顺转,输出a[h][v];p=2为逆转,输出a[v][h]

3. 程序实现

// 双转向旋转方阵递归设计

#include

int n,a[20][20]={0};

void main()

{ int h,v,b,p,s,d;

printf(" 请选择方阵阶数n:");scanf("%d",&n);

printf(" 请选择转向,顺转1,逆转2:");scanf("%d",&p);

b=1;s=n;d=1;

void t(int a[20][20],int b,int s,int d); // 递归函数说明t(a,b,s,d);

if(p==1) // 按要求输出旋转方阵 printf(" %d阶顺转方阵: \n",n);

else

printf(" %d阶逆转方阵: \n",n);

for(h=1;h<=n;h++)

{ for(v=1;v<=n;v++)

if(p==1)

printf(" %3d",a[h][v]);

else

printf(" %3d",a[v][h]);

printf("\n");

}

return;

}

void t(int a[20][20],int b,int s,int d) // 定义递归函数{ int j,h=b,v=b;

if(s==0) return; // 递归出口

if(s==1)

{ a[b][b]=d;return;}

for(j=1;j

for(j=1;j

for(j=1;j

for(j=1;j

t(a,b+1,s-2,d); // 调用内一圈递归函数

}

4. 程序运行示例与变通

请选择方阵阶数n:7

请选择方向,顺转1,逆转2:2

7阶逆转方阵:

1 24 23 2

2 21 20 19

2 25 40 39 38 37 18

3 26 41 48 47 36 17

4 27 42 49 46 3

5 16

5 28 43 44 45 34 15

6 29 30 31 32 33 14

7 8 9 10 11 12 13

程序变通:把方阵的输出元素作以下修改

a[h][v] 修改为:n*n+1-a[h][v]

a[v][h] 修改为:n*n+1-a[v][h]

输出从中心开始旋转的数字方阵。例如7阶逆转方阵,输出为:49 26 27 28 29 30 31

48 25 10 11 12 13 32

47 24 9 2 3 14 33

46 23 8 1 4 15 34

45 22 7 6 5 16 35

44 21 20 19 18 17 36

43 42 41 40 39 38 37

高中信息技术 算法与程序设计-递归算法的实现教案 教科版

递归算法的实现 【基本信息】 【课标要求】 (三)算法与问题解决例举 1. 内容标准 递归法与问题解决 (1)了解使用递归法设计算法的基本过程。 (2)能够根据具体问题的要求,使用递归法设计算法、编写递归函数、编写程序、求解问题。 【教材分析】 “算法的程序实现”是《算法与程序设计》选修模块第三单元的内容,本节课是“递归算法的程序实现”,前面学习了用解析法解决问题、穷举法解决问题、在数组中查找数据、对数进行排序以及本节的前一小节知识点“什么是自定义函数”的学习,在学习自定义函数的基础上,学习递归算法的程序实现是自定义函数的具体应用,培养学生“自顶向下”、“逐步求精”的意识起着重要的作用。 『递归算法在算法的学习过程中是一个难点,在PASCAL和C语言等程序语言的学习过程中,往往是将其放在“函数与过程”这一章节中来讲解的。递归算法的实现也是用函数或是过程的自我调用来实现的。从这一点上来讲,作者对教材的分析与把握是准确的,思路是清晰的,目标是明确的。』 【学情分析】 教学对象是高中二年级学生,前面学习了程序设计的各种结构,在学习程序设计各种结构的应用过程中培养了用计算机编程解决现实中问题的能力,特别是在学习循环语句的过程中,应用了大量的“递推”算法。前一节课学习了如何自定义函数,在此基础上学习深入学习和体会自定义函数的应用。以递推算法的逆向思维进行求解问题,在学习过程中体会递归算法的思想过程。多维度的思考问题和解决问题是提高学生的学习兴趣关键。 『递归算法的本质是递推,而递推的实现正是通过循环语句来完成的。作者准确把握了学生前面的学习情况,对递归算法的本质与特征也分析的很透彻,可以说作者对教学任务的分析是很成功的,接来就要看,在成功分析的基础上作者是如何通过设计教学来解决教学难点的了。』 【教学目标】

算法设计题详解

算法设计的特征:有穷性,确定性,输入和输出,可行性 运行算法的时间:硬件的速度。书写程序的语言。问题的规模,编译生成程序的代码质量算法复杂度: 时间复杂度和空间复杂度 1.迭代法 迭代法又称为辗转法,是用计算机解决问题的一种基本方法,为一种不断用变量的旧值递推新值的过程,与直接法相对应,一次性解决问题。迭代法分为精确迭代和近似迭代,“二分法”和“牛顿迭代法”属于近似迭代法。迭代法利用计算机运算速度快、适合做重复性操作的特点,让计算机对一组指令(或一定步骤)进行重复执行,在每次执行这组指令(或这些步骤)时,都从变量的原值推出它的一个新值。 利用迭代算法解决问题,需要做好以下三个方面的工作: 1.确定迭代变量(在可以用迭代算法解决的问题中,至少存在一个直接或间接地 不断由旧值递推出新值的变量,这个变量就是迭代变量。) 2. 建立迭代关系式(所谓迭代关系式,指如何从变量的前一个值推出其下一个值 的公式(或关系)。迭代关系式的建立是解决迭代问题的关键,通常可以顺推 或倒推的方法来完成。) 3.对迭代过程进行控制(在什么时候结束迭代过程?这是编写迭代程序必须考虑 的问题。不能让迭代过程无休止地重复执行下去。迭代过程的控制通常可分为 两种情况:一种是所需的迭代次数是个确定的值,可以计算出来;另一种是所 需的迭代次数无法确定。对于前一种情况,可以构建一个固定次数的循环来实 现对迭代过程的控制;对于后一种情况,需要进一步分析出用来结束迭代过程 的条件。) 2.穷举搜索法 穷举搜索法是对可能是解的众多候选解按某种顺序进行逐一枚举和检验,并从众找出那些符合要求的候选解作为问题的解。 即本方法使用可以理解为暴力循环方法,穷举所有可能性,一般这种方法的时间效率太低,不易使用。但是方法简单,易理解。 3.递推法 递推是计算机数值计算中的一个重要算法,思路是通过数学推导,将复杂的运算化解为若干重复的简单运算,以充分发挥计算机长于重复处理的特点。递推法: 递推法实际上是一种递推关系,就是为了得到问题的解,把它推到比原问题简单的 问题求解,可分为顺推法和倒推法。 i.顺推法,就是先找到递推关系式,然后从初始条件出发,一步步地按 递推关系式递推,直至求出最终结果。 ii.倒推法,就是在不知道初始条件的情况下,经某种递推关系而获知问题的解,再倒过来,推知它的初始条件。 4.递归法(递推加回归) 一个过程或函数在其定义或说明中又间接或间接调用本身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题类似的规模较小的问题来求解,递 归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了 程序的代码量。递归的能力在于用有限的语句来定义对象的无限集合。用递归思想 写出的程序往往十分简洁易懂。 一般来说,递归需要有边界条件、递归前进段和递归前往段。当边界条件不满脚时,递归前进;当边界条件满脚时,递归前往。

递归算法详解

递 归 冯文科 一、递归的基本概念。 一个函数、概念或数学结构,如果在其定义或说明内部直接或间接地出现对其本身的引 用,或者是为了描述问题的某一状态,必须要用至它的上一状态,而描述上一状态,又必须用到它的上一状态……这种用自己来定义自己的方法,称之为递归或递归定义。在程序设计中,函数直接或间接调用自己,就被称为递归调用。 二、递归的最简单应用:通过各项关系及初值求数列的某一项。 在数学中,有这样一种数列,很难求出它的通项公式,但数列中各项间关系却很简单,于是人们想出另一种办法来描述这种数列:通过初值及n a 与前面临近几项之间的关系。 要使用这样的描述方式,至少要提供两个信息:一是最前面几项的数值,一是数列间各项的关系。 比如阶乘数列 1、2、6、24、120、720…… 如果用上面的方式来描述它,应该是: ???>==-1 ,1,11n na n a n n 如果需要写一个函数来求n a 的值,那么可以很容易地写成这样:

这就是递归函数的最简单形式,从中可以明显看出递归函数都有的一个特点:先处理一 些特殊情况——这也是递归函数的第一个出口,再处理递归关系——这形成递归函数的第二个出口。 递归函数的执行过程总是先通过递归关系不断地缩小问题的规模,直到简单到可以作为 特殊情况处理而得出直接的结果,再通过递归关系逐层返回到原来的数据规模,最终得出问题的解。 以上面求阶乘数列的函数)(n f 为例。如在求)3(f 时,由于3不是特殊值,因此需要计 算)2(*3f ,但)2(f 是对它自己的调用,于是再计算)2(f ,2也不是特殊值,需要计算 )1(*2f ,需要知道)1(f 的值,再计算)1(f ,1是特殊值,于是直接得出1)1(=f ,返回上 一步,得2)1(*2)2(==f f ,再返回上一步,得62*3)2(*3)3(===f f ,从而得最终解。 用图解来说明,就是 下面再看一个稍复杂点的例子。 【例1】数列}{n a 的前几项为

算法设计及分析递归算法典型例题

算法递归典型例题 实验一:递归策略运用练习 三、实验项目 1.运用递归策略设计算法实现下述题目的求解过程。 题目列表如下: (1)运动会开了N天,一共发出金牌M枚。第一天发金牌1枚加剩下的七分之一枚,第二天发金牌2枚加剩下的七分之一枚,第3天发金牌3枚加剩下的七分之一枚,以后每天都照此办理。到了第N天刚好还有金牌N枚,到此金牌全部发完。编程求N和M。 (2)国王分财产。某国王临终前给儿子们分财产。他把财产分为若干份,然后给第一个儿子一份,再加上剩余财产的1/10;给第二个儿子两份,再加上剩余财产的1/10;……;给第i 个儿子i份,再加上剩余财产的1/10。每个儿子都窃窃自喜。以为得到了父王的偏爱,孰不知国王是“一碗水端平”的。请用程序回答,老国王共有几个儿子?财产共分成了多少份? 源程序: (3)出售金鱼问题:第一次卖出全部金鱼的一半加二分之一条金鱼;第二次卖出乘余金鱼的三分之一加三分之一条金鱼;第三次卖出剩余金鱼的四分之一加四分之一条金鱼;第四次卖出剩余金鱼的五分之一加五分之一条金鱼;现在还剩下11条金鱼,在出售金鱼时不能把金鱼切开或者有任何破损的。问这鱼缸里原有多少条金鱼? (4)某路公共汽车,总共有八站,从一号站发轩时车上已有n位乘客,到了第二站先下一半乘客,再上来了六位乘客;到了第三站也先下一半乘客,再上来了五位乘客,以后每到一站都先下车上已有的一半乘客,再上来了乘客比前一站少一个……,到了终点站车上还有乘客六人,问发车时车上的乘客有多少? (5)猴子吃桃。有一群猴子摘来了一批桃子,猴王规定每天只准吃一半加一只(即第二天吃剩下的一半加一只,以此类推),第九天正好吃完,问猴子们摘来了多少桃子? (6)小华读书。第一天读了全书的一半加二页,第二天读了剩下的一半加二页,以后天天如此……,第六天读完了最后的三页,问全书有多少页? (7)日本著名数学游戏专家中村义作教授提出这样一个问题:父亲将2520个桔子分给六个儿子。分完后父亲说:“老大将分给你的桔子的1/8给老二;老二拿到后连同原先的桔子分1/7给老三;老三拿到后连同原先的桔子分1/6给老四;老四拿到后连同原先的桔子分1/5给老五;老五拿到后连同原先的桔子分1/4给老六;老六拿到后连同原先的桔子分1/3给老大”。结果大家手中的桔子正好一样多。问六兄弟原来手中各有多少桔子? 四、实验过程 (一)题目一:…… 1.题目分析 由已知可得,运动会最后一天剩余的金牌数gold等于运动会举行的天数由此可倒推每一 天的金牌剩余数,且每天的金牌数应为6的倍数。 2.算法构造 设运动会举行了N天, If(i==N)Gold[i]=N; Else gold[i]=gold[i+1]*7/6+i;

递归算法详解完整版

递归算法详解标准化管理处编码[BBX968T-XBB8968-NNJ668-MM9N]

递归 冯文科一、递归的基本概念。 一个函数、概念或数学结构,如果在其定义或说明内部直接或间接地出现对其本身的引用,或者是为了描述问题的某一状态,必须要用至它的上一状态,而描述上一状态,又必须用到它的上一状态……这种用自己来定义自己的方法,称之为递归或递归定义。在程序设计中,函数直接或间接调用自己,就被称为递归调用。 二、递归的最简单应用:通过各项关系及初值求数列的某一项。 在数学中,有这样一种数列,很难求出它的通项公式,但数列中各项间关系却很简 a与前面临近几项之间的关单,于是人们想出另一种办法来描述这种数列:通过初值及 n 系。 要使用这样的描述方式,至少要提供两个信息:一是最前面几项的数值,一是数列间各项的关系。 比如阶乘数列 1、2、6、24、120、720…… 如果用上面的方式来描述它,应该是: a的值,那么可以很容易地写成这样: 如果需要写一个函数来求 n

这就是递归函数的最简单形式,从中可以明显看出递归函数都有的一个特点:先处理一些特殊情况——这也是递归函数的第一个出口,再处理递归关系——这形成递归函数的第二个出口。 递归函数的执行过程总是先通过递归关系不断地缩小问题的规模,直到简单到可以作为特殊情况处理而得出直接的结果,再通过递归关系逐层返回到原来的数据规模,最终得出问题的解。 以上面求阶乘数列的函数) f为例。如在求)3(f时,由于3不是特殊值,因此需 (n 要计算)2( 3f,但)2(f是对它自己的调用,于是再计算)2(f,2也不是特殊值,需要计 * 算)1( f,返回 )1(= 2f,需要知道)1(f的值,再计算)1(f,1是特殊值,于是直接得出1 * 上一步,得2 3 * )2( )3(= = f,从而得最终 =f )1( 3 2 * * )2(= =f 2 f,再返回上一步,得6 解。 用图解来说明,就是

汉诺塔问题非递归算法详解

Make By Mr.Cai 思路介绍: 首先,可证明,当盘子的个数为n 时,移动的次数应等于2^n - 1。 然后,把三根桩子按一定顺序排成品字型(如:C ..B .A ),再把所有的圆盘按至上而下是从小到大的顺序放在桩子A 上。 接着,根据圆盘的数量确定桩子的排放顺序: 若n 为偶数,按顺时针方向依次摆放C ..B .A ; 若n 为奇数,按顺时针方向依次摆放B ..C .A 。 最后,进行以下步骤即可: (1)首先,按顺时针方向把圆盘1从现在的桩子移动到下一根桩子,即当n 为偶数时,若圆盘1在桩子A ,则把它移动到B ;若圆盘1在桩子B ,则把它移动到C ;若圆盘1在桩子C ,则把它移动到A 。 (2)接着,把另外两根桩子上可以移动的圆盘移动到新的桩子上。 即把非空桩子上的圆盘移动到空桩子上,当两根桩子都非空时,移动较小的圆盘。 (3)重复(1)、(2)操作直至移动次数为2^n - 1。 #include #include using namespace std; #define Cap 64 class Stake //表示每桩子上的情况 { public: Stake(int name,int n) { this->name=name; top=0; s[top]=n+1;/*假设桩子最底部有第n+1个盘子,即s[0]=n+1,这样方便下面进行操作*/ } int Top()//获取栈顶元素 { return s[top];//栈顶 } int Pop()//出栈 { return s[top--];

} void Push(int top)//进栈 { s[++this->top]=top; } void setNext(Stake *p) { next=p; } Stake *getNext()//获取下一个对象的地址 { return next; } int getName()//获取当前桩子的编号 { return name; } private: int s[Cap+1];//表示每根桩子放盘子的最大容量 int top,name; Stake *next; }; void main() { int n; void hanoi(int,int,int,int); cout<<"请输入盘子的数量:"; cin>>n; if(n<1) cout<<"输入的盘子数量错误!!!"<

递归算法详解

递归算法详解 C通过运行时堆栈支持递归函数的实现。递归函数就是直接或间接调用自身的函数。 许多教科书都把计算机阶乘和菲波那契数列用来说明递归,非常不幸我们可爱的著名的老潭老师的《C语言程序设计》一书中就是从阶乘的计算开始的函数递归。导致读过这本经书的同学们,看到阶乘计算第一个想法就是递归。但是在阶乘的计算里,递归并没有提供任何优越之处。在菲波那契数列中,它的效率更是低的非常恐怖。 这里有一个简单的程序,可用于说明递归。程序的目的是把一个整数从二进制形式转换为可打印的字符形式。例如:给出一个值4267,我们需要依次产生字符‘4’,‘2’,‘6’,和‘7’。就如在printf函数中使用了%d格式码,它就会执行类似处理。 我们采用的策略是把这个值反复除以10,并打印各个余数。例如,4267除10的余数是7,但是我们不能直接打印这个余数。我们需要打印的是机器字符集中表示数字‘7’的值。在ASCII码中,字符‘7’的值是55,所以我们需要在余数上加上48来获得正确的字符,但是,使用字符常量而不是整型常量可以提高程序的可移植性。‘0’的ASCII码是48,所以我们用余数加上‘0’,所以有下面的关系: ‘0’+ 0 =‘0’ ‘0’+ 1 =‘1’ ‘0’+ 2 =‘2’ ... 从这些关系中,我们很容易看出在余数上加上‘0’就可以产生对应字符的代码。接着就打印出余数。下一步再取商的值,4267/10等于426。然后用这个值重复上述步骤。 这种处理方法存在的唯一问题是它产生的数字次序正好相反,它们是逆向打印的。所以在我们的程序中使用递归来修正这个问题。 我们这个程序中的函数是递归性质的,因为它包含了一个对自身的调用。乍一看,函数似乎永远不会终止。当函数调用时,它将调用自身,第2次调用还将调用自身,以此类推,似乎永远调用下去。这也是我们在刚接触递归时最想不明白的事情。但是,事实上并不会出现这种情况。 这个程序的递归实现了某种类型的螺旋状while循环。while循环在循环体每次执行时必须取得某种进展,逐步迫近循环终止条件。递归函数也是如此,它在每次递归调用后必须越来越接近某种限制条件。当递归函数符合这个限制条件时,它便不在调用自身。 在程序中,递归函数的限制条件就是变量quotient为零。在每次递归调用之前,我们都把quotient除以10,所以每递归调用一次,它的值就越来越接近零。当它最终变成零时,递归便告终止。 /*接受一个整型值(无符号0,把它转换为字符并打印它,前导零被删除*/

递归算法工作栈的变化详解

通常,一个函数在调用另一个函数之前,要作如下的事情:a)将实在参数,返回地址等信息传递给被调用函数保存; b)为被调用函数的局部变量分配存储区;c)将控制转移到被调函数的入口. 从被调用函数返回调用函数之前,也要做三件事情:a)保存被调函数的计算结果;b)释放被调函数的数据区;c)依照被调函数保存的返回地址将控制转移到调用函数.所有的这些,不论是变量还是地址,本质上来说都是"数据",都是保存在系统所分配的栈中的. ok,到这里已经解决了第一个问题:递归调用时数据都是保存在栈中的,有多少个数据需要保存就要设置多少个栈,而且最重要的一点是:控制所有这些栈的栈顶指针都是相同的,否则无法实现同步. 下面来解决第二个问题:在非递归中,程序如何知道到底要转移到哪个部分继续执行?回到上面说的树的三种遍历方式,抽象出来只有三种操作:访问当前结点,访问左子树,访问右子树.这三种操作的顺序不同,遍历方式也不同.如果我们再抽象一点,对这三种操作再进行一个概括,可以得到:a)访问当前结点:对目前的数据进行一些处理;b)访问左子树:变换当前的数据以进行下一次处理;c)访问右子树:再次变换当前的数据以进行下一次处理(与访问左子树所不同的方式). 下面以先序遍历来说明: void preorder_recursive(Bitree T) /* 先序遍历二叉树的递归算法*/ { if (T) { visit(T); /* 访问当前结点*/ preorder_recursive(T->lchild); /* 访问左子树*/ preorder_recursive(T->rchild); /* 访问右子树*/ } } visit(T)这个操作就是对当前数据进行的处理, preorder_recursive(T->lchild)就是把当前数据变换为它的左子树,访问右子树的操作可以同样理解了. 现在回到我们提出的第二个问题:如何确定转移到哪里继续执行?关键在于一下三个地方:a)确定对当前数据的访问顺序,简单一点说就是确定这个递归程序可以转换为哪种方式遍历的树结构;b)确定这个递归函数转换为递归调用树时的分支是如何划分的,即确定什么是这个递归调用树的"左子树"和"右子树"c)确定这个递归调用树何时返回,即确定什么结点是这个递归调用树的"叶子结点".

04.递归算法讲解

1.用递归法计算n! 【讲解】 递归是算法设计中的一种基本而重要的算法。递归方法即通过函数或过程调用自身将问题转化为本质相同但规模较小的子问题,是分治策略的具体体现。 递归方法具有易于描述、证明简单等优点,在动态规划、贪心算法、回溯法等诸多算法中都有着极为广泛的应用,是许多复杂算法的基础。 递归概述 一个函数在它的函数体内调用它自身称为递归(recursion)调用。是一个过程或函数在其定义或说明中直接或间接调用自身的一种方法,通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。递归的能力在于用有限的语句来定义对象的无限集合。用递归思想写出的程序往往十分简洁易懂。一般来说,递归需要有边界条件、递归前进段和递归返回段。当边界条件不满足时,递归前进;当边界条件满足时,递归返回。 使用递归要注意以下几点: (1)递归就是在过程或函数里调用自身; (2)在使用递增归策略时,必须有一个明确的递归结束条件,称为递归出口。 例如有函数r如下: int r(int a) { b=r(a?1); return b; } 这个函数是一个递归函数,但是运行该函数将无休止地调用其自身,这显然是不正确的。为了防止递归调用无终止地进行,必须在函数内有终止递归调用的手段。常用的办法是加条件判断,满足某种条件后就不再作递归调用,然后逐层返回。 构造递归方法的关键在于建立递归关系。这里的递归关系可以是递归描述的,也可以是递推描述的。 例4-1 用递归法计算n!。 n!的计算是一个典型的递归问题。使用递归方法来描述程序,十分简单且易于理解。 (1)描述递归关系 递归关系是这样的一种关系。设{U 1,U 2 ,U 3 ,…,U n ,…}是一个序列,如果从某一项k开始, U n 和它之前的若干项之间存在一种只与n有关的关系,这便称为递归关系。 注意到,当n≥1时,n!=n*(n?1)!(n=0时,0!=1),这就是一种递归关系。对于特定的k!,它只与k与(k?1)!有关。 (2)确定递归边界 在步骤1的递归关系中,对大于k的U n 的求解将最终归结为对U k 的求解。这里的U k 称 为递归边界(或递归出口)。在本例中,递归边界为k=0,即0!=1。对于任意给定的N!,程序将最终求解到0!。 确定递归边界十分重要,如果没有确定递归边界,将导致程序无限递归而引起死循环。例如以下程序: #include int f(int x) { return(f(x?1));}

实验1:循环与递归算法实验

实验一:循环与递归算法的应用 【实验目的】 1.掌握循环、递归算法的基本思想、技巧和效率分析方法。 2.熟练掌握循环和递归的设计要点,清楚循环和递归的异同。 3.学会利用循环、递归算法解决实际问题。 【实验内容】 1. 问题描述: (1)题目一:打印图形 编程打印如下图所示的N阶方阵。 1 3 6 10 15 2 5 9 14 4 8 13 7 12 11 (2)题目二:计算前n项和 根据参数n,计算1+2+……+n。 要求:用循环和递归分别实现 (3)题目三:回文判断 判断s字符串是否为“回文”的递归程序。 2. 数据输入:个人设定,由键盘输入。 3. 要求: (1)上述题目一、二必做,题目三选做; (2)独立完成实验及实验报告。 【具体实现过程】 题目一: 【算法分析】 通过两个for循环控制数字的输出。 【实现代码】

#include int main() { int i,j,k,n,l,middle,temp; printf("请输入n的大小\n"); scanf("%d",&n); k = 1; temp = 0; middle = 0; for(i=1;i<=n;i++) { middle = i+1; k += temp; printf("%d ",k); l = k; for(j=n;j>0;j--) { if(j==1) printf("\n"); else { l += middle; printf("%d ",l); middle++; } } temp++; n--; } return 0; }

题目二: 【算法分析】 定义一个sum函数求和,把求出的新值赋给sum,最后求得的值即为前n项和。【实现代码】 递归 #include "stdio.h" int fun(int num) { int sum; if( num==1) sum=1; else sum=num+fun(num-1); return sum; } void main() { int n,s; printf("n="); scanf("%d",&n); s=fun(n); printf("s=%d\n",s); }

c++,使用欧几里得算法计算两个数的最大公约数,分别用递推和递归两种算法实现5

实验九 一、实验内容 教材3.9 定义递归函数实现下面的Ackman函数 n+1 m=0 Acm(m,n)= Acm(m-1,1) n=0 Acm(m-1,Acm(m,n-1)) n>0,m>0 教材3.10 用递归法实现勒让德多项式: 1 n=0 Pn= x n=1 ((2n-1)xPn-1(x)-(n-1)Pn-2(x))/n 教程p24 使用欧几里得算法计算两个数的最大公约数,分别用递推和递归两种算法实现 教程p26 编程:将上题以多文件方式组织,在area.h中声明各个area()函数原型,在area.cpp文件中定义函数,然后在Exp9_2中包含area.h,定义main() 函数并执行。 二、实验目的 1、掌握函数的嵌套调用好递归调用 2、掌握递归算法 3、了解内联函数、重载函数、带默认参函数的定义及使用方法 4、掌握程序的多文件组织 5、掌握编译预处理的内容,理解带参数宏定义与函数的区别 三、实验步骤 教材3.9 定义递归函数实现下面的Ackman函数 n+1 m=0 Acm(m,n)= Acm(m-1,1) n=0 Acm(m-1,Acm(m,n-1)) n>0,m>0 教材3.10用递归法实现勒让德多项式: 1 n=0 Pn= x n=1 ((2n-1)xPn-1(x)-(n-1)Pn-2(x))/n 教程p24 使用欧几里得算法计算两个数的最大公约数,分别用递推和递归两种算法实现 教程p26 编程:将上题以多文件方式组织,在area.h中声明各个area()函数原型,在area.cpp文件中定义函数,然后在Exp9_2中包含area.h,定义main() 函数并执行。 四、实验数据及处理结果

递归算法经典案例

案例一: publicclass Fibonacci { //一列数的规则如下: 1、1、2、3、5、8、13、21、34 ,求第30位数是多少?使用递归实现 publicstaticvoid main(String[] args){ System.out.println(Fribonacci(9)); } publicstaticint Fribonacci(int n){ if(n<=2) return 1; else return Fribonacci(n-1)+Fribonacci(n-2); } } 案例二: publicclass Hanio1 { /* * 汉诺塔(又称河内塔)问题其实是印度的一个古老的传说。开天辟地的神勃拉玛(和中国的盘古差不多的神吧)在一个庙里留下了三根金刚石的棒, * 第一根上面套着64个圆的金片,最大的一个在底下,其余一个比一个小,依次叠上去,庙里的众僧不倦地把它们一个个地从这根棒搬到另一根棒上, * 规定可利用中间的一根棒作为帮助,但每次只能搬一个,而且大的不能放在小的上面。计算结果非常恐怖(移动圆片的次数)18446744073709551615, * 众僧们即便是耗尽毕生精力也不可能完成金片的移动了。 *要求:输入一个正整数n,表示有n个盘片在第一根柱子上。输出操作序列,格式为“移动 t从 x 到y”。每个操作一行,

*表示把x柱子上的编号为t的盘片挪到柱子y上。柱子编号为A,B,C,你要用最少的操作把所有的盘子从A柱子上转移到C柱子上。 */ publicstaticvoid main(String[] args){ int i=3; char a ='A',b='B',c='C'; hanio(i,a,b,c); } publicstaticvoid hanio(int n,char a,char b,char c){ if(n==1) System.out.println("移动"+n+"号盘子从"+a+"到"+c); else{ hanio(n-1,a,c,b);//把上面n-1个盘子从a借助b搬到c System.out.println("移动"+n+"号盘子从"+a+"到"+c);//紧接着直接把n搬动c hanio(n-1,b,a,c);//再把b上的n-1个盘子借助a搬到c } } } 案例三: publicclass Rabbit { /* 古典问题:3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少? 分析:首先我们要明白题目的意思指的是每个月的兔子总对数;假设将兔子分为小中大三种,兔子从出生后三个月后每个月就会生出一对兔子, 那么我们假定第一个月的兔子为小兔子,第二个月为中兔子,第三个月之后就为大兔子,那么第一个月分别有1、0、0,第二个月分别为0、1、0, 第三个月分别为1、0、1,第四个月分别为,1、1、1,第五个月分别为2、1、2,第六个月分别为3、2、3,第七个月分别为5、3、5…… 兔子总数分别为:1、1、2、3、5、8、13…… 于是得出了一个规律,从第三个月起,后面的兔子总数都等于前面两个月的兔子总

n!非递归算法的设计与实现

数据结构课程设计 设计说明书 n!非递归算法的设计与实现 学生姓名赵娜 学号1021024042 班级信管102班成绩 指导教师曹记东 数学与计算机科学学院 2012 年 3 月 3 日

数据结构课程设计评阅书 注:指导教师成绩60%,答辩成绩40%,总成绩合成后按五级制记入。

课程设计任务书 2011—2012学年第2学期 专业:计算机科学与技术学号:1021024042 姓名:赵娜 课程设计名称:数据结构课程设计 设计题目:n!非递归算法的设计与实现 完成期限:自2012 年 2 月20 日至2012 年 3 月 3 日共 2 周 设计内容: 利用非递归算法实现n!的计算,在设计过程中应注意n值大小与数据类型表数范围之间的关系,并尽可能求出较大n值的阶乘。 要求: 1)阐述设计思想,画出流程图; 2)说明测试方法,写出完整的运行结果; 3)从时间、空间对算法分析; 4)较好的界面设计; 5)编写课程设计报告。 以上要求中第一个阶段的任务完成后,先将设计说明书的草稿交指导老师面审,审查合格后方可进入后续阶段的工作。设计工作结束后,经指导老师验收合格后将设计说明书打印装订,并进行答辩。 指导教师(签字):教研室主任(签字): 批准日期:2012年 2 月20 日

摘要 设计了一个用非递归算法实现n!的软件,该软件具有计算从1到999之间整数的阶乘的运算的功能。本计算器采用VC++作为软件开发环境,采用数组存储运算的结果,用栈输出运算结果,用递推法实现了整数的阶乘运算,界面清晰,易于为用户所接受。 关键词:n!; 非递归;数组;栈

目录 1 课题描述 (1) 2 需求分析 (2) 3 概要设计 (3) 4 详细设计 (4) 5 程序编码 (8) 6 程序调试与测试 (10) 7 结果分析 (12) 8 总结 (13) 参考文献 (14)

C语言经典算法详解

一分而治之算法 分而治之方法与软件设计的模块化方法非常相似。为了解决一个大的问题,可以: 1) 把它分成两个或多个更小的问题; 2) 分别解决每个小问题; 3) 把各小问题的解答组合起来,即可得到原问题的解答。小问题通常与原问题相似,可以递归地使用分而治之策略来解决。下列通过实例加以说明。 例:利用分而治之算法求一个整数数组中的最大值。

练习:[找出伪币] 给你一个装有1 6个硬币的袋子。1 6个硬币中有一个是伪造的,并且那个伪造的硬币比真的硬币要轻一些。你的任务是找出这个伪造的硬币。

二贪心算法 贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的仅是在某种意义上的局部最优解。贪心算法不是对所有问题都能得到整体最优解,但对范围相当广泛的许多问题他能产生整体最优解或者是整体最优解的近似解。 贪心算法(Greedy algorithm)是一种对某些求最优解问题的更简单、更迅速的设计技术。用贪婪法设计算法的特点是一步一步地进行,常以当前情况为基础根据某个优化测度作最优选择,而不考虑各种可能的整体情况,它省去了为找最优解要穷尽所有可能而必须耗费的大量时间,它采用自顶向下,以迭代的方法做出相继的贪心选择,每做一次贪心选择就将所求问题简化为一个规模更小的子问题, 通过每一步贪心选择,可得到问题的一个最优解,虽然每一步上都要保证能获得局部最优解,但由此产生的全局解有时不一定是最优的,所以贪婪法不要回溯。 贪心算法是一种改进了的分级处理方法。其核心是根据题意选取一种量度标准。然后将这多个输入排成这种量度标准所要求的顺序,按这种顺序一次输入一个量。如果这个输入和当前已构成在这种量度意义下的部分最佳解加在一起不能产生一个可行解,则不把此输入加到这部分解中。这种能够得到某种量度意义下最优解的分级处理方法称为贪婪算法。 对于一个给定的问题,往往可能有好几种量度标准。初看起来,这些量度标准似乎都是可取的,但实际上,用其中的大多数量度标准作贪婪

高中信息技术递归算法的实现教案 粤教版

《递归算法与递归程序》(一)教学设计 一、教材分析 “递归算法与递归程序”是广东教育出版社《算法与程序设计》选修1第四单元第五节的内容,前面学习了用解析法解决问题、穷举法解决问题、在数组中查找数据、对数进行排序,且在第二章中学习了自定义过程与函数。在前面学习的基础上,学习递归算法的程序实现是自定义函数的具体应用,在培养学生“自顶向下”、“逐步求精”的意识起着重要的作用。 二、学情分析 教学对象是高中二年级学生,前面学习了程序设计的各种结构与自定义函数(过程)及常用基础算法,在学习程序设计各种结构的应用过程中,培养了学生用计算机编程解决现实中的问题的能力。在学习循环语句的过程中,应用了大量的“递推”算法,在第二章中,学习了如何使用自定义函数,在此基础上深入学习和体会自定义函数的应用,以递推算法的逆向思维进行求解问题,在学习过程中体会递归算法的思想过程。多维度的思考问题和解决问题是提高学生的学习兴趣关键。 三、教学目标 知识与技能: 1、理解什么是递归算法,学会递归算法的思想分析问题 2、能够应用递归算法编程处理实际问题 过程与方法:学生参与讨论,通过思考、动手操作,体验递归算法的方法 情感态度与价值:结合数学中的实例,激发学生使用数学知识建模的意识,培养学生多维度的思考问题和解决问题。 四、教学重点与难点 重点:理解什么是递归算法 难点:学生用递归算法的思想分析问题 五、教学过程 进程教师活动学生活动设计意图 创设情境课堂导入: 师:今天我们先做一个小的智力题目 有4个人排成一队,问最后一个人的身高时,他 说比第3个人高2厘米;问第3个人的身高时, 他说比第2个人高2厘米;问第2个人的身高时, 他说比第1个人高2厘米;最后问第1个人的身 师生共同活动找 出递变规律 使用情境教学法 在此活动过程中能 让学生初步从活动 中体验“问题的发与 收”从而走进了递归 的思维模式,为进一

算法设计与分析(详细解析(含源代码))

常用算法设计方法 要使计算机能完成人们预定的工作,首先必须为如何完成预定的工作设计一个算法,然后再根据算法编写程序。计算机程序要对问题的每个对象和处理规则给出正确详尽的描述,其中程序的数据结构和变量用来描述问题的对象,程序结构、函数和语句用来描述问题的算法。算法数据结构是程序的两个重要方面。 算法是问题求解过程的精确描述,一个算法由有限条可完全机械地执行的、有确定结果的指令组成。指令正确地描述了要完成的任务和它们被执行的顺序。计算机按算法指令所描述的顺序执行算法的指令能在有限的步骤内终止,或终止于给出问题的解,或终止于指出问题对此输入数据无解。 通常求解一个问题可能会有多种算法可供选择,选择的主要标准是算法的正确性和可靠性,简单性和易理解性。其次是算法所需要的存储空间少和执行更快等。 算法设计是一件非常困难的工作,经常采用的算法设计技术主要有迭代法、穷举搜索法、递推法、贪婪法、回溯法、分治法、动态规划法等等。另外,为了更简洁的形式设计和藐视算法,在算法设计时又常常采用递归技术,用递归描述算法。 一、迭代法 迭代法是用于求方程或方程组近似根的一种常用的算法设计方法。设方程为f(x)=0,用某种数学方法导出等价的形式x=g(x),然后按以下步骤执行: (1)选一个方程的近似根,赋给变量x0; (2)将x0的值保存于变量x1,然后计算g(x1),并将结果存于变量x0; (3)当x0与x1的差的绝对值还小于指定的精度要求时,重复步骤(2)的计算。 若方程有根,并且用上述方法计算出来的近似根序列收敛,则按上述方法求得的x0就认为是方程的根。上述算法用C程序的形式表示为: 【算法】迭代法求方程的根 { x0=初始近似根; do { x1=x0; x0=g(x1);/*按特定的方程计算新的近似根*/ } while ( fabs(x0-x1)>Epsilon); printf(“方程的近似根是%f\n”,x0); } 迭代算法也常用于求方程组的根,令 X=(x0,x1,…,x n-1) 设方程组为:

汉诺塔C递归算法详细解答

汉诺塔C递归算法详细解答 程序如下: void move(char x,char y){ printf("%c-->%c\n",x,y); } void hanoi(intn,charone,chartwo,char three){ /*将n个盘从one座借助two座,移到three座*/ if(n==1) move(one,three); else{ hanoi(n-1,one,three,two); move(one,three); hanoi(n-1,two,one,three); } } main(){ int n; printf("input the number of diskes:"); scanf("%d",&n); printf("The step to moving %3d diskes:\n",n); hanoi(n,'A','B','C'); } Hanoi塔问题, 算法分析如下,设A上有n个盘子。 如果n=1,则将圆盘从A直接移动到C。 如果n=2,则: (1)将A上的n-1(等于1)个圆盘移到B上; (2)再将A上的一个圆盘移到C上; (3)最后将B上的n-1(等于1)个圆盘移到C上。 如果n=3,则: A)将A上的n-1(等于2,令其为n`)个圆盘移到B(借助于C),步骤如下:(1)将A上的n`-1(等于1)个圆盘移到C上。 (2)将A上的一个圆盘移到B。 (3)将C上的n`-1(等于1)个圆盘移到B。 B)将A上的一个圆盘移到C。 C)将B上的n-1(等于2,令其为n`)个圆盘移到C(借助A),步骤如下:(1)将B上的n`-1(等于1)个圆盘移到A。 (2)将B上的一个盘子移到C。 (3)将A上的n`-1(等于1)个圆盘移到C。到此,完成了三个圆盘的移动过程。

递归讲解

复习 输入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;

五种算法知识讲解

算法 分治算法 一、基本概念 在计算机科学中,分治法是一种很重要的算法。字面上的解释是“分而治之”,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。这个技巧是很多高效算法的基础,如排序算法(快速排序,归并排序),傅立叶变换(快速傅立叶变换)…… 任何一个可以用计算机求解的问题所需的计算时间都与其规模有关。问题的规模越小,越容易直接求解,解题所需的计算时间也越少。例如,对于n个元素的排序问题,当n=1时,不需任何计算。n=2时,只要作一次比较即可排好序。n=3时只要作3次比较即可,…。而当n较大时,问题就不那么容易处理了。要想直接解决一个规模较大的问题,有时是相当困难的。 二、基本思想及策略 分治法的设计思想是:将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之。 分治策略是:对于一个规模为n的问题,若该问题可以容易地解决(比如说规模n较小)则直接解决,否则将其分解为k个规模较小的子问题,这些子问题互相独立且与原问题形式相同,递归地解这些子问题,然后将各子问题的解合并得到原问题的解。这种算法设计策略叫做分治法。 如果原问题可分割成k个子问题,1

相关文档
最新文档