递归实现';最小硬币数量';用python
此问题与中的问题相同 给出一个硬币列表,它们的值(c1,c2,c3,…cj,…)和总金额i。找到最小数量的硬币,其总和为i(我们可以使用任意数量的硬币),或者报告不可能选择总和为s的硬币 我昨天刚被介绍了动态规划,我试着为它编写一个代码递归实现';最小硬币数量';用python,python,algorithm,dynamic-programming,Python,Algorithm,Dynamic Programming,此问题与中的问题相同 给出一个硬币列表,它们的值(c1,c2,c3,…cj,…)和总金额i。找到最小数量的硬币,其总和为i(我们可以使用任意数量的硬币),或者报告不可能选择总和为s的硬币 我昨天刚被介绍了动态规划,我试着为它编写一个代码 # Optimal substructure: C[i] = 1 + min_j(C[i-cj]) cdict = {} def C(i, coins): if i <= 0: return 0 if i in cdic
# Optimal substructure: C[i] = 1 + min_j(C[i-cj])
cdict = {}
def C(i, coins):
if i <= 0:
return 0
if i in cdict:
return cdict[i]
else:
answer = 1 + min([C(i - cj, coins) for cj in coins])
cdict[i] = answer
return answer
最优子结构:C[i]=1+min_j(C[i-cj])
cdict={}
def C(一、硬币):
如果我如注释所述,则在
i<0
时需要返回足够大的值,这样它就不会被min
选择,如下所示:
cdict = {}
def C(i, coins):
if i == 0:
return 0
if i < 0:
return 1e100 # Return infinity in ideally
if i in cdict:
return cdict[i]
else:
answer = 1 + min([C(i - cj, coins) for cj in coins])
cdict[i] = answer
return answer
使用方法:
python2 coins.py <amount> <coin1> <coin2> ...
python2 coins.py。。。
这是一个很好的算法问题,但老实说,我不认为您的实现是正确的,或者可能是我不理解您函数的输入/输出,为此我道歉。这是您的实现的修改版本。
def C(i, coins, cdict = None):
if cdict == None:
cdict = {}
if i <= 0:
cdict[i] = 0
return cdict[i]
elif i in cdict:
return cdict[i]
elif i in coins:
cdict[i] = 1
return cdict[i]
else:
min = 0
for cj in coins:
result = C(i - cj, coins)
if result != 0:
if min == 0 or (result + 1) < min:
min = 1 + result
cdict[i] = min
return cdict[i]
假设这些测试不够健壮,您也可以这样做
import random
random_sum = random.randint(10**3, 10**4)
result = get_min_coin_configuration(sum = random_sum, coins = random.sample(range(10**3), 200))
assert sum(result) == random_sum
有可能没有这样的硬币组合等于我们的随机总和,但我相信这不太可能
我确信有更好的实现,我试图强调可读性而不是性能。
祝你好运
更新
以前的代码有一个小错误,它假设检查最小硬币而不是最大硬币,重新编写符合pep8的算法,并在找不到组合时返回[]
,而不是None
def get_min_coin_configuration(total_sum, coins, cache=None): # shadowing python built-ins is frowned upon.
# assert(all(c > 0 for c in coins)) Assuming all coins are > 0
if cache is None: # initialize cache.
cache = {}
if total_sum in cache: # check cache, for previously discovered solution.
return cache[total_sum]
elif total_sum in coins: # check if total_sum is one of the coins.
cache[total_sum] = [total_sum]
return [total_sum]
elif min(coins) > total_sum: # check feasibility, if min(coins) > total_sum
cache[total_sum] = [] # no combination of coins will yield solution as per our assumption (all +).
return []
else:
min_configuration = [] # default solution if none found.
for coin in coins: # iterate over all coins, check which one will yield the smallest combination.
results = get_min_coin_configuration(total_sum - coin, coins, cache=cache) # recursively search.
if results and (not min_configuration or (1 + len(results)) < len(min_configuration)): # check if better.
min_configuration = [coin] + results
cache[total_sum] = min_configuration # save this solution, for future calculations.
return cache[total_sum]
assert all([ get_min_coin_configuration(**test[0]) == test[1] for test in
[({'total_sum':25, 'coins':[1, 5, 10]}, [5, 10, 10]),
({'total_sum':153, 'coins':[1, 5, 10, 50]}, [1, 1, 1, 50, 50, 50]),
({'total_sum':100, 'coins':[1, 5, 10, 25]}, [25, 25, 25, 25]),
({'total_sum':123, 'coins':[5, 10, 25]}, []),
({'total_sum':100, 'coins':[1,5,25,100]}, [100])] ])
def get_min_coin_配置(total_sum,coins,cache=None):#不赞成隐藏python内置。
#断言(所有(对于硬币中的c,c>0))假设所有硬币都>0
如果缓存为无:#初始化缓存。
缓存={}
如果缓存中的总和:#检查缓存,查找以前发现的解决方案。
返回缓存[总计]
elif total_sum in coins:#检查total_sum是否为硬币之一。
缓存[总和]=[总和]
返回[总计]
elif min(硬币)>总计:#检查可行性,如果min(硬币)>总计
cache[total_sum]=[]#根据我们的假设,任何硬币组合都不会产生解决方案(全部+)。
返回[]
其他:
min_configuration=[]如果未找到默认解决方案,则为默认解决方案。
对于硬币中的硬币:#迭代所有硬币,检查哪一个会产生最小的组合。
结果=获取\u最小\u硬币\u配置(总计\u总和-硬币,硬币,缓存=缓存)#递归搜索。
如果结果和(不是最小配置或(1+len(结果))
这里是一个递归的、效率非常低的更改算法实现,其中V
是一个硬币列表和C
目标金额:
def min_change(V, C):
def min_coins(i, aC):
if aC == 0:
return 0
elif i == -1 or aC < 0:
return float('inf')
else:
return min(min_coins(i-1, aC), 1 + min_coins(i, aC-V[i]))
return min_coins(len(V)-1, C)
这里有一个有趣的方法。有点小技巧,但这就是为什么它很有趣
import math
def find_change(coins, value):
coins = sorted(coins, reverse=True)
coin_dict = {}
for c in coins:
if value % c == 0:
coin_dict[c] = value / c
return coin_dict
else:
coin_dict[c] = math.trunc(value/ float(c))
value -= (c * coin_dict[c])
coins = [1, 5, 10, 25]
answer = find_change(coins, 69)
print answer
[OUT]: {25: 2, 10: 1, 5: 1, 1: 4}
下面是带边缘保护的注释的相同解决方案
import math
def find_change(coins, value):
'''
:param coins: List of the value of each coin [25, 10, 5, 1]
:param value: the value you want to find the change for ie; 69 cents
:return: a change dictionary where the key is the coin, and the value is how many times it is used in finding the minimum change
'''
change_dict = {} # CREATE OUR CHANGE DICT, THIS IS A DICT OF THE COINS WE ARE RETURNING, A COIN PURSE
coins = sorted(coins, reverse=True) # SORT COINS SO WE START LOOP WITH BIGGEST COIN VALUE
for c in coins:
for d in coins: # THIS LOOP WAS ADDED BY A SMART STUDENT: IE IN THE CASE OF IF THERE IS A 7cent COIN AND YOU ARE LOOKING FOR CHANGE FOR 14 CENTS, WITHOUT THIS D FOR LOOP IT WILL RETURN 10: 1, 1: 4
if (d != 1) & (value % d == 0):
change_dict[d] = value / d
return change_dict
if value % c == 0: # IF THE VALUE DIVIDED BY THE COIN HAS NO REMAINDER, # ie, if there is no remainder, all the neccessary change has been given # PLACE THE NUMBER OF TIMES THAT COIN IS USED IN THE change_dict # YOU ARE FINISHED NOW RETURN THE change_dict
change_dict[c] = value / c
return change_dict
else:
change_dict[c] = math.trunc(value/ float(c)) # PLACE THAT NUMBER INTO OUR coin_dict # DIVIDE THE VALUE BY THE COIN, THEN GET JUST THE WHOLE NUMBER # IE 69 / 25.0 = 2.76 # math.trunc(2.76) == 2 # AND THAT IS HOW MANY TIMES IT WILL EVENLY GO INTO THE VALUE,
amount = (c * change_dict[c]) # NOW TAKE THE NUMBER OF COINS YOU HAVE IN YOUR UPDATE THE VALUE BY SUBTRACTING THE c * TIME NUMBER OF TIMES IT WAS USED # AMOUNT IS HOW MUCH CHANGE HAS BEEN PUT INTO THE CHANGE DICT ON THIS LOOP # FOR THE CASE OF 69, YOU GIVE 2 25CENT COINS, SO 2 * 25 = 50, 19 = 69 - 50
value = value - amount # NOW, UPDATE YOUR VALUE, SO THE NEXT TIME IT GOES INTO THIS LOOP, IT WILL BE LOOKING FOR THE MIN CHANGE FOR 19 CENTS...
coins = [1, 5, 10, 25]
answer = find_change(coins, 69)
print answer
[OUT]: {25: 2, 10: 1, 5: 1, 1: 4}
edge_case_coins = [1, 7, 10, 25]
edge_case_answer = find_change(coins, 14)
print edge_case_answer
[OUT]: {7: 2}
这是一个使用while循环的方法。算法非常简单。你首先使用最大的硬币来支付这笔钱。如果你知道你会选择下一个较小的硬币,然后重复,直到钱为0。这段代码的优点是,虽然最坏情况下的运行时间较高(我认为是m*n)(m是列表的大小,n是while中的迭代次数),代码要简单得多 我假设没有价值为0的硬币,并且总是有价值为1的硬币。当没有价值为1时,函数将给出价格下最佳硬币数量的答案
def find_change(coins, money):
coins = sorted(coins, reverse=True)
coincount = 0
for coin in coins:
while money >= coin:
money = money - coin
coincount += 1
return coincount
我试着想一个这样做行不通的极端情况(它会溢出任何具有0值硬币的列表)但是我想不出一个。如果我<0,你真的想返回0吗?如果你让你的程序从标准美国硬币中赚20美分,你的程序将返回1,因为20-25=-5,所以在递归调用中,程序将返回0,所以你所有递归调用的最小值都将是1,这似乎不正确(除非我误解了这个问题)。此外,cdict是某种回忆录吗?是的,cdict将记住以前的解决方案。例如,cdict[10]将包含10笔钱所需的最少数量的硬币。I“我意识到,如果我在某个点上
无
如果找不到组合例如,如果我给你一枚一角硬币和一枚五分硬币,请你找到可以产生13的硬币组合,但除了5模的数字之外,没有任何镍和一角硬币的组合可以产生任何东西…无论如何现在重写算法,如果找不到匹配项并修复了错误,它将返回[]
,尽管我希望我的测试能够找到它,尽管错误可能对性能的影响比任何东西都大……当我通过count\u change调用它时
def min_change(V, C):
def min_coins(i, aC):
if aC == 0:
return 0
elif i == -1 or aC < 0:
return float('inf')
else:
return min(min_coins(i-1, aC), 1 + min_coins(i, aC-V[i]))
return min_coins(len(V)-1, C)
def min_change(V, C):
m, n = len(V)+1, C+1
table = [[0] * n for x in xrange(m)]
for j in xrange(1, n):
table[0][j] = float('inf')
for i in xrange(1, m):
for j in xrange(1, n):
aC = table[i][j - V[i-1]] if j - V[i-1] >= 0 else float('inf')
table[i][j] = min(table[i-1][j], 1 + aC)
return table[m-1][n-1]
import math
def find_change(coins, value):
coins = sorted(coins, reverse=True)
coin_dict = {}
for c in coins:
if value % c == 0:
coin_dict[c] = value / c
return coin_dict
else:
coin_dict[c] = math.trunc(value/ float(c))
value -= (c * coin_dict[c])
coins = [1, 5, 10, 25]
answer = find_change(coins, 69)
print answer
[OUT]: {25: 2, 10: 1, 5: 1, 1: 4}
import math
def find_change(coins, value):
'''
:param coins: List of the value of each coin [25, 10, 5, 1]
:param value: the value you want to find the change for ie; 69 cents
:return: a change dictionary where the key is the coin, and the value is how many times it is used in finding the minimum change
'''
change_dict = {} # CREATE OUR CHANGE DICT, THIS IS A DICT OF THE COINS WE ARE RETURNING, A COIN PURSE
coins = sorted(coins, reverse=True) # SORT COINS SO WE START LOOP WITH BIGGEST COIN VALUE
for c in coins:
for d in coins: # THIS LOOP WAS ADDED BY A SMART STUDENT: IE IN THE CASE OF IF THERE IS A 7cent COIN AND YOU ARE LOOKING FOR CHANGE FOR 14 CENTS, WITHOUT THIS D FOR LOOP IT WILL RETURN 10: 1, 1: 4
if (d != 1) & (value % d == 0):
change_dict[d] = value / d
return change_dict
if value % c == 0: # IF THE VALUE DIVIDED BY THE COIN HAS NO REMAINDER, # ie, if there is no remainder, all the neccessary change has been given # PLACE THE NUMBER OF TIMES THAT COIN IS USED IN THE change_dict # YOU ARE FINISHED NOW RETURN THE change_dict
change_dict[c] = value / c
return change_dict
else:
change_dict[c] = math.trunc(value/ float(c)) # PLACE THAT NUMBER INTO OUR coin_dict # DIVIDE THE VALUE BY THE COIN, THEN GET JUST THE WHOLE NUMBER # IE 69 / 25.0 = 2.76 # math.trunc(2.76) == 2 # AND THAT IS HOW MANY TIMES IT WILL EVENLY GO INTO THE VALUE,
amount = (c * change_dict[c]) # NOW TAKE THE NUMBER OF COINS YOU HAVE IN YOUR UPDATE THE VALUE BY SUBTRACTING THE c * TIME NUMBER OF TIMES IT WAS USED # AMOUNT IS HOW MUCH CHANGE HAS BEEN PUT INTO THE CHANGE DICT ON THIS LOOP # FOR THE CASE OF 69, YOU GIVE 2 25CENT COINS, SO 2 * 25 = 50, 19 = 69 - 50
value = value - amount # NOW, UPDATE YOUR VALUE, SO THE NEXT TIME IT GOES INTO THIS LOOP, IT WILL BE LOOKING FOR THE MIN CHANGE FOR 19 CENTS...
coins = [1, 5, 10, 25]
answer = find_change(coins, 69)
print answer
[OUT]: {25: 2, 10: 1, 5: 1, 1: 4}
edge_case_coins = [1, 7, 10, 25]
edge_case_answer = find_change(coins, 14)
print edge_case_answer
[OUT]: {7: 2}
def find_change(coins, money):
coins = sorted(coins, reverse=True)
coincount = 0
for coin in coins:
while money >= coin:
money = money - coin
coincount += 1
return coincount