我有一个家庭作业,其中我必须编写一个程序,以使用最少的硬币数量输出自动售货机要提供的找零。例如。可将£3.67分配为1x£2 + 1x£1 + 1x50p + 1x10p + 1x5p + 1x2p。
但是,我没有得到正确的答案,并怀疑这可能是由于舍入问题引起的。
change=float(input("Input change"))
twocount=0
onecount=0
halfcount=0
pttwocount=0
ptonecount=0
while change!=0:
if change-2>=0:
change=change-2
twocount+=1
else:
if change-1>=0:
change=change-1
onecount+=1
else:
if change-0.5>=0:
change=change-0.5
halfcount+=1
else:
if change-0.2>=0:
change=change-0.2
pttwocount+=1
else:
if change-0.1>=0:
change=change-0.1
ptonecount+=1
else:
break
print(twocount,onecount,halfcount,pttwocount,ptonecount)
结果:
Input: 2.3
Output: 10010
i.e. 2.2
Input: 3.4
Output: 11011
i.e. 3.3
一些实际可行的方法:
Input: 3.2
Output: 11010
i.e. 3.2
Input: 1.1
Output: 01001
i.e. 1.1
答案 0 :(得分:1)
您的方法是正确的,但是正如您所猜测的那样,舍入错误会带来麻烦。这可以通过简单地打印change
变量以及有关代码在循环的每次迭代中执行哪个分支的信息来调试:
initial value: 3.4
taking a 2... new value: 1.4
taking a 1... new value: 0.3999999999999999 <-- uh oh
taking a 0.2... new value: 0.1999999999999999
taking a 0.1... new value: 0.0999999999999999
1 1 0 1 1
如果希望保持浮点数用于输出和输入,请在输入途中乘以100(用int(round(change))
转换为整数),然后在函数出路时除以100,从而允许对整数进行运算。
此外,如果没有5p,2p和1p值,则会受到您可以处理的精度的限制,因此请不要忘记添加这些值。将所有代码乘以100可得出:
initial value: 340
taking a 200... new value: 140
taking a 100... new value: 40
taking a 20... new value: 20
taking a 20... new value: 0
1 1 0 2 0
除了十进制问题外,嵌套条件还使您的逻辑很难推理。这是常见的代码气味;您可以消除的分支越多越好。如果您发现自己超出了3个层次,请停下来考虑如何简化。
此外,由于具有许多分支和手工键入的代码,很可能会忽略细微的错误或错字,或者会忽略面额。
考虑使用字典和列表代替块,例如:
twocount=0
onecount=0
halfcount=0
pttwocount=0
ptonecount=0
可以优雅地表示为:
denominations = [200, 100, 50, 10, 5, 2, 1]
used = {x: 0 for x in denominations}
就效率而言,您可以使用数学运算法则来处理每种面额的金额。将剩余金额按降序除以每个可用面额,以确定将选择多少个硬币并相应地减去。对于每种面额,我们现在可以编写一个简单的循环并完全消除分支:
for val in denominations:
used[val] += amount // val
amount -= val * used[val]
并打印或显示used
的最终结果,例如:
278 => {200: 1, 100: 0, 50: 1, 10: 2, 5: 1, 2: 1, 1: 1}
最终的结果是我们将27条生产线减少到5条,同时提高了效率,可维护性和动态性。
顺便说一句,如果面额是不同的货币,则不能保证这种贪婪的方法会起作用。例如,如果我们的可用面额是25美分,20美分和1美分,而我们要找零63美分,则最佳解决方案是6个硬币(3x 20和3x 1)。但是贪婪算法会产生15(2x 25和13x 1)。对贪婪方法感到满意后,请研究并尝试使用非贪婪方法解决问题。