归纳与递归

来源:互联网 发布:john resig 知乎 编辑:程序博客网 时间:2024/05/18 01:04
归纳与递归
        如果我们知道如何求解规模为n-1的问题,那么我们的任务就化为如何把解法扩展到规模为n的问题

    想必大家都听过这样一个故事,从前有座山,山上有座庙,庙里有个老和尚,老和尚他说从前有座山,山上有座庙,庙里有个老和尚,老和尚他说从前有座山,山上有座庙,庙里有个老和尚,老和尚他说从前有座山,山上有座庙,……。这个故事没完没了的重复着,直到讲故事的人烦了、累了才会停下来。这个故事就是一个典型的递归的例子,故事中直接调用了故事本身,从而使得这个故事永无止境的扩展下去。

递归的定义
        
递归:参见“递归”。
    什么?这个定义什么也没有说呀!好吧,改一下:
递归:如果你还没有明白递归式什么意思的话,参见“递归”。
    哦,也许这次你明白了:原来递归就是“自己用到自己”的意思。这个定义比上面要好一些,因为当你终于悟出其中的道理后,就不必继续“参见”下去了。事实上,递归的含义比这个要广——

    A经理:“这事不归我管,你去找B经理。”于是你去找B经理。
    B经理:“这事不归我管,你去找A经理。”于是你回到了A经理这儿。
    接下来发生的事情就不难想到了:只要两个经理的说辞不变,你又始终听话,你将会永远往返于两个经理之间,这叫无限递归

    回忆一下,正整数是如何定义的?正整数是1,2,3,…这些数。这样的定义也许对于小学生来说是没有任何问题的,但当你开始觉得这个定义“不太严密”时,你或许会喜欢这样的定义:
(1)1是正整数;
(2)如果n是正整数,n+1也是正整数。
(3)只有通过(1)、(2)定义出来的才是正整数。
    这样的定义也是递归的:在“正整数”没有定义完时,就用到了“正整数”的定义!这和前面的“参见递归”是完全一样的,本质是相同的,只是没有那么直接。偶数的定义也是递归的,仿照正整数的定义,同学们可以试着写出来。
    简洁而严密,这就是递归定义的优点。
递归:
    函数(子程序)的实现部分可以包含对该函数(子程序)自身的调用。在这种情况下,该函数(子程序)就是递归的。

【程序1】用递归法计算1到n的和
            使用递归算法需要满足两个条件:
    (1) 递归终止条件;//否则将无限递归下去
    (2) 递归递推公式;
    对于本题,令f(n)=1+2+3+...+n,则f(n)=n+(1+2+...+n-1)=n+f(n-1),即
    递归公式:f(n)=n+f(n-1);
    终止条件:f(1)=1;
C++ code:#include<iostream>using namespace std;long f(int k){   if(k==1) return 1; //递归的终止条件elsereturn f(k-1)+k;}int main(){int n;cin >> n;cout << f(n) << endl;return 0;}
        提示:递归函数必须要写终止条件,否则将产生无限递归。
如果上面的程序同学们无法理解,请看下面的比喻:
皇帝(假设最顶层为f(3)):大臣,你给我算一下f(3)。
大臣(拥有f(3)):知府,你给我算一下f(2)。
知府(拥有f(2)):县令,你给我算一下f(1)。
县令(拥有f(1)):会老爷,f(1)=1。。
知府:(心算f(2)=f(1)+2=3)回大人,f(2)=3。
大臣:(心算f(3)=f(2)+3=6)回皇上,f(3)=6。
皇上满意了。
虽然比喻不很恰当,但能够说明一些问题。其实设计递归程序的重点就是给下级安排工作。
递归函数f(3)的详细过程:


【程序2】用递归法计算n的阶乘
        n!=1*2*...*n
样例输入:6
样例输出:720
【分析】
递归公式:dg(n)=n * dg(n-1)
终止条件:dg(0)=1 //即0!=1
C++ code:#include<iostream>using namespace std;long dg(int k); int main(){int n;cin >> n;cout << dg(n) << endl;}long dg(int k){   if(k==0) return 1;   //递归的终止条件  else  return k*dg(k-1);}

