1. 程式人生 > >遞迴演算法的練習

遞迴演算法的練習

一.一個人趕著鴨子去每個村莊賣,每經過一個村子賣去所趕鴨子的一半又一隻。這樣他經過了七個村子後還剩兩隻鴨子,問他出發時共趕多少隻鴨子?經過每個村子賣出多少隻鴨子?
1.題目分析
經過7個村莊後還剩兩隻鴨子,每經過一個村莊賣當前鴨子的一半加一隻,所以遞迴體是經過前一個村子的鴨子數除以2再減去1就是到達下一個村子的時候的鴨子數,所以要想求在第一個村子的鴨子數,必須從後往前推。
2.演算法構造
當村子數i=0,鴨子數number=2;當村子數0<i<7,經過第i個村子前的鴨子數number=(number+1)*2,在經過第i 個村子時賣的鴨子數m=number/2+1;
3.演算法實現
見檔案sellduck.c

#include<stdio.h>
/*
 *author:軟工1604 徐於敏 16408070731
 *date:2018-11-15
 *description:一個人趕著鴨子去每個村莊賣,每經過一個村子賣去所趕鴨子的一半又一隻。這樣他經過了七個村子後還剩兩隻鴨子,問他出發時共趕多少隻鴨子?經過每個村子賣出多少隻鴨子?	
 *function:sellduck()函式定義了1個全域性變數i代表村子的數量,通過遞迴實現了計算出發時的鴨子數量
 *version:0.1
 */

int	number=2;//最後的鴨子數量
int m;//用來記錄經過每個村子賣出的鴨子數量
int main()
{
	sellduck(7);
}


int sellduck(int i)
{
	
	if(0==i)//i為0時候結束
	{
		printf("他出發時一共趕了%d只鴨子",number);
	}
	else{
		number=(number+1)*2; //計算在經過第i個村子前的鴨子數number 	
		m=number/2+1; //計算在經過第i 個村子時賣的鴨子數m	 	
		printf( "經過第%d個村子時,他賣出%d只鴨子。\n",i,m);
		i--;//村子數量-1
		return sellduck(i); 
	}
}



4.執行結果
在這裡插入圖片描述

二.角谷定理。輸入一個自然數,若為偶數,則把它除以2,若為奇數,則把它乘以3加1。經過如此有限次運算後,總可以得到自然數值1。求經過多少次可得到自然數1。
如:輸入22,
輸出 22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1
STEP=16
1.題目演算法分析
先判斷輸入的數字是偶數還是奇數,是偶數除以2,是奇數乘以3加1,如此反覆呼叫自身,每做完一次步數+1,並輸出此時的數字,最後得到得到的數字為1,此時遞迴結束
2.演算法實現
見solvejiaogu.c

#include<stdio.h>
/*author:軟工1604 徐於敏 16408070731 
 *date:2018-11-15
 *description:.角谷定理。輸入一個自然數,若為偶數,則把它除以2,若為奇數,則把它乘以3加1。
 *經過如此有限次運算後,總可以得到自然數值1。求經過多少次可得到自然數1。
 *function:定義一個solve()函式,n代表一開始輸入的自然數,step代表步數,通過遞迴實現角谷定理
 *version:0.1
 */

int main()
{	
	int a;
	printf("輸入一個自然數:");
	scanf("%d",&a);
	solve(a,1);
	
}

int solve(int n,int step)
{
	
	if(n%2==0){//判斷是否為偶數,是偶數除以2
		n=n/2;
		printf("%d\n",n);
		step++;//步數+1
	}
	else {//否則是奇數,將他乘以3加1
		n=n*3+1;
		printf("%d\n",n);
		step++;//步數+1
	}
	if(n==1){
		printf("總的步數是%d次",step);
	}
	if(n!=1){//若當前得到的數字為1,結束遞迴,否則繼續
		solve(n,step);
	}
}

3.執行結果
在這裡插入圖片描述

三.電話號碼對應的字元組合:在電話或者手機上,一個數字如2對應著字母ABC,7對應著PQRS。那麼數字串27所對應的字元的可能組合就有3*4=12種(如AP,BR等)。現在輸入一個3到11位長的電話號碼,請打印出這個電話號碼所對應的字元的所有可能組合和組合數。
1.題目分析
2對應ABC,3對應DEF,4對應GHI,5對應JKL,6對應MNO,7對應PQRS,8對應TUV,9對應WXYZ,1和0對應空。
2.演算法構造
當輸入一個數字時,返回這個數字所有可能性的組合。遞迴體是當輸入一串數字,每個數字代表不同的字串,返回最後一個數字跟前面已產生的字串進行組合,所以首先應該建一個數組,用來存放每個數字代表的字串組合,然後在建一個數組,用來存放每個字串的長度,在函式中,先進行判斷是否是最後一位,然後根據判斷執行函式。
3.演算法實現
見phone.cpp

