python算法学习(后续更新)

本文介绍了贪心算法的基本概念,以硬币找零问题为例,展示了如何利用贪心策略选择面值较大的硬币来减少所需硬币数。文章还探讨了贪心算法的适用条件,以及如何通过证明最优子结构性质确保算法的有效性。最后给出了一个实际的Python代码实现.
摘要由CSDN通过智能技术生成

python算法学习

1.贪心算法

主要思想:不断选取当前条件下最好的选择来构造子步骤,不从整体上考虑最优,只从当前局部最优

利用贪心算法需要解决两个问题:

  1. 是否具有贪心选择性质:根据同一规则f,将原问题变成一个相似的但规模更小的子问题,后面的每一步都是当前最佳的选择。
  2. 是否具有局部最优解:通过一个贪心标准可以得到问题的最优解。

解决贪心问题的一般思路:

  • 建立对问题精确描述的数学模型,包括定义最优解的模型。
  • 将问题分成一系列子问题,同时定义子问题的最优解结构。
  • 应用贪心算法原则可以确定每个子问题的局部最优解,并根据最优解模型,用子问题 的局部最优解堆叠出全局最优解。

1.1硬币找零问题

问题描述:

有6种不同面值的硬币,各硬币的面值分别为5分、1角、2角、5角、1 元和2元。现要用这些面值的硬币来购物和找钱。购物时规定了可以使用的各种面值的硬币个 数。对于给定的各种面值的硬币个数和付款金额,如何计算使用硬币个数最少的交易方案。(硬币数等于付钱硬币数加找零硬币数)

核心思想为消费者硬币数量有限,商店的硬币无限。因此问题可以用以下公式来描述:

  • MIN(消费者支付硬币数量+商店找零硬币数量)
  • 支付值-找零值=商品值

这里所用的贪心算法为:

MAX(消费者拥有的硬币面值-商店拥有的硬币面值)优先使用。 即消费者优先使用面值最大的,商店优先使用面值最小的

为什么有效?

接下来验证这个算法的贪心选择性和最优子结构性质,证明贪心算法可以获得最优解:

贪心选择性:用贪心策略的选择替代原来的选择,如果能获得更优解或同优解,那么贪心 策略就可以获得最优解。

c[]={10,5,2,1} 在硬币选择问题中,如果需要表示一张六分的面值,可以选用三个两分的,也可以用一张更大的面值5加一个一分的来代替,这种优先使用大面值的方法可以使用更少的硬币,所以说满足贪心选择性质

疑问?

那如果说表示6分的,按照贪心选择的策略应该选择更大的10分再减去两个两分,这样不是比选择一个五分加上一个一分用到的硬币更多了吗

解答:

您提出的情况是贪心算法中的一个特例,其中贪心策略并不总是直接选择最大的硬币,而是选择能够使得找零中硬币总数最少的方式。在这种情况下,选择一个5分硬币和一个1分硬币来凑成6分,确实比选择一个10分硬币再找零两个2分硬币更优,因为前者只需要两枚硬币。
这个例子说明,贪心算法的有效性取决于问题的具体情况和贪心选择的定义。在找零问题中,贪心选择的策略是每一步都选择当前最大的可用硬币,但这并不意味着总是选择最大的硬币。实际上,贪心选择的策略是在每一步都做出一个局部最优的选择,以期望这个选择能导致全局最优解。
对于找零问题,贪心算法的正确策略是选择当前最大的硬币,但如果这个选择导致后续找零时需要更多的硬币,那么这个选择就不再是贪心的。因此,在实际应用贪心算法时,我们需要考虑当前选择对后续步骤的影响。
在您给出的例子中,选择10分硬币然后找零两个2分硬币,实际上可能会导致后续找零时需要更多的硬币。因此,贪心算法在这里会选择更小的硬币组合,即一个5分硬币和一个1分硬币,因为这样可以减少总的硬币数量。
总之,贪心算法在找零问题中的应用是选择每一步的最优解,但这个最优解可能是基于当前情况的最优,而不是单纯选择最大的硬币。在实际编程实现时,我们需要根据问题的具体规则来设计贪心策略,以确保最终得到全局最优解。

最优子结构证明

  1. 定义问题
    • S[i] 表示凑出金额 i 所需的最小硬币数。
    • c[j] 是一个特定面值的硬币。
  2. 减少一个硬币
    • 假设我们已经知道了凑出金额 i 所需的最优硬币数 S[i],并且这个解中包括了至少一个面值为 c[j] 的硬币。
    • 现在我们尝试去掉一个 c[j] 硬币,这将改变问题为用最少的硬币凑出金额 i - c[j]
    • 我们设 S[j] 是凑出金额 j 的最优解。如果从 S[j] 减去一个 c[j] 硬币,问题变为求解 n - c[j] 的最优解,硬币数变为 k-1
  3. 证明最优子结构
    • 如果我们假设存在另一个解 T[i],使得 T[i] 是金额 n - c[j] 的最优解,而且所用的硬币数 m 小于 k-1
    • 那么,如果我们在 T[i] 的基础上加上一个 c[j] 硬币,我们将得到一个用于金额 n 的解,总硬币数是 m + 1
    • 根据假设,这个解的硬币数 m + 1 小于 k(因为 m < k-1),这与 S[i] 是金额 n 的最少硬币数的假设矛盾,因为我们已经找到了一个更小的硬币数。
  4. 结论
    • 这个矛盾说明了我们的初步假设(存在一个比 S[i] 更好的解)是错误的。这意味着减去一个硬币后的问题(金额 n - c[j])的最优解确实是 S[i] 的子问题的最优解。
    • 因此,问题满足最优子结构性质,即整个问题的最优解可以通过其子问题的最优解来构造。

最终代码

d = [0.05,0.1,0.2,0.5,1,2]   # 存储每种硬币面值 
d_num = []       # 存储每种硬币的数量 
s = 0 
# 拥有的零钱总和 
temp = raw_input('请输入每种零钱的数量:') 
d_num0 = temp.split(" ") 
for i in range(0, len(d_num0)):   	   		   		d_num.append(int(d_num0[i])) 	
    s += d[i] * d_num[i]    # 计算出收银员拥有多少钱 
sum = float(raw_input("请输入需要找的零钱:")) 
if sum > s: 
# 当输入的总金额比收银员的总金额多时,无法进行找零 
    print("数据有错") 
    return 0 
s = s - sum 
# 要想用的硬币数量最少,那么需要利用面值大的硬币, 
# 因此从数组的面值大的元素开始遍历 
i = 5 
while i >= 0:  
    if sum >= d[i]: #优先选择最大的面值
        n = int(sum / d[i]) 
        if n >= d_num[i]: 
            n = d_num[i]    # 更新n 
   		sum -= n * d[i]    # 贪心的关键步骤,令sum动态改变 
   		print("用了%d个%f元硬币"%(n, d[i])) 
  	i -= 1 
  • 27
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值