1. 程式人生 > >遞迴程式設計方法及例項

遞迴程式設計方法及例項

 

 

                                                                                                遞迴程式設計方法及例項

 

 

    例項1:一個人趕著鴨子去每個村莊賣,每經過一個村子賣去所趕鴨子的一半又一隻。這樣他經過了七個村子後還剩兩隻鴨子,問他出發時共趕多少隻鴨子?經過每個村子賣出多少隻鴨子?

 

//賣鴨子
#include<stdio.h>
int num(int sum,int day);
int main()
{
	int s=num(2,8);                         //第七天還剩餘鴨子2只
	printf("鴨子總數為%d只\n\n",s);
	for(int day=1;day<=7;day++)             //輸出每天賣出的鴨子數
	{
		printf("第%d天賣出%d只\n",day,s/2+1);
		s=s/2-1;
	}
	return 0;
}
int num(int sum,int day)
{
	if(day==1)
		return sum;
	else
		return num((sum+1)*2,day-1);        //遞迴計算鴨子數
	return 0;
}

 

   例項2:角谷定理。輸入一個自然數,若為偶數,則把它除以2,若為奇數,則把它乘以3加1。經過如此有限次運算後,總可以得到自然數值1。求經過多少次可得到自然數1。

如:輸入22,

輸出 22 11 34 17 52 26 13 40 20 10 5 16 8 4 1

 

//角谷定理
#include<stdio.h>
int step;
int calculate(int a);
int main()
{
	int a;                       //輸入的自然數a
	printf("請輸入一個自然數:");
	scanf("%d",&a);  
	calculate(a);
	return 0;
}
int calculate(int a)
{
	printf("%6d",a);
	if(a==1)
		printf("\nStep is %d\n",step+1);
        else
	{
	 	if(a%2==0)           //輸入的自然數為偶數
		{
			a=a/2;
			step++;          //每計算一次,step加1
			calculate(a);    //遞迴呼叫
		}
		else if(a%2!=0)      //輸入的自然數為奇數
		{
			a=a*3+1;
			step++;
			calculate(a);
		}
	}
	return 0;
}
if(a%2==0) //輸入的自然數為偶數 { a=a/2; step++; //每計算一次,step加1 calculate(a); //遞迴呼叫 } else if(a%2!=0) //輸入的自然數為奇數 { a=a*3+1; step++; calculate(a); } } return 0; }

           例項三:電話號碼對應的字元組合:在電話或者手機上,一個數字如2對應著字母ABC,7對應著PQRS。那麼數字串27所對應的字元的可能組合就有3*4=12種(如AP,BR等)。現在輸入一個3到11位長的電話號碼,請打印出這個電話號碼所對應的字元的所有可能組合和組合數。

 

 

//電話號碼對應字元組合
//假設此程式中,0和1對應的英文字母分別為'*','#'
//做出此假設的原因是因為0和1沒有對應的英文字母
#include<stdio.h>
#define M 12
#define N 30
int a[M];               //a[M]儲存電話號碼每位上的數字
char b[28]={'*','#','A','B','C','D','E','F','G','H','I','J','K','L','M','N',
'O','P','Q','R','S','T','U','V','W','X','Y','Z'}; //b[M]儲存每個數字對應的不同的英文字母
char c[10][10] = {      //用字元陣列儲存每個數字對應的不同英文字母
    '*', '#', "ABC", "DEF",
    "GHI", "JKL", "MNO",
    "PQRS", "TUV", "WXYZ"};
int i,n;
int GroupNum(int x); 
int main()
{
	printf("請輸入電話號碼的總位數:");
	scanf("%d",&n);
	for(i=0;i<n;i++)
	{
		printf("請輸入電話號碼的第%d位:",i+1);
		scanf("%d",&a[i]);
	}
	i=0;
	printf("此電話號碼對應的字串組合共有%d組\n",GroupNum(1));
	return 0;
}
int GroupNum(int x)       //求總共組合數
{
	if(i==n)
		return x;
	else
	{
		if(a[i]==0 || a[i]==1)       
		{
			x=1*x;
			i++;
			GroupNum(x);  //遞迴呼叫  
		}
		else if(a[i]==2 || a[i]==3 || a[i]==4 || a[i]==5 || a[i]==6 || a[i]==8 )  
		{
			x=3*x;
			i++;
			GroupNum(x);
		}
		else
		{
			x=4*x;
			i++;
			GroupNum(x);
		}
	}
}

         例項4:日本著名數學遊戲專家中村義作教授提出這樣一個問題:父親將2520個桔子分給六個兒子。分完 後父親說:“老大將分給你的桔子的1/8給老二;老二拿到後連同原先的桔子分1/7給老三;老三拿到後連同原先的桔子分1/6給老四;老四拿到後連同原先的桔子分1/5給老五;老五拿到後連同原先的桔子分1/4給老六;老六拿到後連同原先的桔子分1/3給老大”。結果大家手中的桔子正好一樣多。問六兄弟原來手中各有多少桔子?

 

 

