递归算法练习

问题1: 一个人赶着鸭子去每个村庄卖,每经过一个村子卖去所赶鸭子的一半又一只。这样他经过了七个村子后还剩两只鸭子,问他出发时共赶多少只鸭子?经过每个村子卖出多少只鸭子?

 

题目分析:下一村庄没卖出鸭子之前的数量和当前村庄剩余鸭子数满足:  

sub(n+1)  =  sub(n)/2 – 1  ------>  sub(n) = (sub(n+1) + 1)*2

 

算法构造:

          定义递归函数int curr_num(int n), 返回当前村剩余的鸭子书, 当n == 8时,结束,返回2。

 

算法实现(java):

package com.programingmethod.work6;
/**
 * 
 * @author dengyong
 * @Description:${通过递归解决卖鸭子的问题}
 * @version 1.0
 * @date: 2018-11-15
 * 问题描述:一个人赶着鸭子去每个村庄卖,每经过一个村子卖去所赶鸭子的一半又一只。
 *       这样他经过了七个村子后还剩两只鸭子,问他出发时共赶多少只鸭子?
 *       经过每个村子卖出多少只鸭子?
 */
public class Duck {
	/**
	 * 计算当前村庄剩余的鸭子数
	 * 原理:
	 *    下一村庄剩余鸭子数和当前村庄剩余鸭子数满足:   sub(n+1) = sub(n)/2 - 1
	 *    ==》sub(n) = (sub(n+1) + 1)*2
	 *    这就是递推公式
	 * @param n 经过的村子编号
	 * @return 该村子当前的鸭子数
	 */
	public static int curr_n(int n){
		
		int sub_n;// 当前村庄剩余的鸭子的数量
		
		// 递归的出口
		if (8 == n){ // 当经过第七个村子的时候,停止递归,剩余两只鸭子
			sub_n = 2; 
			System.out.println("经过第"+n+"个村子还剩"+sub_n+"个鸭子");
			return sub_n;
		} else {
			
			// sub(n) = (sub(n+1) + 1)*2
			sub_n = (curr_n(n+1)+1)*2;
			System.out.println("经过第"+n+"个村子"+"共有"+sub_n+"个鸭子,"+"卖了"+(sub_n/2+1)+"个鸭子");
			if (1 == n) {
				System.out.println("一共赶了: "+sub_n+"只鸭子");
			}
			return sub_n;
		}
		
	}
	
