python算法之贪心算法(硬币找零问题)

硬币找零问题

贪心算法

贪心算法遵循某种既定原则,不断地选取当前条件下最优的选择来构造每一个子步骤,直到获得问题最终的求解。即在求解时,总是做出当前看来最好的选择。也就是说,不从整体最优上加以考虑,他所做出的仅是局部最优解。

利用贪心算法解题,需要解决以下两个问题。

  1. 是问题是否适合用贪心法求解,即所求解问题是否具有贪心选择性质。所谓贪心选择性质,是指应用同一规则,将原问题变为一个相似的但规模更小的子问题,后面的每一步都是当前看似最佳的选择。这种选择依赖于已做出的选择,但不依赖于未做出的选择。从全局来看,运用贪心策略解决的问题在程序的运行过程中无回溯过程。
  2. 是问题是否具有局部最优解,从而通过选择一个贪心标准,可以得到问题的最优解。
    利用贪心算法解题的思路一般为:
    1>建立对问题精确描述的数学模型,包括定义最优解的模型。
    2>将问题分成一系列子问题,同时定义子问题的最优解结构。
    3>应用贪心算法原则可以确定每个子问题的局部最优解,并根据最优解模型,用子问题的局部最优解堆叠出全局最优解。

硬币找零问题

小明去商店里买棒棒糖,她怎么样才能用最少个数的硬币买到心仪的糖果呢?

问题描述

现在市面上有6中不同面值的硬币,各硬币的面值分别为5分、1角、2角、5角、1元、2元。

假定商店里各面值的硬币数量无限,小明想买一只标价为0.55元的棒棒糖。

解题:

展示一种情况:用面值为2角、1角和5分的共四枚硬币来付款。

另一种情况:小明用1元硬币支付,商店用2角和5分的硬币找零,同样需要4枚硬币。付1元,找:0.2+0.2+0.05.

最佳情况:小明用1元和5分的硬币支付,商店用5角的硬币找零,整个过程只需要三枚硬币。

现在问题来了,对于给定的各种面值的硬币个数和付款金额,如何计算使用硬币个数最少的交易方案。

该问题是对上述场景的简化,即仅考虑商店方,确定了找等金额之后,要用最少的硬币个数进行找零,可以抽象成如下模型:

  • 寻找找零所用最少的硬币数量,使所有硬币面值之和为待找零金额。

在求解上述问题时,每次选择当前阶段的支付价值时,总是优先地(贪心地)选择小于待支付金额的最大面值硬币进行支付。举例来说,假如现在有面值2元、1元、5角的硬币,要找等6.5元,则优先选择2元的硬币3枚支付6元,剩下的0.5元可以选择5角的硬币进行支付,共需要3枚硬币。

在这里,为了满足我们要用最少的硬币数量支付指定额度的金额这一要求,每次使用可选的最大金额付款符合一贯“贪心”的习惯。根据常识,在当前阶段,使用可用的最大面值金额支付剩余待找零额度,可以使后面的待找零额度尽量小,从而更有可能促使之后支付需要的硬币数量尽量少。

以下我们来做一些验证,确认上述贪心策略满足贪心选择性质,同时具有最优子结构性质。

贪心选择性质:如果贪心策略做出的选择所获得的结果与其他选择所获得的结果相比,具有更优或同质的性质,说明该贪心策略具有贪心选择性,即我们可以通过确定的贪心策略寻找局部最优解来构造整体最优解。

我们先通过一个简单的例子来观察:
ParValue[]={2,1,0,4.0.2}列出了可用的硬币面值,这里,为了使每次选择最大面值的贪心策略可以获得全局最优解,需要规定各面值硬币对应的出现次数最多为{n,1,2,1}。因为面值为1的硬币出现次数为2时,可以用1个面值为2的硬币来代替,硬币使用数就少了,那么就可以获得更优解,所以面值为1的硬币最多出现次数为1。面值为0.4的硬币只能选用2个,若选用了3个,可以用1枚面值为1和一枚面值为0.2的硬币来代替。概括来讲,就是说如果ParValue[1]超过一定数量,可以用更少的ParValue[i-1]来代替,因此,我们优先选择大的面值的话,就会用更少的硬币数,因此满足贪心选择性质。

从另一方面看,对于组合Parvalue[]={2,1,0.7,0.2}而言,则不满足贪心选择性质,比如要找零的金额是1.4元时,若按照贪心选择策略,得到的支付组合是1元硬币1枚、0.2元硬币2枚共3枚硬币,而通过观察我们知道,存在更优解,即使用0.7元硬币2枚即可完成支付。

综上,对于可用面值从大到小排列的支付组合ParValue[],只有当ParValue[i]>=2* ParValue[i+1]时,才满足贪心性质,否则,若ParValue[i] < 2ParValue[i+1],对于待找零值在(ParValue[i], ParValue[i+1])时,不满足贪心选择性质。

这里回归到问题的序列C[],容易看出, C[j]总可以用更大的C[i-1]或者配合一些小币值来代替,但是数量<-C[i]的使用数量,因此满足贪心选择性质。当然在这个过程中要考虑消费者硬币是否足够的问题。

最优子结构性质:用反证法证明。设Cm为待找零价格为P时的最优解,即最少使用Cm个硬币。当前价格可拆分为用最大面值支付的部分aParValue m与其他部分Pm-1之和,即P=aParValue m+ Pm-i, a为当前最大面值硬币使用数量, ParValue m为当前最大面值硬币面值, Pm-1为其他硬币面值之和,且有Cm=a+Cm-1, Cm-1为满足剩余价格需要支付的硬币数量。若存在Cm-1.’,满足Cm-1.’

最终代码

par = [0.05,0.1,0.2,0.5,1.0,2.0] #存储每种硬币,从小到大排列
sum = float(input("请输入需要找的零钱:")) 
#从面值最大的开始遍历
i = len(par) -1
while i >= 0:
    if sum >= par[i]:
        n = int(sum // par[i])
        change = n * par[i]
        sum = float("%.6f" %(sum - change))
        print("用了%d个%1.2f元硬币" % (n,par[i]))
    i -= 1

你可能感兴趣的:(python算法,算法,python,贪心算法,数据结构,深度学习)