//分桔子問題
#include<stdio.h>
// oldUNum為每個人原有桔子數,getNum為得到的桔子數,n為第幾個人
int orangeNum(int oldNum,int getNum,int n);
int main()
{
	//老大原有240個桔子,得到210個
	orangeNum(240,210,1);
	return 0;
}
int orangeNum(int oldNum,int getNum, int n)
{
	int nextGetNum,nextOldNum,m;    
	if(n > 6)      //遞迴出口
		return 0;
        else
	{
		printf("     第%d個人原有桔子%d\n",n,oldNum);  //輸出原先橘子數
                if(n == 1)                                     //計算分給下一個人的橘子數
			nextGetNum = oldNum/(9 - n);
                else
             		nextGetNum = (oldNum+getNum)/(9-n);
        	nextOldNum = 2520/6* (8-n)/(7-n) - nextGetNum;  //下一個人原來的橘子
       	 	m = n+1;
        	orangeNum(nextOldNum,nextGetNum,m);
        }
	return 0;
}

	nextGetNum = oldNum/(9 - n);
                else
             		nextGetNum = (oldNum+getNum)/(9-n);
        	nextOldNum = 2520/6* (8-n)/(7-n) - nextGetNum;  //下一個人原來的橘子
       	 	m = n+1;
        	orangeNum(nextOldNum,nextGetNum,m);
        }
	return 0;
}

                                                                      遞迴程式設計方法總結

 

 

                                                                                                                                                 ————常見遞迴問題的分析與設計

       1.賣鴨子問題:這是常見的遞迴函式問題,求得一個遞迴函式公式即可。設計函式int num( int sum,int day);其中sum引數返回鴨子的數量,day引數返回第幾天。編寫遞迴程式的關鍵為確定遞迴出口和遞迴體。本題遞迴出口為當day==1時,return sum;遞迴體為當day!=1時,return num((sum+1)*2,day-1);同時在主函式中輸出每天所賣的鴨子數目。

       2.角谷定理:本題的設計思路比較簡單,仍然為一個遞迴函式問題。按照題意,輸入一個自然數,若為偶數,則把它除以2,若為奇數,則把它乘以3加1。經過如此有限次運算後,總可以得到自然數值1。得到遞迴結束出口為a==1;return總步數。遞迴體為當a==偶數時,自身除以二,並累加步數,返回calculate函式。當a為奇數時,自身乘三加一,並累加步數,返回calculate.

      3.電話號碼對應的字元組合:首先清楚2,3,4,5,6,8六個數字對應三個英文不同的字母,8,9三個數字對應4個不同的英文字母。由於數字1和0並沒有對應的英文字母,所以假設0和1分別對應’*’,’#’,這種假設並不影響最後遞迴演算法的設計。還可以增強程式的健壯性。

      計算組合數:需要統計電話號碼有幾個數字,且每個數字對應幾個英文字母。把每個數字對應的英文字母個數乘起來即為組合數。

      計算具體的組合:需要統計具體哪幾個數字,每個數字具體對應的英文字母是什麼。逐一進行組合即可。

      4.中村義作教授分桔子問題:這是一個環狀互相關聯的問題,想到應該使用遞迴來求解。首先應該分析得到已知和隱含條件。

最後桔子數目相等,則每人為420個。同時老大的身份最特殊,其餘的人都是先得到再給出桔子,而老大是先給出後得到,並且最後為420個,由老六分給他210個桔子,則老大原來有240個桔子,老大原先有240個桔子,分出210個桔子,問題的突破口就找到了。設計函式orangeNum(oldNum,getNum,n);三個引數分別為原有桔子數,得到桔子數,和第幾個人。將240,210,1三個引數傳入後,就開始了遞迴函式的呼叫,當n>6時,遞迴呼叫結束。

       遞迴問題總結:遞迴是程式直接或者間接呼叫自身的過程,遞迴模型由遞迴出口和遞迴體兩部分組成,分析問題時主要要抽象出遞迴出口和遞迴體。遞迴問題一般分為兩大類:一類是比較簡單的數學上的遞迴函式,要求算得一個函式值,只需要將遞迴函式翻譯出來即可。第二類為問題為一些實際的問題,沒有統一的規律可循,解決問題的關鍵為充分的理解問題,發現問題中的已知和隱含條件。根據邏輯關係分析此問題為什麼為一個遞迴問題,並由此得到遞迴出口和遞迴遞迴體,從而使問題得到解決。這類問題都比較複雜,需要進行大量的練習才能建立這種思維方式。