玩轉算法「基礎篇」| 貪婪算法

2021-02-20 AlgorithmDeveloper

2.1  適用場景

貪婪算法常用於尋求最優解問題。將問題分成若干個求解步驟,在每個步驟中都依據某個最優原則選擇當前狀態的最優解,以此希望最後得到的結果也是最優的。貪婪算法在迭代完一輪之後就不再進行回溯,簡單高效,這種只關注當前狀態最優解的方法,通常可能會錯過全局最優解。

2.2  設計思想

(1)定義最優解模型:將問題抽象為數學模型;
(2)定義子問題最優解結構:將問題分解為若干個子問題;
(3)應用貪婪法則確定每個子問題的最優解,最後得到全局最優解。

2.3  0-1背包問題

總共有7件物品、一個最大承載重量為150的背包,其中每件物品的重量與價格分別為W[i]和P[i],W=[35,30,60,50,40,10,25],P=[10,40,30,50,35,40,30];選擇哪些物品裝入背包可以使背包中物品價格最高。

3.  C++實現


背包問題可以簡述為:
第1次迭代:背包載重為C,此時所有物品均可選擇,根據貪婪法則選擇一個物品裝入背包,假設選擇了物品i;
第2次迭代:背包載重為C-W[i],從所有可選物品中根據貪婪法則選擇一個物品裝入背包;
……
第N次迭代:背包不能再裝下物品或者所有物品已被選中,結束算法。

3.1  定義物品數據結構

物品包含三個屬性,重量、價格以及物品狀態,物品狀態0表示該物品未被選擇,1表示已被選擇,2表示在某次迭代中選擇該物品會超過背包允許最大載重,後續也不要再選。

typedef struct 
{
    int weight;
    int price; 
    int status;
}Object;

3.2  定義背包問題

typedef struct 
{
    std::vector<Object> objs; 
    int totalC;              
}Knapsack_problem;

3.3  貪婪法則

3.3.1  選擇價格最高的物品

int Choosefunc1(std::vector<Object>& objs)
{
    int index = -1;
    int maxPrice = 0;

    for (int i = 0; i < static_cast<int>(objs.size()); i++)
    {
        if ((objs[i].status == 0) && (objs[i].price > maxPrice))
        {
            maxPrice = objs[i].price;
            index = i;
        }
    }
    return index;
}

3.3.2  選擇重量最輕的物品

int Choosefunc2(std::vector<Object>& obj)
{
    int index = -1;
    int minWeight = 10000;

    for (int i = 0; i < static_cast<int>(obj.size()); i++)
    {
        if (obj[i].status == 0 && obj[i].weight < minWeight)
        {
            minWeight = obj[i].weight;
            index = i;
        }
    }
    return index;
}

3.3.3  選擇價格密度最高的物品

int Choosefunc3(std::vector<Object>& obj)
{
    int index = -1;
    double maxDes = 0.0;
    for (int i = 0; i < static_cast<int>(obj.size()); i++)
    {
        if (obj[i].status == 0)
        {
            double si = obj[i].price/obj[i].weight;
            if (si > maxDes)
            {
                maxDes = si;
                index = i;
            }
        }
    }
    return index;
}

3.4  輔助函數

列印最終所選物品的重量以及價格:

void PrintResult(std::vector<Object>& objs)
{
    int totalW = 0;
    int totalP = 0;
    for (int i = 0; i < static_cast<int>(objs.size()); i++)
    {
        if (objs[i].status == 1)
        {
            totalW += objs[i].weight;
            totalP += objs[i].price;
            cout<<"物品"<<i<<": 重量="<<objs[i].weight<<", 價格="<<objs[i].price<<endl;
        }
    }
    cout<<"所選物品總重:"<<totalW<<", 總價格:"<<totalP<<endl;
}
v

3.5  貪婪算法

