四舍五入计算零钱的最小数量问题(python)

时间:2019-06-15 19:50:05

标签: python-3.x

我有一个家庭作业,其中我必须编写一个程序,以使用最少的硬币数量输出自动售货机要提供的找零。例如。可将£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

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)。对贪婪方法感到满意后,请研究并尝试使用非贪婪方法解决问题。