/*
 *author:軟工1604 徐於敏 16408070731
 *date:2018-11-15
 *description:電話號碼對應的字元組合:在電話或者手機上,一個數字如2對應著字母ABC,7對應著PQRS。那麼數字串27所對應的字元的可能組合就有3*4=12種(如AP,BR等)。現在輸入一個3到11位長的電話號碼,請打印出這個電話號碼所對應的字元的所有可能組合和組合數。
 *function:通過遞迴函式tellphone實現
 *version:0.1
 */

#include<iostream>
using namespace std;


char* phone[10]={"","","ABC","DEF","GHI","JKL","MNO","PQRS","TUV","WXYZ"};//陣列phone用來存放數字對應的字母
int num[10]={0,0,3,3,3,3,3,4,3,4};//陣列num用來存放每一個數字對應的字母的數量
char input[15];//陣列input存放輸入的電話號碼
char output[15];//陣列ouput存放輸出的組合



void tellphone(int n,int len)
{
    if(n == len)
    {
        cout<<output<<endl;
    }
    for(int i=0; i<num[input[n]]; i++)
    {
        output[n] = phone[input[n]][i];
        tellphone(n+1,len);
    }
}
int main()
{
	int len;
	printf("輸入一個3到11位長的電話號碼:");
    scanf("%s",input);
	printf("該電話號碼所對應字元的所有可能的組合是:\n");
    len=strlen(input);//統計輸入的電話號碼的長度
    int count = 1;
    for(int i=0; i<len; i++)//計算組合數量 
    {
        input[i] -= '0';
        count *= num[input[i]];
    }
    tellphone(0,len);//呼叫函式
    cout<<"組合數:"<<count<<endl;///輸出組合數量
    return 0;
}

3.執行結果
在這裡插入圖片描述

四.日本著名數學遊戲專家中村義作教授提出這樣一個問題:父親將2520個桔子分給六個兒子。分完 後父親說:“老大將分給你的桔子的1/8給老二;老二拿到後連同原先的桔子分1/7給老三;老三拿到後連同原先的桔子分1/6給老四;老四拿到後連同原先的桔子分1/5給老五;老五拿到後連同原先的桔子分1/4給老六;老六拿到後連同原先的桔子分1/3給老大”。結果大家手中的桔子正好一樣多。問六兄弟原來手中各有多少桔子?
1.題目分析
每個兒子拿到的橘子數目分別是原有的和現有的,除了老大,其他的兒子原有的+別人給的-給別人的=現有的=平均數,所以,可以得出原有的=現有的+給別人的-別人給的。
2.演算法構造
當老幾n>6,結束;當0<n<6時,算出分給下一個人的桔子數,下一個人的桔子數,下一人加上之前的桔子數的總數,接著一個個呼叫實現
3.演算法實現
見orange.cpp

/*
 *author:軟工1604徐於敏16408070731
 *date:2018-11-15
 *description:日本著名數學遊戲專家中村義作教授提出這樣一個問題:父親將2520個桔子分給六個兒子。分完 後父親說:“老大將分給你的桔子的1/8給老二;老二拿到後連同原先的桔子分1/7給老三;老三拿到後連同原先的桔子分1/6給老四;老四拿到後連同原先的桔子分1/5給老五;老五拿到後連同原先的桔子分1/4給老六;老六拿到後連同原先的桔子分1/3給老大”。結果大家手中的桔子正好一樣多。問六兄弟原來手中各有多少桔子?
 version:0.1
 */
#include<iostream>
using namespace std;


int orange(int n,int before, int after,int m )//n是老幾,before是之前的數量,after是之後的數量,m是從八分之一開始的分桔子的比例
{
	if(n>6)//結束條件
	{
		return 0;
	}
	else
	{
		printf("老%d原有的桔子數:%d\n",n,before);
		int givenum = after/m;//分給下一個人的桔子數
		int nextBnum = 420*(m-1)/(m-2)-givenum;//下一個人的桔子數
		int afterGetnum = nextBeforenum+givenum;//下一人加上之前的桔子數的總數
		return orange(n+1,nextBeforenum,afterGetnum,m-1);
	}
}
int main()
{
	orange(1,240,240,8);
}```

 4.執行結果
  ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20181117154556919.PNG?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MTIyMDgzMw==,size_16,color_FFFFFF,t_70)




五.歸納總結
遞迴的解決首要就是,找到遞迴體和遞迴出口,將大問題分解成小問題,將小問題分解成可以直接解決的問題,在往上推,最終解決問題。