3. 库存对冲核心算法:FIFO、LIFO 与加权平均
好,咱们今天聊点硬核的。库存对冲算法,说白了就是决定「哪一批货先被卖掉」的规则。你可能会想,这不就是个出库顺序吗?嗯,没那么简单。我当年在做一个电商仓储系统时,就因为选错了算法,导致财务账面和实际库存差了十几万,老板差点让我去仓库搬货……
今天我把三种最主流的算法掰开揉碎讲清楚:先进先出(FIFO)、后进先出(LIFO)、加权平均。每种我都会给出核心逻辑、代码实现,以及我踩过的坑。
3.1 先进先出(FIFO)对冲算法
FIFO 的逻辑最简单:先入库的货,先出库。这符合大多数人的直觉,也符合很多行业的实际流转规律——比如食品、药品,你总不能让先来的面包过期吧?
我在项目中遇到过一家生鲜电商,他们坚持用 FIFO。为什么?因为水果放不起。第一批进来的草莓如果不出掉,第三天就得扔。所以 FIFO 不仅是算法,更是生存法则。
核心逻辑
- 每次出库时,从最早的批次开始扣减库存
- 扣完一个批次,再扣下一个批次
- 出库成本 = 最早批次的入库成本
代码示例(Python)
class FIFOEngine:
def __init__(self):
self.batches = [] # 每个元素: (qty, cost)
def inbound(self, qty, cost):
self.batches.append([qty, cost])
def outbound(self, qty):
total_cost = 0
remaining = qty
while remaining > 0 and self.batches:
batch_qty, batch_cost = self.batches[0]
if batch_qty <= remaining:
total_cost += batch_qty * batch_cost
remaining -= batch_qty
self.batches.pop(0) # 批次用完,移除
else:
total_cost += remaining * batch_cost
self.batches[0][0] -= remaining
remaining = 0
return total_cost
3.2 后进先出(LIFO)对冲算法
LIFO 正好反过来:后入库的货,先出库。你想想看,什么场景下会这样?我最早接触 LIFO 是在钢材贸易行业。钢材价格波动大,新进来的货成本高,老板想先把高成本的货出掉,这样账面利润低,能少交税。嗯,财务上的小心思。
但 LIFO 有个致命问题:实物可能不是后进先出的。比如你仓库里堆着水泥,新来的水泥压在最上面,工人肯定先搬上面的——这倒是符合物理规律。但如果是液体罐,新油和旧油混在一起,LIFO 就完全没法用了。
核心逻辑
- 每次出库时,从最新的批次开始扣减库存
- 扣完一个批次,再扣上一个批次
- 出库成本 = 最新批次的入库成本
代码示例(Python)
class LIFOEngine:
def __init__(self):
self.batches = []
def inbound(self, qty, cost):
self.batches.append([qty, cost])
def outbound(self, qty):
total_cost = 0
remaining = qty
while remaining > 0 and self.batches:
batch_qty, batch_cost = self.batches[-1] # 取最后一个
if batch_qty <= remaining:
total_cost += batch_qty * batch_cost
remaining -= batch_qty
self.batches.pop()
else:
total_cost += remaining * batch_cost
self.batches[-1][0] -= remaining
remaining = 0
return total_cost
3.3 加权平均对冲算法
加权平均,说白了就是「不管哪批货,统一按平均成本算」。这个算法最省心,也最常用。尤其是那些无法区分批次的场景——比如散装液体、颗粒物料、或者你根本懒得管批次。
加权平均有两种常见变体:
- 移动加权平均:每次入库后重新计算平均成本
- 月末一次加权平均:月底统一算一次,平时只记数量
我个人习惯用移动加权平均,因为实时性更好。但要注意,如果入库频率特别高,计算量会比较大。我做过一个日订单量 10 万+的系统,移动加权平均的 CPU 消耗占了整个库存模块的 40%。后来改成了定时批量计算,才压下去。
核心逻辑
- 维护当前总库存数量和总库存成本
- 每次入库:更新总数量和总成本
- 每次出库:按当前平均成本计算出库成本
- 平均成本 = 总成本 / 总数量
代码示例(Python)
class WeightedAverageEngine:
def __init__(self):
self.total_qty = 0
self.total_cost = 0.0
@property
def avg_cost(self):
if self.total_qty == 0:
return 0.0
return self.total_cost / self.total_qty
def inbound(self, qty, cost):
self.total_cost += qty * cost
self.total_qty += qty
def outbound(self, qty):
if qty > self.total_qty:
raise ValueError("库存不足")
cost = qty * self.avg_cost
self.total_qty -= qty
self.total_cost -= cost
return cost
3.4 三种算法对比
| 特性 | FIFO | LIFO | 加权平均 |
|---|---|---|---|
| 出库顺序 | 先入先出 | 后入先出 | 无顺序,统一平均 |
| 适用场景 | 食品、药品、快消品 | 钢材、大宗商品(财务需求) | 散装物料、无法区分批次 |
| 计算复杂度 | 低(需维护批次队列) | 低(需维护批次栈) | 极低(仅维护总量) |
| 财务影响 | 利润偏高(物价上涨时) | 利润偏低(物价上涨时) | 利润平滑 |
| 合规性 | 全球通用 | 部分国家限制 | 全球通用 |
3.5 算法选择决策流程
你可能会问:那我到底该用哪个?别急,我画了一张流程图,帮你快速决策。
这张图我建议你保存下来。每次做新项目,先问自己两个问题:能不能分批次?财务允不允许 LIFO?答案出来,算法就定了。
- FIFO 最符合直觉,适合保质期敏感的商品
- LIFO 有财务避税优势,但合规性要提前确认
- 加权平均最省事,适合无法区分批次的场景
- 没有「最好」的算法,只有「最合适」的算法
好了,三种算法的核心逻辑和代码实现都讲完了。你可能会觉得代码很简单,嗯,确实不复杂。但真正难的是——当你的系统有 10 万种 SKU、每天几十万笔出入库时,如何保证性能、精度和一致性。这些实战问题,咱们后面会慢慢聊。