解决一般 MOQ(最小订单量)问题

解决一般 MOQ(最小订单量)问题


首页 » 知识库 » 此处
作者:Joannès Vermorel,2016 年 1 月

MOQ(最小订单量)是供应链中一种普遍存在的订货限制形式。MOQ 限制意味着供应商不会接受低于指定阈值(通常用金额-美元或件数来表示)的采购订单。多种 MOQ 限制往往并存并且必须同时满足这些限制。一般 MOQ 问题包括计算(接近)最佳采购订单,此类订单既要满足所有 MOQ 限制,同时又要实现所购产品件数的经济回报最大化。

一般 MOQ 问题在形式上属于一种非线性的优化难题。这个问题之所以被划定为难题,是因为计算最佳解决方案通常超出了计算的范畴,这一点是可以证明的。但是,尽管通常无法获得最佳解决方案,但可以通过高级的非线性约束求解算法来获得接近最佳的解决方案。我们将在下文介绍 moqsolv,这是一种由 Lokad 提出的用于解决一般 MOQ 问题的高级数值解算器。

常见的 MOQ 限制

MOQ 限制可以多种形式存在。在实际供应链中,我们遇到的常用 MOQ 限制有:

  • 按每个 SKU 件数表示的最小数量,通常反映的是项目价格太低,因而无法单独出售。
  • 按整个采购订单的金额(美元)表示的最低数量,当订单金额达到一定数量时供应商不会收取运输费用。
  • 按项目类别件数表示的最小数量,常见于以最小数量批量生产产品以供订购的情况。

一次处理一种限制往往很简单。但是,如果需要同时考虑多种 MOQ 限制,那么创建能同时满足所有这些限制的采购订单就非常难了。

MOQ 概念

在深入研究数值优化问题前,我们先来介绍与一般 MOQ 问题相关的重要概念。这些概念有:

  • 项目,表示可以实际购买的产品。尽管在这方面没有任何限制,但项目数量通常为整数。
  • 每种项目的订货量(可能为 0),它表示 MOQ 问题的一种潜在解决方案。
  • 每种项目额外增加一件所产生的相关效益 – 可通过 stockrwd 函数(库存效益函数)获取,但并不要求必须使用该函数。
  • 所购件数的相关成本。其目标是最大化给定经费预算(表示为“成本”)的效益。成本通常按单位成本考虑并且是固定的,但在此处我们不会做任何假设;另外也要考虑价格间断。
  • 目标表示指定某种停止标准的方式(可能并非实际成本)。这一点相当复杂,下文会进行详细介绍。

在按照采购优先级列表进行采购时,典型的停止标准是定义一个最高预算:所购件数的 ROI 递减,直至整个采购预算花完为止。但是,预算上限并不能说明可以获取的绝对库存绩效。因此,虽然目标仍然是优化 ROI,但不论考虑的是何种停止标准,再考虑一种备选标准是完全可行的,例如总体供货率目标。

在处理优先订货策略时,引入了目标的概念来定义备选的停止标准。简单地说,目标变成了寻找能够为最小投资实现最高 ROI,同时又要满足该目标的采购订单。在下面,我们将为这个优化过程提供更精确的定义。

示例:Frank 是一名供应链经理,他制定的供货率目标为 90%。解决这道 MOQ 问题涉及到计算最小订单(成本),同时实现效益最大化,并达到 90% 的供货率。该订单并非是尽可能小且能达到 90% 供货率的订单,因为这样将变成纯粹的供货率优先化。而是最小的订单,同时要以效益为先,即订单要足够大到实现 90% 的供货率。纯粹的供货率优先化是错误的,因为与库存效益不同,它没有考虑产生积压库存所导致的成本。

一般 MOQ 问题的形式定义

本节将把一般 MOQ 问题作为一种正式的非线性优化问题进行介绍。证明该问题为 NP-hard 相对简单。实际上,一般 MOQ 问题是对 装箱问题的延伸,后者同样也具有 NP-hard 的特点。因此,一般 MOQ 问题至少和装箱问题一样难。尽管这个问题是 NP-hard,必须要提的是它可以计算出很合理的解。

令 $I$ 为考虑订购的项目集合。 令 $q_i$($i \in I$)为项目 $i$ 的订购数量。

然后,定义几个函数。

  • 令 $r_i(q)$ 为持有 $q$ 件 $i$ 项目时的效益
  • 令 $c_i(q)$ 为购买 $q$ 件 $i$ 项目时的成本
  • 令 $t_i(q)$ 为持有 $q$ 件 $i$ 项目时的目标

