贪婪算法
贪婪算法会在每个步骤中贪婪地选择最佳选择,并希望这些选择将引导我们找到问题的最佳解决方案。当然,贪婪算法并不总是为我们提供最佳解决方案,但在许多问题中却能提供最佳解决方案。例如,在“硬币找零”一章的硬币找零问题中,我们看到选择具有最大值的硬币并不能使我们获得最佳解决方案。但是,考虑硬币面额分别为1美分,5美分,10美分和20美分的情况。在这种情况下,如果我们在每一步中选择具有最大值的硬币,它将导致问题的最佳解决方案。
同样如前所述,分数背包也可以使用贪婪策略来解决,即通过选择最高 v一种升üËwË一世GHŤv一种升üËwË一世GHŤ 比率优先。
因此,检查贪婪算法是否将我们引向最优解是我们的下一个任务,它取决于以下两个属性:
- 最优子结构→如果子问题的最优解导致了问题的最优解,那么该问题就表现出最优子结构的性质。
- 贪婪选择属性→每个步骤的最优解都会导致全局最优解,此属性称为贪婪选择属性。
贪心算法的实现很容易,因为我们只需要在每个步骤中选择最佳选项,并且与其他算法(例如分治法)相比,它的分析也是如此,但是检查在每个步骤中做出贪婪的选择是否会导致最优选择在某些情况下,解决方案与否可能会很棘手。例如,让我们以面额为1¢,5¢,10¢和20¢的硬币找零问题为例。如前所述,这是一种特殊情况,我们可以使用贪婪算法而不是动态规划来获得最佳解决方案,但是我们如何检查或知道它是否正确?
假设我们必须更改数字 ññ使用这些硬币。我们可以写ññ 作为 5X+ÿ5X+ÿ,其中x和y是整数。这意味着我们可以将任何值写为5 +余数的倍数。例如,示例4可以写成5∗0+45∗0+4,7可以写成 5∗1个+2个5∗1个+2个, 等等。
现在,y的值将介于0到4之间(如果它大于或等于5,则它将覆盖在 5X5X 部分),我们可以检查是否只能通过使用所有值1的硬币来制作0到4之间的任何值。因此,我们知道该部分的最优解 ÿÿ将仅包含价值1的硬币。因此,我们将考虑最佳解决方案5X5X部分。由于问题表现出最优的子结构,因此对这两个子问题的最优解都将导致问题的最优解。
自从 5X5X是5的倍数,因此可以使用值5、10和20(因为所有三个都是5的倍数)。同样在5、10和20中,较高的值是较低的值的倍数。例如,20是5的倍数,而10是5的倍数。因此,我们可以用具有较高价值的硬币替换多次出现的较小硬币,从而可以减少硬币总数。例如,如果出现5次以上,则可以用10代替;如果发生10次以上,则可以用20代替。换句话说,我们可以先选择具有较高价值的硬币来减少总数硬币。
因此,我们刚刚检查了是否可以在这种情况下应用贪婪算法。
在本课程中,我们将看到更多的贪婪算法。因此,随着课程的进展,您将对贪婪算法更加满意。
让我们对上述硬币找零问题进行编码,并更加熟悉贪婪算法。
贪婪算法的硬币找零问题
首先让我们以相反的顺序(即)将硬币的值放在数组中coins = [20, 10, 5, 1]
。
现在,如果我们必须使用这些硬币使n值为零,那么我们将检查数组中的第一个元素(贪婪的选择),如果它大于n,则将移至下一个元素,否则取而代之。现在,取一枚有价硬币后coins[i]
,我们必须制造的总价值将变为n-coins [i]。
i = 0
while (n)
if coins[i] > n
i++
if coins [i] > n
→我们从第0个元素(具有最大值的元素)开始,检查是否可以使用此硬币。如果该硬币的价值大于要制造的价值,那么我们将移至下一个硬币- i++
。
如果硬币的价值不大于要制造的价值,那么我们可以拿走这个硬币。所以,我们会接受的。让我们仅在此处打印该值以表明我们已经接受了它,否则,我们也可以将这些值附加到数组中并返回它。
while (n)
...
else
print coins[i]
现在,将要产生的值减少了coins [i],即n-coins[i]
。
COIN-CHANGE-GREEDY(n)
硬币= [ 20,10,5,1 ]
i = 0
而(n)
如果硬币[i]> n
i ++
,则
打印硬币[i]
n = n硬币[i]
- C
- Python
- 爪哇
#include <stdio.h>
空隙 coin_change_greedy (INT Ñ ) {
INT 硬币[] = { 20 , 10 , 5 , 1 };
int i = 0 ;
而(n ) {
if (硬币[ i ] > n ) {
i ++ ;
}
else {
printf (“%d \ t ” ,硬币[ i ]);
ñ = Ñ -硬币[我];
}
}
printf (“ \ n ” );
}
int main () {
int i ;
对于(i = 1 ; i <= 20 ; i ++ ) {
coin_change_greedy (i );
}
返回 0 ;
}
算法分析
我们可以很容易地看到该算法所花费的时间不会超过线性时间。在while循环结束时,由于n由coins [i]减少,因此可以说,在大多数情况下,它花费的时间远远少于Ø(ñ)Ø(ñ) 时间。
因此,可以说我们的算法有一个 Ø(ñ)Ø(ñ) 运行时间。