1. 程式人生 > IOS開發 >【譯】貪心演演算法,你入門了嗎?

【譯】貪心演演算法,你入門了嗎?

貪心演演算法,你入門了嗎?

貪心演演算法在大多數情況下都易於實現,在求解最優問題時,也是最常用的編碼套路之一,而且它的資源消耗也比較低。

不過這個演演算法也有缺點,它不能保證每次都能找到最優解,有時候只能找到接近最優解的方案。不管怎樣,在很多情況下,接近最優解就足夠了。

這個演演算法一般是對規模為 “n” 的問題迭代 “n” 次,所以它的複雜度可能是 O(n),O(n × log(n)),但是不會超過 O(n²)。

這個演演算法能解決的大多數問題都有以下兩個特性:

  1. 貪心屬性:它的意思是每次迭代時都採用區域性最優解,而無需考慮對全域性的影響。我們相信通過不斷求解區域性最優解終會得到全域性最優解,但是正如我之前所說,這個結論不一定成立。為了證明在每次迭代中都求得了最優解,我們需要使用歸納法(顯然不是簡單的證明)。
  2. 最優子結構: 我之前提到過一些。求解的問題必須能劃分為子問題,每個子問題都有最優解。

本文中,我們將學習如何編寫自己的貪心演演算法,然後用這個演演算法解決一個非常著名的難題。

貪心演演算法通用模版 (Java)

public ArrayList greedy(ArrayList candidates)
{ solution; while (!isSolution(solution) && (candidatesLeft(candidates)) { cadidate = selectCandidate(candidates); removeCandidate(candidate,candidates); if (isGoodCandidate(candidate,solution)) { addCandidate(candidate,solution); } } if
(isSolution(solution)) { return solution; } else { return null; } } } 複製程式碼

在解釋程式碼之前,我先給出一些在虛擬碼中用到的術語的定義。

  1. Candidates: 所有可能的解集。它可以是任意的資料型別,但通常是可迭代的。在我們處理示例問題時,會加深對它的理解。現在請先記住結論 ?。
  2. Candidate: 在解集中,我們當前選中的一個解。
  3. Solution: 解變數的第一個例項只需是一種資料結構,在這裡我們將儲存當前的解。
  4. isSolution,candidatesLeft,removeCandidate,addCandidate,isGoodCandidate: 這些也是我們要建立的方法,其中的某些方法在一些實際問題中不必是完整的,但是為了總結程式碼模版,我把它們全部定義為方法。

首先,我們初始化解的資料結構,它可以是陣列,布林值,整數…… 我們只需要宣告一下。

solution
複製程式碼

然後,我們看一下這個 while 迴圈,它的迴圈條件中有兩個方法。這些方法必須編寫,但有時並不需要完整的方法體,例如,判斷是否有剩餘備選解的這個方法。

while (!isSolution(solution) && (candidatesLeft(candidates))
複製程式碼

當我們發現當前尚未找到解,並且有剩餘備選解可以嘗試時,我們將選擇一個備選解,並立即將其從我們的備選解集中刪除。

cadidate = selectCandidate(candidates);
removeCandidate(candidate,candidates);
複製程式碼

下一步很簡單。如果候選解是正確的解,則只需將其新增到解結構中。

if (isGoodCandidate(candidate,solution)) { 
    addCandidate(candidate,solution); 
}
複製程式碼

然後,我們只需檢查問題是否已達到解決的狀態,然後將解返回。

if (isSolution(solution)) { 
    return solution; 
} else { 
    return null; 
}
複製程式碼

至此我們已經看完了程式碼並對其進行了粗略的解釋,現在我給您出道題,請您嘗試自己解答。這是一個眾所周知的問題,在網上很容易就能搜到答案,但我建議您還是嘗試自己解決。


零錢兌換的問題

有6種硬幣,每種硬幣的值分別為 {50,20,10,5,2,1},它們按遞減排序作為引數傳遞。 每種硬幣都可能成為我們的候選解。您必須找到一種最佳的兌換方式。(用最少的硬幣找零

示例輸入: 15(我們必須以最少的硬幣數量湊齊 15 並返回硬幣集合

示例輸出: 10、5(我們返回了和為 15 的硬幣集合,且硬幣數量最少

在這個確定的硬幣系統 {50,20,10,5,2,1} 中,該演演算法能找到最優解,但是請注意,如果候選解發生改變,可能會導致該演演算法無法找到最優解。

提示

如果您沒有足夠努力嘗試,就不應該看這一段內容 ?…… 開個玩笑,繼續吧,我確信我希望您已經學到了一些新知識 ?。

  • selectCandidate() 方法中,首先選擇面額最大的硬幣,然後用較小的硬幣填充剩餘的零錢。在這個過程中,您要一直檢查是否超出剩餘的零錢。

解法

我提供的解法用 Java 編寫的,其中用到了面向物件的知識。

public class Coin {
    private int value;
    private int quantity;
    Moneda(int value,int quantity) {
        this.value = value;
        this.quantity = quantity;
    }
    /* getters & setters */
}

/* This is actually the "hard" part */
int selectCandidate(ArrayList < Integer > values) {
    int biggest = Integer.MIN_VALUE;
    for (Integer coin: values)
        if ((biggest < coin)) biggest = coin;
    return biggest;
}

/* Now the actual schema */

ArrayList < Coin > greedySchema(ArrayList < Integer > values,int quantity) {
    /* We initialize our solution structure */
    ArrayList < Coin > solution = new ArrayList < Coin > ();
    /* Any auxiliary variable is ok */
    int value;
    
    while ((quantity > 0) && (!values.isEmpty())) {
        /* Select and remove the coin from our monetary system */
        value = selectCandidate(values);
        values.remove(new Integer(value));
        
        /* If this is true,it meanwe can still look for coins to give */
        if ((quantity / value) > 0) {
            solution.add(new Coin(value,quantity / value));
            /* This will lower the quantity we need to give back */
            quantity = quanity % value;
        }
    }
    
    /* Once the quantity is 0 we are DONE! */
    if (quantity == 0) {
        return solution;
    } else return null;
}
複製程式碼

資源

如果您喜歡貪心演演算法的工作原理,並且想深入研究,請訪問 Hackerrank 或者 Hackerearth,這裡有有很多要解決的問題,我相信您已經對它們有一定了解 ?。

有時,我個人也會把 GitHub 作為搜尋引擎,並簡單地寫下我尋找的主題 [貪心演演算法]。

結論

綜上所述,即使對於簡單的個人專案,貪心演演算法也能表現優異,它不需要你花費太多時間去思考,並且只消耗很少的資源。而且,使用貪心演演算法可以輕鬆解決很多面試問題。大多數時候,使用貪心或動態規劃都可以滿足記憶體和複雜度方面的要求,但這就是另一個話題了 ?。

感謝您的閱讀,歡迎評論 ?。

如果發現譯文存在錯誤或其他需要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可獲得相應獎勵積分。文章開頭的 本文永久連結 即為本文在 GitHub 上的 MarkDown 連結。


掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