【程序3】将一个整数反向输出
         样例输入:3125
    样例输出:5213

【分析】
    函数(子程序)dg的执行可以分以下两种情况:
      (1) 如果n<10,那么直接输出n;(递归的终止条件)
      (2) 如果n>=10,那么分两个子任务。
         i) 输出最后1位数字,终止函数的执行;
         ii) 将n的最后1位删除,即n=n/10;
         iii) 继续执行该函数。
C++ code:#include<iostream>using namespace std;void dg(int k); int main(){  int n;cin>>n;dg(n);}void dg(int k){   if(k<10) cout << k << endl;else{cout << (k%10);dg(k/10);}}

【程序4】兔子繁殖问题(laoj1264)
         有一种兔子,出生后一个月就可以长大,然后再过一个月一对长大的兔子就可以生育一对小兔子且以后每个月都能生育一对。
     现在,我们有一对刚出生的这种兔子,那么,n个月过后,我们会有多少对兔子呢?假设所有的兔子都不会死亡。
输入:5
输出:5
【分析】具体分析见“归纳与递推” >> “【程序2】”,这里不再讨论。
因此,递归公式:f(n)=f(n-1)+f(n-2)
而递归的终止条件则是:n=1或n=2时,f(n)=1
C++ code:#include<iostream>using namespace std;long dg(int k); int main(){   int n;   cin>>n;   cout << dg(n) << endl;}long dg(int k){   if((k==1)||(k==2)) return 1;elsereturn dg(k-1)+dg(k-2);}

【程序5】汉诺塔问题
        如下图所示有A,B,C三根细柱,A柱子上有n个从小到大的圆盘,小盘放在大盘上面,要求将这n个圆盘移到C柱子上,移动过程中,可以借助B柱子,每次只许移动一个圆盘,在任何一根柱子上都不允许出现大盘压住小盘的情况,请编写一个程序显示移动步骤。

输入:3
输出:
A->C
A->B
C->B
A->C
B->A
B->C
A->C
【分析】原问题可以分成3个子问题:
     (1) 将A柱子最上面的n-1个圆盘借助C柱子移到B柱。
     (2) 将A柱子剩下的一个圆盘移到C柱子。
     (3) 将B柱子的n-1个圆盘借助A柱子移到C柱子。
显然子问题(1)和子问题(3)与原问题具有相同的性质,只是盘子数少了一个,并且代表原柱、中间柱、目标柱的柱子代号不同而已。因此可用递归的方法解决,结束递归的边界条件是n=0。
C++ code:#include<iostream>using namespace std;void hanoi(int k,char a,char b,char c){      if(k>0){hanoi(k-1,a,c,b);cout << a << "->" << c << endl;hanoi(k-1,b,a,c);   }  return;}int main(){   int n;   cin >> n;   hanoi(n,'A','B','C');}或者:#include<iostream>using namespace std;void hanoi(int k,char a,char b,char c){     if(k==1)cout << a << "->" << c << endl;else{hanoi(k-1,a,c,b);cout << a << "->" << c << endl;hanoi(k-1,b,a,c);}  return;}int main(){   int n;   cin >> n;   hanoi(n,'A','B','C');}


【程序6】最大公约数
         输入两个数,输出它们的最大公约数。
【分析】采用辗转相除法。
C++ code:#include<iostream>using namespace std;int gcd(int m,int n); int main(){   int m,n;   cin >> m >> n;   cout << gcd(m,n) << endl;}int gcd(int m,int n){   if(n==0) return m;elsereturn gcd(n,m%n);}


【程序7】用递归算法求x^n
         【分析】把x^n分解成:
x^0 = 1 (n=0)
x^1 = x * x^0 (n=1)
x^2 = x * x^1 (n=2)
……
x^n = x * x^(n-1) (n>1)
C++ code:#include<iostream>using namespace std;int x,n;long dg(int k){   if(k==0) return 1;elsereturn x*dg(k-1);} int main(){   cin >> x >> n;   cout << dg(n) << endl;}
0 1