问题描述
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.
- 6 = 2 + 4
- 6 = 1 + 1 + 4
- 6 = 3 + 3
- 6 = 1 + 2 + 3
- 6 = 1 + 1 + 1 + 3
- 6 = 2 + 2 + 2
- 6 = 1 + 1 + 2 + 2
- 6 = 1 + 1 + 1 + 1 + 2
- 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)
is10
.next_smaller_coin
will return the next smaller coin denomination from the input, i.e.next_smaller_coin(5)
is1
.- 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)