Tree recursion example: Partitions

问题描述

The number of partitions of a positive integer n, using parts up to size m, is the number of ways in which n can be expressed as the sum of positive integer parts up to m in increasing order. For example, the number of partitions of 6 using parts up to 4 is 9.

  1. 6 = 2 + 4
  2. 6 = 1 + 1 + 4
  3. 6 = 3 + 3
  4. 6 = 1 + 2 + 3
  5. 6 = 1 + 1 + 1 + 3
  6. 6 = 2 + 2 + 2
  7. 6 = 1 + 1 + 2 + 2
  8. 6 = 1 + 1 + 1 + 1 + 2
  9. 6 = 1 + 1 + 1 + 1 + 1 + 1

题目分析

要求将正整数n表示为不大于正整数m的数字之和(要求按升序表示),将这种关系记为(n, m),代表着表示法的数量。

可以发现(n, m)的所有表示方法可以分为两部分,一部分表示方法中含有m,另一部分表示方法中没有m(如题目例子中表示方法1、2含有数字4,剩余3-7中不含数字4)。那么这两部分可以分别记为(n - m, m)(即将n表示为一个m和剩下的n - m之和,如上例1、2)和(n, m - 1),这样实现了问题的简化。

将求(n, m)的函数记为count_partition(n, m),那么通过上述分析可以得到count_partition(n, m) = count_partition(n - m, m) + count_partition(n, m - 1)。

再来考虑base case的情况:

1.对于一个递归函数count_partition(n, m),如果输入的n == 0,那么肯定是上一步递归时n0 = m0,且是count_partition(n0 - m0, m0)的那一个tree recursion的branch,于是return 1(即用不大于m的数字表示n,且表示法中含有一个m,那么肯定只有这一种表示法n = m);

2.如果n < 0,同1类似,即用不大于m的数字表示n,要求表示法中含有一个m,那么肯定不存在这种表示法,return 0;

3.如果m = 0,那么上一步递归时m0 = 1,那么也只有一种表示法,即用n个1的和表示n,所以return 1.

代码

def count_partition(n, m):
    """Count the ways to partition n using parts up to m."""
    if n == 0:
        return 1
    elif n < 0:
        return 0
    elif m == 0:
        return 0
    else:
        return count_partition(n - m, m) + count_partition(n, m - 1)

延伸问题

Given a positive integer total, a set of coins makes change for total if the sum of the values of the coins is total. Here we will use standard US Coin values: 1, 5, 10, 25. For example, the following sets make change for 15:

  • 15 1-cent coins
  • 10 1-cent, 1 5-cent coins
  • 5 1-cent, 2 5-cent coins
  • 5 1-cent, 1 10-cent coins
  • 3 5-cent coins
  • 1 5-cent, 1 10-cent coin

Thus, there are 6 ways to make change for 15. Write a recursive function count_coins that takes a positive integer total and returns the number of ways to make change for total using coins.

You can use either of the functions given to you:

  • next_larger_coin will return the next larger coin denomination from the input, i.e. next_larger_coin(5) is 10.
  • next_smaller_coin will return the next smaller coin denomination from the input, i.e. next_smaller_coin(5) is 1.
  • Either function will return None if the next coin value does not exist

There are two main ways in which you can approach this problem. One way uses next_larger_coin, and another uses next_smaller_coin.

Important: Use recursion; the tests will fail if you use loops.

Hint: Refer the implementation of count_partitions for an example of how to count the ways to sum up to a final value with smaller parts. If you need to keep track of more than one value across recursive calls, consider writing a helper function.

def next_larger_coin(coin):
    """Returns the next larger coin in order.
    >>> next_larger_coin(1)
    5
    >>> next_larger_coin(5)
    10
    >>> next_larger_coin(10)
    25
    >>> next_larger_coin(2) # Other values return None
    """
    if coin == 1:
        return 5
    elif coin == 5:
        return 10
    elif coin == 10:
        return 25

def next_smaller_coin(coin):
    """Returns the next smaller coin in order.
    >>> next_smaller_coin(25)
    10
    >>> next_smaller_coin(10)
    5
    >>> next_smaller_coin(5)
    1
    >>> next_smaller_coin(2) # Other values return None
    """
    if coin == 25:
        return 10
    elif coin == 10:
        return 5
    elif coin == 5:
        return 1

def count_coins(total):
    """Return the number of ways to make change using coins of value of 1, 5, 10, 25.
    >>> count_coins(15)
    6
    >>> count_coins(10)
    4
    >>> count_coins(20)
    9
    >>> count_coins(100) # How many ways to make change for a dollar?
    242
    >>> count_coins(200)
    1463
    >>> from construct_check import check
    >>> # ban iteration
    >>> check(HW_SOURCE_FILE, 'count_coins', ['While', 'For'])
    True
    """
    "*** YOUR CODE HERE ***"

题目分析

此题与partition问题类似,但是将对某个金额进行划分的单位设置为了不同面额的coin,从而得到可划分的方法数量。题目中给出了两个获取下一顺序coin的面值的函数(从小到大或从大到小),从中选取一个使用。

在这里采用和partition一样的思路,建立一个helper函数,输入参数为当前划分金额total和可以使用的coin的最大面值m,对一个total进行划分时考虑其金额是否大于当前最大面值m,若大于,则可以return helper(total - m, m) + helper(total, next_smaller_coin(m)),也就是用当前最大面值m对当前金额进行划分的方法数量等于分出一个m后用剩下的total-m金额继续以最大面值为m的coin进行划分的方法数量加上当前total用比m小的最大面值来划分的方法数量(这样划分的结果中不会有m面值的coin)。另外,如果当前total与m大小相等,那么可以用一个m面值的coin划分整个total,也就相当于比用小于m的面值的coin来划分的方法数量多了1,即return 1 + helper(total, next_smaller_coin(m))。

basecase为next_smaller_coin(m)返回None的情况,此时表明已经只能使用最小的面值的coin来划分了(也就是面值为1),此时划分方法只有一种,故return 1。

代码实现

def count_coins(total):
    """Return the number of ways to make change using coins of value of 1, 5, 10, 25."""
    def helper(total, m):
        if next_smaller_coin(m) == None:
            return 1
        if total > m:
            return helper(total - m, m) + helper(total, next_smaller_coin(m))
        elif total == m:
            return 1 + helper(total, next_smaller_coin(m))
        else:
            return helper(total, next_smaller_coin(m))
    return helper(total, 25)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值