最優包裹組合-貪心算法

週末時間基本都在帶娃,斷更了一段時間,可貴有點時間仍是得有毅力堅持寫,堅持總結。算法

最近公司在拓展電商相關業務,其中一個環節是物流發貨。物流打包環節有一個需求是但願能給運營同事一個小工具能快速計算最優的包裹組合。後端

咱們這裏不關注過多業務細節,只是把這個問題抽象總結一下。工具

問題:

假設咱們有以下4種規格的包裹可供選擇:code

name size weight price
Package S 20 5KG 4.99€
Package M 40 10KG 6.99€
Package L 80 20KG 8.99€
Package XL 120 30KG 10.99€

因爲咱們的貨物是拋貨, 因此只考慮給定任意總Size的的打包需求,能獲得最優的包裹組合。blog

拋重:便是體積重,
一、當實重大於體積重,就屬於重貨,按實重計費度;
二、當體積重大於實重就叫拋貨,按體積重計費;get

這裏最優的標準比較簡單:包裹個數最少,同時也是總運費最低。input

例如,我須要發153個貨,那麼最優的就是Package XL * 1 + Package M * 1。產品

貪心算法

很符合直覺的策略是先儘量選大的包裹,先從小號開始匹配,小號的裝不下就升級大一號的包裹,直到能裝下或者沒有更大的箱子了,依次類推。例如總共要裝箱150個,那麼:it

  • 先用Package S 發現裝不下,
  • 升級成Package M也裝不下,
  • 繼續升級成Package L也裝不下,
  • 繼續升級成Package XL也裝不下,可是沒有更大的選擇了,因此(貪心)打包一次。更新待裝箱的商品數據量 = 150 - 120 = 30;

這樣每次一個週期結束,再次用剩餘沒裝箱的數量繼續這樣作打包操做:io

  • 先用Package S 發現裝不下,
  • 升級成Package M發現能裝下了,因此打包一次。更新待裝箱的商品數據量 = 30 - 40 <= 0;

總結上述過程,咱們發現每次迭代打包本質都是把當前須要求解的問題分紅了一個子問題,每次解決子問題的策略都是儘可能優先用最大的箱子,且任意子問題是不依賴上次迭代的結果。能夠理解爲每次迭代無非是傳入了一個新的須要打包的產品數量,這樣就能每次迭代都是朝着問題的最終解進了一步。

貪心算法不是對全部問題都能獲得總體最優解,選擇的貪心策略必須具有無後效性,即每次貪心決策不會影響之前的狀態,只與當前狀態有關。因此對所採用的貪心策略必定要仔細分析其是否知足無後效性。

因此貪心策略適用的前提是,局部最優策略能最終產生全局最優解。也就是當算法終止的時候,局部最優正好就是全局最優

實現

因爲只是單純的計算,不須要持久化業務的數據,若是把計算業務邏輯寫在後端每次API調用也比較耗時,因此我索性直接用JS把計算代碼也擼了。

計算代碼:

function minPackageCal(packages, amount) {
    let minimumUnit = Number.MAX_SAFE_INTEGER;
    let maximumUnit = 0;
    let packageMap = new Map();
    for (let i = 0; i < packages.length; i++) {
      packageMap.set(packages[i], 0);
      minimumUnit = Math.min(packages[i], minimumUnit);
      maximumUnit = Math.max(packages[i], maximumUnit);
    }

    let packageResult = [];
    let totalPacked = 0;
    while (totalPacked < amount) {
      for (let i = 0; i < packages.length; i++) {
        var packageUnit = packages[i];
        if(packageUnit > amount - totalPacked || packageUnit === maximumUnit){
          packageResult.push(packageUnit);
          packageMap.set(packageUnit, packageMap.get(packageUnit)+1);
          totalPacked += packageUnit;
          break;
        }
      }
    }
    return packageMap;
  }

調用代碼:

//given package types
    const packageTypes = [20, 40, 80, 120];
    
    //amountInputValue should be data binding with the input box    
    var result = minPackageCal(packageTypes, amountInputValue);

最終實現的效果:
pacjage.gif

貪心的侷限性

在這裏例子中,因爲可供選擇的包裹條件限制,咱們使用貪心算法得出來的局部最優解剛好是全局最優解。可是若是咱們換一個包裹組合陣型就不必定了,
好比:
假設今天咱們的包裹Package M作了推廣特價,從6.99€ 打折到3.99€,那麼按照貪心咱們不必定能獲得最佳的組合了:

name size weight price
Package S 20 5KG 4.99€
Package M 40 10KG 3.99€
Package L 80 20KG 8.99€
Package XL 120 30KG 10.99€

若是局部最優不是全局最優,那麼就不能用貪心算法,能夠考慮用動態規劃來解決這類最優解問題。

若是以爲有所收穫,麻煩幫我順手點個在看或者轉發吧,你的舉手之勞對我來講就是最大的鼓勵。 END~

歡迎關注個人公衆號:好奇心森林
Wechat

相關文章
相關標籤/搜索