paper2skills Playbook

Efficient Algorithms for the Joint Replenishment Problem with Minimum Order Quantities

Skill-Dynamic-Lot-Sizing-MOQ · 04-供应链

experimentforecastingpricing供应链与补货定价与利润WF-A 智能补货WF-F 动态定价
年化 ROI3×$800 - $1,200 = $1,200/批次,年节省约 $8,000
实现难度⭐⭐☆☆☆
业务视角
适用角色供应链负责人 · 采购负责人 · CEO / 运营 VP
适用平台Amazon FBA · 海外仓 · 多国仓位(美/欧/日)
什么情况下用库存周转率低,资金压在海外仓出不来;SKU 断货紧急空运,物流成本吃掉毛利;多仓库存分布不均
成功是什么样的库存周转天数从 90 天降到 60 天,断货率 <3%,海外仓综合成本降低 15-25%
业务痛点
库存周转天数太长资金压死了断货了只能空运救急成本爆了多市场库存分配不均

1. 解决的问题

核心思想:供应商的 MOQ(最低起订量)和价格阶梯(all-units discount)把补货决策从"按需订货"变成了一个权衡题——少订安全但单价高,多订便宜但压库存。Q-jump (s,S) 策略给出了在随机需求+all-units折扣下的最优解:当库存触发订货点 s 时,根据「凑量判据」决定是按实际需求量订还是直接跳到折扣门槛 Q。

2. 核心算法逻辑

核心思想:供应商的 MOQ(最低起订量)和价格阶梯(allunits discount)把补货决策从"按需订货"变成了一个权衡题——少订安全但单价高,多订便宜但压库存。Qjump (s,S) 策略给出了在随机需求+allunits折扣下的最优解:当库存触发订货点 s 时,根据「凑量判据」决定是按实际需求量订还是直接跳到折扣门槛 Q。

3. 业务应用场景

场景 A:MOQ=500,1000件降12%,3月需求700件

场景 B:三个 SKU 联合订货决策(合并运费 vs 分批)

- 独立运费:每 SKU $800/次;联合运费:一票 $1,200 - 三 SKU 月需求:A=200件@$35,B=150件@$28,C=80件@$42 - 联合最优间隔 T* = 1.8 个月(每 1.8 月联合下一次单) - 节省:3×$800 - $1,200 = $1,200/批次,年节省约 $8,000

4. 输入数据要求

请查看原始代码模板获取输入规格。

5. 输出结果

请查看原始代码模板获取输出规格。

6. 业务价值 / ROI

  • ROI 预估
  • 母婴场景典型案例(MOQ=500,3月需求700件):方案C vs 方案B 节省 ¥6,681 = $930/次
  • 10 个 SKU × 年均 4 次采购决策 = 40 次 = 年节省约 $37,200
  • 三 SKU 联合订货:年节省运费约 $8,000-$12,000
  • 实施难度:⭐⭐☆☆☆(2/5)— 纯数学计算,无需机器学习
  • 优先级评分:⭐⭐⭐⭐⭐(5/5)— 每次采购都需要,ROI 直接可量化

7. 代码模板

代码块数量:6 · 路径:paper2skills-code/supply_chain/dynamic_lot_sizing_moq

"""
Skill-Dynamic-Lot-Sizing-MOQ
基于 EJOR 2018 Q-jump (s,S) (All-Units Discount) +
    EJOR 2022 JRP with MOQ (Chugh et al.) +
    Wagner-Whitin DP (经典,1958)
母婴跨境 DTC 供应商 MOQ/价格阶梯下的动态批量决策
"""

import numpy as np
from dataclasses import dataclass
from scipy import stats


@dataclass
class LotSizingParams:
    sku_id: str
    demand_mean: float
    demand_std: float
    unit_price_normal: float
    unit_price_discount: float
    discount_moq: int
    min_order_qty: int
    holding_cost_rate: float = 0.20
    fixed_order_cost: float = 800.0
    stockout_cost_per_unit: float = 15.0
    discount_factor_per_period: float = 0.97

    @property
    def holding_cost_per_unit_per_period(self):
        return self.unit_price_normal * self.holding_cost_rate / 4


def should_jump_to_discount(params: LotSizingParams) -> tuple[bool, dict]:
    """
    EJOR 2018 凑量判据:h + p' >= α·p → 不值得凑量
    返回 (should_jump, 计算细节)
    """
    h  = params.holding_cost_per_unit_per_period
    p_prime = params.unit_price_discount
    alpha   = params.discount_factor_per_period
    p       = params.unit_price_normal

    lhs = h + p_prime
    rhs = alpha * p
    should_jump = lhs < rhs

    return should_jump, {
        "h": round(h, 4),
        "p_prime": p_prime,
        "alpha_times_p": round(rhs, 4),
        "lhs": round(lhs, 4),
        "verdict": "凑量有利 ✅" if should_jump else "不值得凑量 ❌"
    }


def breakeven_demand(params: LotSizingParams) -> float:
    """
    计算凑量盈亏平衡需求量:方案B(凑至折扣MOQ)vs 方案C(多次按小MOQ订)。
    """
    Q  = params.discount_moq

8. 论文来源

未自动抽取;请查看原始 Skill 卡片。