typedef int (*Select_policy)(std::vector<Object>& objs);
void GreedyAlgorithm(Knapsack_problem* instance, Select_policy spFunc)
{
    int index;
    int mCurWeight = 0;

    while((index = spFunc(instance->objs)) != -1)
    {
        if (mCurWeight + instance->objs[index].weight <= instance->totalC)
        {
            instance->objs[index].status = 1;
            mCurWeight += instance->objs[index].weight;
        }
        else
        {
            instance->objs[index].status = 2;
        }
    }
    PrintResult(instance->objs);
}

3.6  算法測試

#include <stdlib.h>
#include <iostream>
#include <vector>
using namespace std;

Object objects[] = {{35,10,0},{30,40,0},{60,30,0},{50,50,0},{40,35,0},{10,40,0},{25,30,0}};

int main(int argc, char const *argv[])
{
    Knapsack_problem instance;
    instance.objs.assign(objects, objects+7);
    instance.totalC = 150;
    GreedyAlgorithm(&instance, Choosefunc1);
    return 0;
}

測試結果

物品1: 重量=30, 價格=40
物品3: 重量=50, 價格=50
物品4: 重量=40, 價格=35
物品5: 重量=10, 價格=40
所選物品總重:130, 總價格:165

4.  Python實現

class Knapsack_problem():
    
    
    def __init__(self, weight, price, totalC):
        self.weight = weight
        self.price = price
        self.number = len(weight)
        
        
        self.status = [0]*self.number
        self.totalC = totalC

    
    def PrintResult(self):
        totalW = 0
        totalP = 0
        for i in range(self.number):
            if self.status[i] == 1:
                totalW += self.weight[i]
                totalP += self.price[i]
                print('所選物品:%d, 重量:%d, 價格:%d' % (i, self.weight[i], self.price[i]))
        print('所選物品總重: %d, 總價值: %d' % (totalW, totalP))

    
    def GreedyAlgorithm(self):
        nCurWeight = 0
        while True:
            idx = self.ChooseFunc1()
            if idx == -1:
                break
            
            if nCurWeight + self.weight[idx] <= self.totalC:
                self.status[idx] = 1
                nCurWeight += self.weight[idx]
            else:
                self.status[idx] = 2
        self.PrintResult()

    
    def ChooseFunc1(self):
        maxPrice = 0
        index = -1
        for i in range(self.number):
            if (self.status[i] == 0) and (self.price[i] > maxPrice):
                maxPrice = self.price[i]
                index = i
        return index
    
    def ChooseFunc2(self):
        minWight = 10000
        index = -1
        for i in range(self.number):
            if (self.status[i] == 0) and (self.weight[i] < minWight):
                minWight = self.weight[i]
                index = i
        return index
    
    def ChooseFunc3(self):
        index = -1
        maxDensity = 0
        for i in range(self.number):
            if self.status[i] == 0:
                den = self.price[i]/self.weight[i]
                if den > maxDensity:
                    maxDensity = den
                    index = i
        return index


if __name__ == '__main__':
    weight = [35, 30, 60, 50, 40, 10, 25]
    price = [10, 40, 30, 50, 35, 40, 30]
    totalC = 150
    instance = Knapsack_problem(weight, price, totalC)
    instance.GreedyAlgorithm()

測試結果:

所選物品:1, 重量:30, 價格:40
所選物品:3, 重量:50, 價格:50
所選物品:4, 重量:40, 價格:35
所選物品:5, 重量:10, 價格:40
所選物品總重: 130, 總價值: 165

5.  小結

在某個階段中只考慮局部最優解都可以理解為貪婪算法,貪婪算法的解通常只能接近全局最優解。

