一.实验目的
掌握递归程序设计的方法。明确递归的概念,通过对问题的分析,找出递归关系以及递归出口以对问题进行递归结构设计;
掌握递归程序转换为非递归程序的方法。
二 .实验内容
用递归方法设计下列各题,并给出每道题目的递归出口(递归结束的条件)和递归表达式。同时考虑题目可否设计为非递归方法,如果可以,设计出非递归的算法。
1.一个人赶着鸭子去每个村庄卖,每经过一个村子卖去所赶鸭子的一半又一只。这样他经过了七个村子后还剩两只鸭子,问他出发时共赶多少只鸭子?经过每个村子卖出多少只鸭子?
2.角谷定理。输入一个自然数,若为偶数,则把它除以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.题目分析
递归的主要思想是把大问题转化为一个小问题和一些常量,虽然小问题还没有解决,但已经向目标靠近了一步,这是一个“量变”,当到达递归的出口时,便发生了“质变”,递归问题就解决了。所以,问题在于如何将碰到的问题转化,确定“大问题”和“小问题”以及之间的联系,最后找到递归出口,问题就迎刃而解了,有了以上理解,对这两个问题具体分析一下。
1.1 求鸭子数量
大问题是求在经过某一个村子前有多少鸭子,小问题是经过上一个村子有多少鸭子,他们之间的联系是:这个村子=(后个村子+1)*2,同时在第七个村子时的鸭子很容易得出6只,但这样存在一个,递归公式是往后递归的,得不到结果,所以需要处理一下,将过程反过来看,问题变为到第7个村子鸭子有6只,要求前面的村子有多少鸭子,则:这个村子=(前村子+1)*2。
这个问题非递归也可以解决,任然从第七个村子往前看,经过每个村子前可以大致排一下:
… 30 14 6 2(最后剩下)。
可以看出,要求某一个村子对应的鸭子数,只需要在6的基础上进行加1再乘以2的多次运算即可,运算次数为7-村子序号+1。
1.2 角谷定理
大问题是求一个数经过特定操作变为1的操作次数,小问题是这个数过一次变换得到的数还需要操作多少次,他们之间的关系是前者等于后者加1,递归出口时当这个数等于2时,变换次数为1,这也是操作次数与具体数之间的联系。
从非递归的角度看,也能解决,只需要构建无限循环,结束条件为数等于1,在函数体对数进行对应的操作同时定义累加器记录操作数即可。
2.算法构造
2.1 经过上面对的分析,容易得出数学表达式如下:
递归调用函数:
非递归调用流程图:
2.2 角谷定理
递归调用
非递归调用
3.算法实现
3.1 鸭子
package RecursionProgram;
public class test1 {
public static int question1_recursion(int x) {
if(x==1)
return 6;
else return ((question1_recursion(x-1)+1)*2);
}
public static int question1_norec(int x) {
int sum=6;
for(int i=0;i<(7-x);i++) {
sum=2*(sum+1);
}
return sum;
}
public static void main(String[] args) {
/* 递归解法
System.out.println("出发时赶了"+question1_recursion(7)+"只鸭");
for(int x=7;x>1;x--) {
System.out.println("第"+(7-x+1)+"个村庄卖了"+(question1_recursion(x)-question1_recursion(x-1))+"只鸭");
}
System.out.println("第7个村庄卖了"+(question1_recursion(1)-2)+"只鸭");
*/
//非递归解法
System.out.println("出发时赶了"+question1_norec(1)+"只鸭");
for(int x=1;x<7;x++) {
System.out.println("第"+x+"个村庄卖了"+(question1_norec(x)-question1_norec(x+1))+"只鸭");
}
System.out.println("第7个村庄卖了"+(question1_norec(7)-2)+"只鸭");
}
}
3.2 角谷定理
package RecursionProgram;
public class test2 {
public static int question_recurtion(int x) {
if(x==2)
return 1;
else if(x%2==0)
return question_recurtion(x/2)+1;
else return question_recurtion(x*3+1)+1;
}
public static int question_norec(int x) {
int count = 0;
while(x!=1) {
if(x%2==0) {
x=x/2;
count++;
}
else {
x=x*3+1;
count++;
}
}
return count;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
//System.out.println(question_recurtion(22));
System.out.println(question_norec(22));
}
}
4.调试、测试及运行结果
4.1 鸭子问题
4.2 角谷定理
5.经验归纳
递归问题重点在于问题的抽象以及划分,找到问题的大问题和小问题,找出他们之间的联系,个人感觉这是最难的部分,最后的递归出口不难找到,解决这3个点列出数学函数表方式,相应的编程就容易多了。