vlambda博客
学习文章列表

算法实现 | 贪心算法


关注“云计算通俗讲义

「云计算」「存储」「数据库」

背景

有1元、5元、10元、20元、100元、200元的钞票无穷多张。现使用这些钞票支付X元,最少需要多少张?

例如,X=628

我们应该尽可能使用面值较大的钞票,最佳支付方法:

3张200元的,1张20元的,1张5元的,3张1元的,共需要3+1+1+3=8张。

为何这么做一定是对的?

面额为1元、5元、10元、20元、100元、200元,任意面额是比自己小的面额的倍数关系。所以,当使用一张较大面额钞票时,若用较小面额钞票替换,一定需要更多的其他面额的钞票!

例如:

5=1+1+1+1+1

10=5+5

20=10+10

100=20+20+20+20+20

200=100+100

因此,当前最优解即为全局最优解,贪心算法成立。

概述

贪心算法(greedy algorithm,贪婪算法)是寻找最优解问题的常用方法。这种方法一般是将求解过程分成若干个步骤,在每个步骤都应用贪心原则,选取当前状态下最好或者最优的选择(局部最优的选择),并以此希望最后堆叠出的结果也是最好或最优的解。贪心法的每次决策都以当前情况为基础并根据某个最优原则进行选择,不从整体上考虑其他各种可能的情况。即贪心法是分阶段执行的,每一个阶段都根据当前的情况来判断,而不考虑后续的发展。

一般来说,这种算法选出的解是局部最佳(local best)解。该算法预设了这样一个前提,就是认为全局最优解可以由局部最优解所推出。

贪心法是遵循某种规律,不断贪心的选取当前最优策略的算法设计方法。即,贪心算法不追求最优解,只找到满意解。

贪心法VS分治法VS动态规划:

贪心法和分治法、动态规划一样,都需要对问题进行分解,定义最优的子结构。但是,贪心法与其他方法最大的区别在于,贪心法每一步选择完之后,局部最优解就确定了,不再进行回溯处理,也就是说,每一个步骤的局部最优解确定以后,就不再修改,直到算法结束。因为不进行回溯处理,贪心法只在很少情况下可以得到真正的最优解,比如最短路径问题、图的最小生成树问题。

条件

有待处理的问题必须满足下列两项条件,才能用贪婪算法求出最优的解:

1、具备贪心选择性质(greedy choice property)

2、具备最优子结构(optimal substructure)

贪心选择性质:该性质意味着全局最优解可以由局部最优解(也就是在贪心策略下所选出的解)所推出。贪婪算法在针对当前这一步做决定时,可以参考前面几步的决定,但是不会依赖后续的步骤。它总是会选出局部最优的解,并将原问题约简为更小的问题,然后在更小的问题上继续寻找其局部最优解,并继续约简。

最优子结构:如果某个问题的最优解可以由其各个子问题的最优解所构成,那么该问题就具备最优子结构。这意味着把子问题的解法拼合起来可以解决最初所要求解的那个大问题

但是,要判断一个问题是否具备上述两种性质,也就是说判断一个问题是否可以通过贪心算法得到最优解,是一件比较苦难的事情。这里需要比较复杂而严格的数学证明。因此,虽然贪心算法简单容易实现,并且效率很高,但是使用贪心算法之前必须对问题本身进行深入而透彻地分析和证明,以保证使用贪心算法得到最优解。

特点

优点

直观、易懂,实现简单。算法一旦做出决定,就不用回过头来去重新检查前面计算过的那些值。

缺点

并非所有问题都能那么解决,对于很多问题,在某个小范围内所做的最优决策,未必是整个问题的最优决策

应用场景

贪心算法不是对所有问题都能得到整体的最优解,但是实际应用中许多问题都可以使用贪心算法得到最优解。与此同时,即使使用贪心算法不能产生出问题的最优解,但最终结果也就是最优解的很好的近似解。因此在解决一般性问题时(并不一定要得到最优解),我们大可不必过分要求使用贪心算法一定要得到最优解,也没有必要进行严格地推理证明,使用贪心算法是一种不错的选择。

-- END --

本文由“135编辑器”提供技术支持

长按关注,看更多技术干货