zoukankan      html  css  js  c++  java
  • 编程之美——买书问题:贪心算法

    1 问题描述及分析
           买书折扣问题的描述是,某出版社的《哈里波特》系列共有5卷,每本单卖都是8块钱,如果读者一次购买不同的k(k>=2)卷,就可以享受不同的折扣优惠,如下所示:

    问题是如果给定一个订单,如何计算出最大的折扣数?
    书中给出的动态规划解法这里就不再赘述了。不过,里面有两个问题需要单独关注一下:
    (1)如果订单描述为X:(X1,X2,X3,X4,X5),其中X1,X2,X3,X4,X5为所订书的数量,其所在位置为卷的编号,即第一卷X1本,第二卷X2本,…,第5卷X5本,由于每本书的价格相同,所以折扣的多少仅仅在于如何选取而不在于究竟取那一卷书。我们将(X1,X2,X3,X4,X5)按照大小顺序重新排列成为一个新的序列Y:(Y1,Y2,Y3,Y4,Y5),其中Y1≥Y2≥Y3≥Y4≥Y5。为什么要这样排列呢?因为我们的关注点不在于第一卷书要买几本,而在于,如何将这些书组合起来,使得组合后的优惠更多。
       (2)下一步可选的书的种类多就能证明这个子问题比别的子问题更好?这点我不敢苟同,须知该问题的解决是一个多步选择的过程,所以要得到这个结论就需要严格证明。如果这个条件不成立,那么书中给出的递归式也就不成立,即不能证明优化子结构性质成立。
     
    作者计算了订单中书的数量在[1-10]区间内,各种不同的选取方法所能获得的最大折扣数:
    本数
    可能的分解本数
    对应的折扣
    对于2-5本,
    直接按折扣
    购买
    2
    3
    4
    5
    0.1
    0.3
    0.8
    1.25
    6
    =5+1
    =4+2
    =3+3
    =2+2+2
    1.25
    0.9
    0.6
    0.3
    7
    =5+2
    =4+3
    1.35
    1.1
    8
    =5+3
    =4+4
    =3+3+2
    =2+2+2+2
    5*25%+3*10%=1.55
    4*20%+4*20%=1.6
    0.7
    0.4
    9
    =5+4
    =5+2+2
    =4+3+2
    =3+3+3
    2.05
    1.45
    1.2
    0.9
    10
    =5+5
    =4+4+2
    =4+3+3
    =2+2+2+2+2
    2.5
    1.7
    1.4
    0.5
    表1-2 折扣计算表
        但是,这里出现了一个违反贪心规则的地方,当我们要买8本书的时候,我们的选择方式是5+3,其折扣 为5*0.25+3*0.10=1.55;而如果采用4+4的模式,则折扣数为2*4*0.20=1.6。
    作者采用了改进的贪心算法,将所有5+3的组合全部替换成4+4的组合。
    (由于书中的表述方式不清晰,甚至是错误,我将采用我自己的表达方法,例如:一组“4种不同卷”的组合,就表示这一组书由4本不同卷的书组成,<卷一,卷二,卷四,卷五>或者<卷一,卷二,卷三,卷五>)
     
    按照书中的例子,X=(2,2,1,1,3), 我们将其转换成Y的形式,则Y=(3,2,2,1,1)。
    按照贪心策略,我们可以取出一组“5种不同卷”的组合,即Y5=1组,这时,Y=(2,1,1,0,0);
    接下来,我们要取出“4种不同卷”的组合,发现条件不成立,不能取出一组不同的四卷书来,即Y4-Y5=1-1=0组;
    但是,我们可以取出1组“3种不同卷”的组合,即Y3-Y4=2-1=1组,这时,Y=(1,0,0,0,0);
    接下来,我们要取出“2种不同卷”的组合,发现条件不成立,不能取出一组不同的两卷书来,即Y2-Y3=2-2=0组;
    最后,我们还可以取出1组“1种不同卷”的组合,即Y1-Y2=3-2=1组。
    这样,我们对订单中的9本书做出了如上的分组。
    但是根据表1-2的反例,我们要做出一些调整,将所有的5+3组合换成4+4组合。“5种不同卷”有1组(Y5=1),“3种不同卷”有1组(Y3-Y4=2-1=1),因此K=min{Y5,Y3-Y4}=1。
    这时,我们对订单中的9本书所做出的分组就变成了:
    5种不同卷 Y5-K=1-1=0
    4种不同卷 Y4-Y5+2K=1-1+2=2
    3种不同卷 Y3-Y4-K=2-1-1=0
    2种不同卷 Y2-Y3=2-2=0
    1种不同卷 Y1-Y2=3-2=1

      上述方法对于10本之内的情况适用。但是对于十本以上的情况呢?

    作者想把多余10本的订单分成 若干个小于10的订单组,并把每组的最大折扣相加,以得到全局最优解,关于这一点我将在下面进行说明。
    2 贪心算法是否适用的分析
    贪心算法的适用有两个必要条 件,即优化子结构和贪心选择性。第一个性质由于已经证明可以适用动态规划算法,所以优化子结构性质显然成立(假如书中的动态规划递归式成立的话)。现需要 证明其贪心选择性,即如何“贪心”的进行选择。显然每次都查找最大的折扣数进行处理的贪心方法是行不通的,那么是贪心方法真的不行还是我们“贪”的不正确 呢?我们下面就来分析。
    贪心选择性的含义是,一个全 局最优解可以通过局部最优选择来达到,换句话说,当考虑作何选择时,我们只考虑对当前问题最佳的选择而不考虑子问题的结果。贪心算法所做的当前选择可能要 依赖于已经做出的所有选择,但不依赖于有待于做出的选择或子问题的解。所以,贪心策略通常是自顶向下地,一个一个地做出贪心选择,不断地将给定的问题归约 为更小的问题。当然,在此之前我们必须证明在每一步所做的贪心选择最终能产生一个全局最优解,这也正是本题的关键所在。
    书中解法二给出的分组的思想是可以借鉴的,但是显然犯了方向性的错误。因为贪心算法的关键在于“选择”, 即从当前的状态来“贪心地”从多种子状态中选择一个“当前的”最优解进行下去,并由其贪心选择性而使最终的解刚好是最优解。既然书中最后采用的还是(经修 改后的)贪心算法,就不应该把整体的问题分成若干组来执行。这是因为贪心算法的上一次选择与下一次选择之间是带有连续性的,并不能够将它们拆开处理;而如 果要拆开处理之后再将结果相加,就还需要证明拆分是使得结果最优的拆分。所以我们的目标最终还是应该定为寻找如何“贪”!其实作者的意图是对的,但实际目 标应该定为限制本次选择对于此后选择的影响,或使本次选择的影响仅限于下一次选择,这也是表格1-1只需要计算到10的原因(两次选择最多选择10本书),但由于下一次选择的影响可能会影响下下次的选择,所以我们不能硬性将这些选择拆开然后再相加,而只能一次一次地完成选择。
     
    部分内容分析来自:
    http://www.kuqin.com/algorithm/20080417/6890.html
  • 相关阅读:
    使用ClassLoader加载配置文件
    Io流和Properties集合的联合应用
    文件拷贝案例
    倒计时
    静态代码块
    数组的四种排序(冒泡排序,选择排序,插入排序,快速排序)
    通过map集合统计每个字符出现的次数
    随机输入几个数字,删除重复数字(但要保留一个),留下不重复的数字
    流程图学习-1-基础符号
    Java-List的对象的校验不起作用的解决方案
  • 原文地址:https://www.cnblogs.com/z-j-n-2015/p/5072417.html
Copyright © 2011-2022 走看看