最优包裹组合-贪心算法

周末时间基本都在带娃,断更了一段时间,可贵有点时间仍是得有毅力坚持写,坚持总结。算法

最近公司在拓展电商相关业务,其中一个环节是物流发货。物流打包环节有一个需求是但愿能给运营同事一个小工具能快速计算最优的包裹组合。后端

咱们这里不关注过多业务细节,只是把这个问题抽象总结一下。工具

问题:

假设咱们有以下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

相关文章
相关标签/搜索