Python 优化变革方案
我在这里编写了一个Python解决方案,它解决了以下问题:如何用最少数量的给定面额的硬币来制造给定数量的货币Python 优化变革方案,python,algorithm,optimization,Python,Algorithm,Optimization,我在这里编写了一个Python解决方案,它解决了以下问题:如何用最少数量的给定面额的硬币来制造给定数量的货币 def min_coin_change(n, d): mini = float("inf") for den in d: diff = n - den if diff == 0: ans = 1 elif diff < 0: ans = float("inf")
def min_coin_change(n, d):
mini = float("inf")
for den in d:
diff = n - den
if diff == 0:
ans = 1
elif diff < 0:
ans = float("inf")
else:
ans = 1 + min_coin_change(n - den, d)
mini = min(mini, ans)
return mini
def最小硬币更换(n,d):
迷你=浮动(“inf”)
对于d中的den:
diff=n-den
如果差异==0:
ans=1
elif差异<0:
ans=浮动(“inf”)
其他:
ans=1+最小硬币兑换(n-den,d)
最小值=最小值(最小值,ans)
返回迷你
虽然我的解决方案有效,但当
n
大于50或d
的长度大于5时,需要很长时间。我怎样才能加快代码的速度,使其能够在相当大的输入下工作?我是否错过了一个技巧或其他可以大大加快代码速度的东西?这是因为它是递归的
阅读此内容(以及前两个答案)
“记忆化”会记住你已经计算过的东西(比如10美分),这样你就不会以指数级的次数重新计算它们
可以按原样复制Mwmoize类,
就像第二个答案中解释的那样,只是“装饰”你的功能
懒人
class Memoize:
def __init__(self, f):
self.f = f
self.memo = {}
def __call__(self, *args):
if not args in self.memo:
self.memo[args] = self.f(*args)
return self.memo[args]
然后在定义之前添加decorator
@Memoize
def min_coin_change(n,d)......
函数的其余部分是相同的
那你叫它
min_coin_change(30,(25,10,5,1))
请注意,您应该将函数设置为从最大的硬币开始,循环查找最大可能数量的最大硬币,然后重复下一个最大的硬币 因此,den应按降序排序
def changecoin(d, denval)
count = 0
while d > denval:
d -= denval
count += 1
return count, d
现在使用下一个最低的denval值和新的d值调用递归,并适当地增加计数
newcount = 0
for denval in den:
count, d = changecoin(d, denval)
newcount += count
实际上,从代码上看,changecoin函数可以通过消除while来进行修复,这样就可以编写内联代码了
count = 0
for denval in den
count += d//denval
d = d % denval
这将返回计数和d中剩余的任何值。如果d小于denval,则计数增量为0,剩余的d不变。当d变为0时,for循环没有中断,但den中几乎没有足够的条目可以忽略测试
if d < 1:
break
如果d<1:
打破
我们能假设硬币的面额是合理的吗?这个问题的一般情况是允许出现奇怪的硬币面额,如[100,90,1]
,而简单的“贪婪”算法不会找到最优解(例如,对于180,最优解是两个90美分的硬币,而简单的贪婪算法会建议一个100美分的硬币和80美分的硬币)
如果我们可以假设合理的货币(就像任何实际货币一样),那么我建议你使用整数除法和模数的组合来计算每枚硬币的使用数量
如果我们不能假设合理的货币,那么这个问题是动态规划解决方案的理想选择。在动态规划中,您构建一个记录中间结果的表,与简单的递归解决方案相比,这节省了大量时间
斯基纳的书《Alogorithm设计手册》中对动态规划有一个很好的解释
以下是我在网上找到的一些链接:
这是一个更快的程序版本。我改变了两件事:
- 实现细节:所有递归程序都可以作为等价的迭代程序重新编写(在这种情况下,使用
和for
循环)。在大多数语言中,迭代版本更快,因为不需要维护堆栈while
- 算法:我使用的是贪婪算法,先从价值最大的硬币开始,然后尝试较小的硬币。正如其他答案所指出的,这并不能保证是最优的,但运行速度非常快(返回的解决方案中的硬币数呈线性)。请查看最佳(但较慢)动态规划解决方案
print min_coin_change2(101, [50, 20, 10])
Out[26]: 'I can get to 100 using [50, 50], but that is not what you asked for'
同样,当贪婪算法找到次优解时
print min_coin_change2(180, [100, 90, 20])
Out[2]: 'I can get to 180 using [100, 20, 20, 20, 20]'
但是最佳的解决方案应该是
[90,90]
,所以30美分(没有镍币)你就可以拿一个25美分和五个便士?@vish为什么不指定镍币?但是,如果这就是你所说的,那么这就是正确的答案。不,正确的答案是三角硬币。我在举一个反例。就是why@vish说得好。算法需要重做,然后再考虑它。也许可以为这种类型的case@vish当然,现实世界中的情况确实有5作为有效值。一个真实世界的例子是以色列,那里的货币是[1000,500,100,50,25,10,5,1]是25,10,1吗?并非所有真正的货币都具有这样的特征:任何硬币都可以被所有较小的面额所除。不,[25,10,1]
是不明智的。我对“明智”的定义是“简单贪婪算法是否有效”,如果你试图以30美分的价格进行优化,你需要使用3个10美分的硬币,而贪婪则建议使用1个25美分的硬币和5个便士。但我见过的真实货币往往有5美分,这将使这一点再次“合理”。我认为重要的不是哪些硬币可以被其他硬币分割,但是,是否有一个较小硬币的平滑连续统一体来保持贪婪的改变方式。你确实意识到,算法应该根据输入而不是相反的方式进行调整。aroundI只是添加了一个更简单的方法,用整数除法和模来代替循环。这使得它在主硬币循环中有两行,并且不需要任何递归。在Python 3.2和更新版本中,您可以使用functools.lru\u cache
,它是一个记忆装饰器。
print min_coin_change2(101, [50, 20, 10])
Out[26]: 'I can get to 100 using [50, 50], but that is not what you asked for'
print min_coin_change2(180, [100, 90, 20])
Out[2]: 'I can get to 180 using [100, 20, 20, 20, 20]'