效益函数可以返回正值或负值,但是,成本和目标函数严格为正: $$\forall i, \forall q, c_i(q) > 0 \text{ and } t_i(q) >0$$ 令 $M$ 为 MOQ 限制集合。对于每个 $m \in M$,令 $I_m$ 为属于限制 $m$ 的项目列表,$Q_m$ 为满足该限制所应达到的最小数量。令 $m_i(q)$ 为购买 $q$ 件项目 $i$ 时这些项目对于 MOQ 限制 $m$ 的贡献。如果满足以下条件,则可以认为满足限制 $m$: $$\forall i \in I_m, q_i = 0 \text{ or } \sum_{i \in I_m}m_i(q_i) \geq Q_m$$ 因此,所有 MOQ 限制均可通过这两种方式来满足:达到 MOQ 阈值,或者令所有项目的数量为 0。

然后,再令 $C$ 为该采购订单所能提供的最大成本。我们可以定义 $\textbf{q}_C=(q_i)_i$,因为: $$\textbf{q}_C = \underset{q}{\operatorname{argmax}} \left\{ \sum_i r_i(q_i) \text{ with $m$ satisfied } \forall m\in M \right\}$$ 就最大化给定预算的效益而言,该采购订单是"最佳"订单。解 $\textbf{q}_C$ 并非唯一,但是,这种考量相当理论化,因为 MOQ 问题很复杂,不是一个解就能解决的。为了简单起见,下文我们会将该解作为唯一的解来处理。

最后,令 $T$ 为目标最小值,并根据以下公式定义 $\textbf{q}^T$ $$C^T = \underset{C}{\operatorname{min}} \left\{ \left(\sum_{q_i \in \textbf{q}_C} t_i(q_i) \right) \geq T \right\}$$ 和 $$\mathbf{q}^T = \textbf{q}_{C^T}$$ 解 $\mathbf{q}^T$ 基于 $\textbf{q}_C$,也就是说,它就是最小、最佳(预算级),能实现 ROI 最大化,同时也能实现该目标的解。

Envision 中的 moqsolv 函数

Envision 提供了专门用于解决一般 MOQ 问题的非线性解算器。该解算器可以通过函数 moqsolv 访问,在处理 MOQ 限制时不必再进行复杂的数值计算。函数 moqsolv 用于处理一系列与 Id(*) 类型的表相关的向量,此类表通常通过 extend.distrib() 扩展分布向量来获得(另请参阅分布代数)。

这个函数的参数如下:

moqsolv(Id, Min, Reward, Cost, Target, threshold, g0, oq0, moq0, g1, oq1, moq1, ...)

其中,

  • Id 为项目组列
  • Min 为单元订购列 – 与 Grid.Min 中一样
  • Reward 为该行的效益 – 与通过库存效益函数 stockrwd 所获得的结果一样
  • Cost 为该行的成本 – 通常为 PurchasePrice * Grid.Q
  • Target 为目标数量 – 对于简单的预算限制,它可以等于 Cost。通过设置 Target == Cost,可令解算器执行预算优化(相应地,Target != Cost 则为目标优化)。
  • threshold 为一个标量值,用作目标的阈值。如果是进行预算优化,目标值为与解相关的成本上限。如果是目标优化,目标值则为与解相关的目标下限。如果阈值为负,那么负号会解读为布尔标志,用于从下面接近(正)目标,而不是从上面接近目标。阈值的符号仅用于控制不等式的方向,阈值始终解读为正数。
  • g0, oq0, moq0 表示与各个 MOQ 限制相关联的三组向量(下文会详细介绍)。

另请参阅具有订货限制的优先订货,该指南说明了如何使用 moqsolv 函数来创建满足 MOQ 限制的采购订单。

该函数返回一个 bool 向量,其中所有位于目标之内的行被标记为 true。所有为 true 的行的组合即表明满足 MOQ 限制。如上一节中的定义,解算器求出了 $\textbf{q}^T$ 的近似解。从上一节中介绍的定义可以看出,参数 CostTargetthreshold 应严格为正。

每个 MOQ 限制通过以下三个向量定义:

  • G MOQ 表示 MOQ 分组,对于根据项目进行的 MOQ,它可以等于 Id
  • OQ 表示该行对于 MOQ 限制的订单数量的贡献。
  • MOQ 表示为了满足 MOQ 限制而应达到的阈值。

G 定义的组应定义部分项目标识符。在所有具有相同 G 值的行中,MOQ 阈值应当相同。所有 OQ 贡献值应当严格为正。MOQ 值应当为正,但也可以为零,以反映出没有实际的 MOQ 限制。

通过包含多个三组式向量,可以指定多个 MOQ。Envision 最多支持 4 个并存的 MOQ 限制。