相關焦點

  • 【算法資源】貪婪算法
    貪婪算法(Greedy algorithm)是一種對某些求最優解問題的更簡單、更迅速的設計技術。
  • 「機器學習」機器學習算法優缺點對比(匯總篇)
    「優點」:樸素貝葉斯模型發源於古典數學理論,有著堅實的數學基礎,以及穩定的分類效率。對大數量訓練和查詢時具有較高的速度。>容易「欠擬合」,一般準確度不太高不能很好地處理大量多類特徵或變量;只能處理兩分類問題(在此基礎上衍生出來的softmax可以用於多分類),且必須「線性可分」;對於非線性特徵,需要進行轉換;
  • ...的「統一場」:從與 WL 算法、組合優化算法的聯繫看 GNN 的表達...
    下一章將介紹 Xu 等人和 Morris 的研究成果,它們通過「Weisfeiler-Lehman」算法描述了這類圖。       如果 1-WL 算法輸出「非同構」,那麼 G 和 H 就不是同構的圖,但即使 1-WL 算法輸出「可能同構」,G 和 H 仍有可能非同構。例如,對於圖 4 中的圖,1-WL 算法將輸出「可能同構」,然而它們並非同構。1-WL 算法可以保證在時間複雜度為
  • Libra 採用的 HotStuff 算法作者親述:「尤物」誕生記
    共識算法,而 LibraBFT 算法則是「HotStuff」的一個變種。我們希望通過這篇文章,記錄下一個創新性算法被年輕華人研究者提出的背景,一個有可能推動區塊鏈技術發展的基礎性研究工作完成的來龍去脈。我們希望以此幫助讀者更好理解 「HotStuff」,更可以激勵區塊鏈行業的研究者和開發者更好地建設。 撰文:Ted Yin,康奈爾大學博士生,Ava Labs 的聯合創始人兼首席系統架構師
  • 【機器學習】機器學習算法優缺點對比(匯總篇)
    「優點」:樸素貝葉斯模型發源於古典數學理論,有著堅實的數學基礎,以及穩定的分類效率。對大數量訓練和查詢時具有較高的速度。:只能處理兩分類問題(在此基礎上衍生出來的softmax可以用於多分類),且必須「線性可分」;「logistic回歸應用領域:」用於二分類領域,可以得出概率值,適用於根據分類概率排名的領域,如搜索排名等。
  • 「算法與數據結構」二叉樹之美
    或「父節點」:若一個節點含有子節點,則這個節點稱為其子節點的父節點;「孩子節點」或「子節點」:一個節點含有的子樹的根節點稱為該節點的子節點;「兄弟節點」:具有相同父節點的節點互稱為兄弟節點;節點的「層次」:從根開始定義起
  • 玩轉算法 - Python歸併排序
    歸併排序算法實現玩轉算法 - Python選擇排序玩轉算法 - Python冒泡排序玩轉算法 - Python插入排序玩轉算法 - Python算法理論
  • 「元學習」解析:學習如何梯度下降與學習新的算法
    在這篇文章中,Cody 介紹了元學習的基本概念和方法類別,討論了「元學習」到底在學什麼、又有哪些限制。當我第一次聽到「元學習」的時候,它的概念是如此地令我沉醉:這個項目要構建不僅能夠進行學習的機器,這些機器還能學習「如何學習」的方法。
  • 算法一看就懂之「 插入排序 」
    「 冒泡排序 」了,今天咱們來看一看「 插入排序 」。「 插入排序 」與「 冒泡排序 」一樣都屬於時間複雜O(n*n)的排序算法,並且也都是基於元素之間比較的方式來完成排序的。不過「 插入排序 」比「 冒泡排序 」在實際應用中更廣泛一些,因為它的執行效率更高一點。一、「 插入排序 」是什麼?
  • 算法一看就懂之「 選擇排序 」
    「 冒泡排序 」和「 插入排序 」了,今天我們來看一看「 選擇排序 」。「 選擇排序 」雖然在實際應用中沒有「 插入排序 」廣泛,但它也是我們學習排序算法中必不可少的一種。「 冒泡排序 」和「 插入排序 」都是在兩層嵌套循環中慢慢比較元素,不停的調整元素的位置。而「 選擇排序 」就比較直接了,屬於不出手則已,一出手,相應的元素就必須要到位,元素的位置就不會再變了。下面我們來詳細了解下一下它的邏輯。一、「 選擇排序 」是什麼?
  • 算法一看就懂之「 數組與鍊表 」
    但別人封裝好了不代表我們就可以不關注了,數據結構作為程式設計師的內功心法,是非常值得我們多花時間去研究的,我這就翻開書複習複習:本文就先從大家最經常使用的「 數組 」和「 鍊表 」聊起。不過在聊數組和鍊表之前,咱們先看一下數據的邏輯結構分類。通俗的講,數據的邏輯結構主要分為兩種:知道了分類,下面我們來詳細看一下「 數組 」和「 鍊表 」的原理。
  • 【機器學習終極盤點】你不知道的機器學習算法優缺點(匯總篇)
    「優點」:樸素貝葉斯模型發源於古典數學理論,有著堅實的數學基礎,以及穩定的分類效率。對大數量訓練和查詢時具有較高的速度。:只能處理兩分類問題(在此基礎上衍生出來的softmax可以用於多分類),且必須「線性可分」;「logistic回歸應用領域:」用於二分類領域,可以得出概率值,適用於根據分類概率排名的領域,如搜索排名等。
  • (十八) -- 從貪婪算法和動態規劃說起
    增強學習的最終目的,就是在和外界環境的接觸/探索/觀察的過程中,不斷改進策略,把長期的回報/利益最大化而已.        (2)增強學習的理論基礎, 要從運籌學裡的"貪婪算法" (Greedy Algorithm) 說起.
  • 圖解「歸併排序」算法(修訂版)
    分」的過程。這一步事實上也是 「治」的過程,所謂治就是真正進行排序的過程,因為通過這一步元素 5 和 1 的順序將被改變。心中一驚,為何這裡的遞歸過程如此曲折,事實上沒有什麼可擔心的,你將代碼中的 mergeSort(arr,l,r) 理解為「分」和「遞
  • 到底什麼是算法穩定幣?
    它被不少業內人士視為一場貨幣改革,但也另有論調認為,算法穩定幣中的三種行權通證都沒有足額贖回權和利潤支撐,完全依賴於後來人的資金投入,具有龐氏色彩。更滑稽的一幕是,謀求用算法穩定的幣價,往往因人性的貪婪或恐懼,導致穩定幣不穩,要麼高於1美元錨定,要麼跌了就漲不回1美元。
  • 深度 | 如何保證算法公正性?ICML 2018兩篇獲獎論文解讀
    第一篇:「Delayed Impact of Fair Machine Learning」贏得了 ICML 2018 Best Paper Awards。標題直譯為《公正機器學習的滯後影響》,本推送中簡稱論文。
  • EM算法初探
    EM算法詳解EM算法,全稱為Expectation Maximum Algorithm,是一個基礎算法,是很多機器學習領域算法的基礎(如HMM,LDA等)。EM算法是在「概率模型中尋找參數最大似然估計或者最大後驗估計的算法,其中概率模型依賴於無法觀測的隱含變量。」
  • 這次,讓算法走下神壇!
    耳熟能詳的三本算法書《算法》、《算法導論》、《算法圖解》,卻一本都沒讀完:「看了半年的《算法》這本書,才看了幾十頁」「4 年了,還是沒有啃完《算法導論》」有的朋友覺得像人工智慧、數據搜索與挖掘這樣高薪的工作才用得上算法,覺得算法深不可測,只可遠觀。這次,我將會用一些通俗的案例解釋算法,讓算法走下神壇。
  • 圖解經典的進程調度算法
    回顧一下進程的三態模型:「運行態」(running):進程佔有 CPU 正在運行。「就緒態」(ready):進程具備運行條件,等待系統分配 CPU 以便運行。「阻塞態」 / 等待態(wait):進程不具備運行條件,正在等待某個事件的完成。
  • 用算法「種」出的草莓裡,藏著年輕人與農業的未來
    用數字種植服務小農 「還是低估了。」 工程師出身的程飈在兩個多月前,參加了「多多農研科技大賽」——一個高原草莓種植的「人機對戰」比賽,要求在 120 天的時間內,用 AI 算法遠程控制草莓的生長,最終綜合比拼草莓的產量、口感、成本等等。同時,有來自國內草莓大縣的頂尖農人作為對照組。 過程是出人意料的。