	public static void main(String[] args) {
		curr_n(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

 

题目分析: 先对输入的数n进行判断,若 n = 1,则输出1;若输入的数为偶数, 则把它除以2;若为奇数则乘3加1;直到n = 1为止。

 

算法构造:

          当n == 1 , 返回1

                     若 n%2 == 0, n = n/2  否则 n = n*3+1

算法实现(Java):

package com.programingmethod.work6;

/**
 * 
 * @author dengyong
 * 
 */
public class ToOne {
	/**
	 * 递归计算的方法
	 * @param count 计算的次数
	 * @param n 要计算的数
	 * @return 1
	 */
	public int f(int n, int count){
		System.out.println(n);
		// 当n==1的时候,返回即可, 输出计算次数
		if (1 == n){
			count += 1;
			System.out.println("一共计算了:"+count+"次");
			return 1;
		} else {
			// 如果是偶数
			if (0 == n%2){
				// 把这个数除2
				count += 1; //次数加一
				return f(n/2, count);
			} else { // 是奇数
				count += 1; //次数加一
				// 乘3加1
				return f(n*3+1, count);
			}
		}
	}
	
	/**
	 * 方法2:
	 * 每次执行一次就返回1,用一个计数器将每次执行的加起来即可
	 * @param n
	 * @return
	 */
	public int fCount(int n) {
		System.out.println(n);
		int count=1;
		if (1 == n){
			count = 1; 
			System.out.println("一共计算了:"+count+"次");
		} else {
			// 如果是偶数
			if (0 == n%2){
				// 把这个数除2
				// 计数器把下一次执行的次数加起来
				count += f(n/2, count);
			} else { // 是奇数
				// 乘3加1
				count += f(n*3+1, count);
			}
		}
		return 1; //每执行一次就相当于次数加一
	}
	
	public static void main(String[] args) {
		ToOne t = new ToOne();
		t.f(22, 0);
		t.fCount(22);
	}

}

运行截图:

 

问题3:电话号码对应的字符组合:在电话或者手机上,一个数字如2对应着字母ABC,7对应着PQRS。那么数字串27所对应的字符的可能组合就有3*4=12种(如AP,BR等)。现在输入一个3到11位长的电话号码,请打印出这个电话号码所对应的字符的所有可能组合和组合数。

 

题目分析:0-->()  1--->()   2—>(a, b, c)  3—>(d, e, f)  4--->(g, h, i)….

 

算法构造:用一个键值对将这些对应关系存储起来,每个电话号码进来后,从第一位开始递归,直到最后一位时开始返回结果。

算法实现(python):

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @File  : phoneNumber.py
# @Author: dengyong
# @Date  : 2018/11/16 15:46
# @Desc  :
"""
3.电话号码对应的字符组合:在电话或者手机上,一个数字如2对应着字母ABC,
7对应着PQRS。那么数字串27所对应的字符的可能组合就有3*4=12种(如AP,BR等)。
现在输入一个3到11位长的电话号码,请打印出这个电话号码所对应的字符的所有可能组合和组合数。
"""

# 存储某个键对应的字母,用一个列表存储
# 列表的下标对应了数字键,内容则是用一个元组存放了这一组字母
char = [(), (), ("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")]

# 每个数字键对应的字母个数
char_sum = [0, 0, 3, 3, 3, 3, 3, 4, 3, 4]


def searchGroup(numbers, temp_list, index, lenth):
    """
    递归方法体
    :param numbers: 电话号码 type-->[list]
    :param temp_list: 辅助列表,用于存放某个数字上的字母索引,动态变化的
    :param index: 电话号码的索引
    :param lenth: 电话号码的长度
    :return:
    """
    # 当数到最后一位的时候,
    if index == lenth:
        for i in range(lenth):
            print(char[numbers[i]][temp_list[i]], end="\t")
        print()
        return
    else:
        # 循环次数为当前数字上的字母数量
        for i in range(char_sum[numbers[index]]):
            temp_list[index] = i  # 当前数字的第一个字母,下一次循环就是当前字母的第二个字母
            searchGroup(numbers, temp_list, index + 1, lenth)


if __name__ == '__main__':
    number = [4, 9, 6, 6]
    answer = [_ for _ in range(len(number))]
    searchGroup(number, answer, 0, len(number))




运行截图:

 

 

 

问题4:日本著名数学游戏专家中村义作教授提出这样一个问题:父亲将2520个桔子分给六个儿子。分完 后父亲说:“老大将分给你的桔子的1/8给老二;老二拿到后连同原先的桔子分1/7给老三;老三拿到后连同原先的桔子分1/6给老四;老四拿到后连同原先的桔子分1/5给老五;老五拿到后连同原先的桔子分1/4给老六;老六拿到后连同原先的桔子分1/3给老大”。结果大家手中的桔子正好一样多。问六兄弟原来手中各有多少桔子?

 

题目分析: 1. 老六得到橘子后分给老大1/3还剩420 则:
                   设老六得到老五给的橘子后有x个橘子, 则: x(1 - 1/3) = 420 -> x = 630
                   则老六给了老大x*1/3 = 210
                   2. 设老大没给老二之前,原本原有x橘子, 则:x(1 - 1/8) + 210  = 420
                    则: --> x = 240  即老大原本有240个橘子,这就是递归的入口

 

算法构造:

# (下一个人原有的 + 别人给的)*(1 - 1/(9 - son_num - 1)) = 420
# -->下一个人原有的 = 420/((7-son_num)/(8-son_num)) - 别人给的

def juzi(son_num, history, other_give):
    """
    递归函数
    :param son_num: 儿子编号
    :param history: 之前原有的橘子数量
    :param other_give: 别人给他的橘子数量
    :return:
    """

 

算法实现(python):

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @File  : origin.py
# @Author: deng
# @Date  : 2018/11/16 17:11
# @Desc  :递归解决分橘子的问题
"""
问题:父亲将2520个桔子分给六个儿子。分完 后父亲说:“老大将分给你的桔子的1/8给老二;
     老二拿到后连同原先的桔子分1/7给老三;老三拿到后连同原先的桔子分1/6给老四;
     老四拿到后连同原先的桔子分1/5给老五;老五拿到后连同原先的桔子分1/4给老六;
    老六拿到后连同原先的桔子分1/3给老大”。结果大家手中的桔子正好一样多。
问: 六兄弟原来手中各有多少桔子?
"""

"""
解题思路:
1. 老六得到橘子后分给老大1/3还剩420 则:
   设老六得到老五给的橘子后有x个橘子, 则: x(1 - 1/3) = 420 -> x = 630
   则老六给了老大x*1/3 = 210
2. 设老大没给老二之前,原本原有x橘子, 则:x(1 - 1/8) + 210  = 420
   则: --> x = 240  即老大原本有240个橘子,这就是递归的入口
"""


def origin(son_num, history, other_give):
    """
    递归函数
    :param son_num: 儿子编号
    :param history: 之前原有的橘子数量
    :param other_give: 别人给他的橘子数量
    :return:
    """
    if son_num > 6:
        # 递归出口, 结束
        return
    else:
        # 如果是大儿子, 先给别人, 别人再给他
        if son_num == 1:
            print("老 %d 原有的橘子数量为: -> %d" % (son_num, history))
            # 计算老大给别人的橘子数量
            give_other = history/(9 - son_num)
            print("老 %d 给出的橘子数量为:-> %d" % (son_num, give_other))
            print("老 %d 从老 %d 那得到的橘子数量为: -> %d" % (son_num, 6, other_give))
            print("------------------------------------------------------")
            # 下一个人的橘子中别人给的=这个人给出去的
            other_give = give_other
        else:
            # 其他儿子都是别人先给他,他再给别人
            print("老 %d 原有的橘子数量为:-> %d" % (son_num, history))
            print("老 %d 从老 %d 那得到的橘子数量为: -> %d" % (son_num, son_num-1, other_give))
            # 计算给别人的
            give_other = (history+other_give)/(9-son_num)
            print("老 %d 给出的橘子数量为: -> %d" % (son_num, give_other))
            print("------------------------------------------------------")
            other_give = give_other

        # 这里是计算下一个人原有的橘子数量
        # (下一个人原有的 + 别人给的)*(1 - 1/(9 - son_num - 1)) = 420
        # -->下一个人原有的 = 420/((7-son_num)/(8-son_num)) - 别人给的
        history = 420/((7 - son_num)/(8 - son_num)) - other_give

        origin(son_num + 1, history, other_give)


if __name__ == '__main__':
    # 老大原本有240个橘子, 老六给了210个
    origin(1, 240, 210)

运行截图:

 

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值