原料涨价,鸭的利润从 3 掉到 1.2,利润从 465 掉到 219——少赚 246(百元),每天少赚两万四。 再涨一轮,鸭的利润变成负数,最优解照样是「做 130 只鸭」——因为客户那条保底还压着。 利润变成 −2.0,每天倒贴 200 元。这不是算错,是约束逼着你做赔本的活。
上一篇加了保底约束,利润降到 465。这一篇把价格改掉,看同一个模型怎么给出完全不同的答案。 「模型只描述你规定的情况」这句话,会在第 10 章(回归)和第 11 章(误差)反复出现。
一、改价格,不改约束
原料涨价,鸭的利润 3 → 1.2,鱼 5 → 4.2。约束一条没变。 重算:最优还是 (130, 15),因为约束把它钉住了;可利润从 465 掉到 219。 少赚 246(百元),也就是每天少赚 24600 元。位置没变,钱没了。
二、跌穿地板是怎么回事
再涨一轮,鸭的利润变成 −0.5:每做一只都在亏。 带保底约束的最优利润是 −2.0——不是「少赚」,是「倒贴」,每天亏 200 元。 如果不接这张单,全做鱼能赚 420(百元)。一来一回差 136(百元),每天 13600 元。
这就是「利润跌穿地板」:模型算得出最优解,可那个最优解在现实里是亏钱的。
三、为什么模型不提醒你
因为模型的任务只有一件:在给定约束下最大化目标函数。 目标函数变成负的,它照样能把「最不亏」的那个点找出来,然后平静地报给你。 模型不会说「这个方案不该做」,它只会说「这是最好的」。 这两句话差得很远。
四、看到负数该怎么办
第一步,确认不是算错:把解代回约束,看是否满足。 第二步,找那条把你钉住的约束,问它值不值得留。 第三步,算「去掉它之后能多赚多少」,拿这个数去和客户谈。
三步走完,你手里就不是一个「最优解」,而是一个能拿去谈的方案。
五、模型的边界
它只回答「在给定的价格和约束下,怎么配最赚」。 它不回答「要不要涨价」「要不要换机器」「要不要放弃这个客户」——那些是另一层决策。 用一个模型去回答它没建的那个问题,是数据分析里最常见的越界。
六、动手 Demo:利润跌穿地板:假设变了,最优解就废了
同一套约束,换两组价格,看最优解和利润怎么动。 第二组价格里鸭的利润是负的,最优解却还是要做 130 只鸭。
A = [(2, 4), (3, 2), (1, 1), (-1, 0), (0, -1), (-1, 0)]
B = [400, 420, 180, 0, 0, -130]
def solve(c, A, B):
pts = []
for i in range(len(A)):
for j in range(i + 1, len(A)):
a1, a2 = A[i], A[j]
det = a1[0] * a2[1] - a1[1] * a2[0]
if abs(det) < 1e-12:
continue
x = (B[i] * a2[1] - a1[1] * B[j]) / det
y = (a1[0] * B[j] - B[i] * a2[0]) / det
if x < -1e-9 or y < -1e-9:
continue
if all(A[k][0] * x + A[k][1] * y <= B[k] + 1e-9 for k in range(len(A))):
pts.append((x, y))
return max(pts, key=lambda p: c[0] * p[0] + c[1] * p[1])
for c in ((3, 5), (1.2, 4.2), (-0.5, 4.2)):
x, y = solve(c, A, B)
z = c[0] * x + c[1] * y
print('利润系数 %.1f/%.1f -> (%.0f, %.0f) 利润 %.1f' % (c[0], c[1], x, y, z))
实跑输出:
--------------------------------------------------------------------
利润跌穿地板:假设变了,最优解就废了
--------------------------------------------------------------------
① 一条坏消息
--------------------------------------------------------------------
原料涨价,橡皮鸭的利润从 3 掉到 1.2,橡皮鱼从 5 掉到 4.2。
厂长问:那还按原来的方案做吗?
② 重算
--------------------------------------------------------------------
新价格下的最优:x = 130.0,y = 15.0,利润 219.0
涨价前同一套约束下是 465.0,掉了 246.0(百元)= 24600 元/天。
注意位置没变:约束还是那几条,只有价格变了,最优解却少赚了这么多。
③ 「利润跌穿地板」是怎么回事
--------------------------------------------------------------------
原料再涨一轮,鸭子的利润变成 −0.5(做一只亏一点)。
带客户约束的最优:x = 130.0,y = 15.0,利润 -2.0
这个负号是真的:每天要倒贴 200 元。
可客户那条「至少 130 百只」还压着,你不能停产。
要是不接这张单,最优是 x = 0.0,y = 100.0,赚 420.0(百元)。
一来一回差 422.0(百元)= 42200 元/天。
! 这就是教材说的「利润跌穿地板」——不是算错,是约束逼着你做赔本的活。
④ 模型只描述你规定的情况
--------------------------------------------------------------------
上面每一步都只回答一个问题:在给定的价格和约束下,最优是多少。
价格一变、约束一变,答案就得重算。模型不会自己知道世界变了。
! 把模型当成「客观规律」,是数据分析里最贵的一次误解。
三行结果:(130, 15) 465;(130, 15) 219;(130, 15) −2.0。 最优解一点没动,利润一路掉到负数——因为约束没变,变的只是「做一只能赚多少」。
30 秒实验
- 把保底约束去掉(删掉最后一行 A、B),再跑第三组价格,最优解是哪里?
- 算出「鸭的利润掉到多少时,接这张单刚好不亏」。
- 想一想:如果客户愿意把单价提到让鸭利回到 0.5,这笔单还划算吗?
常见误区
- 看到负利润就以为算错:先代回约束验证,负数可能是真结论。
- 改目标函数不改约束:只调价格不调保底,会得到一个「合法但荒谬」的方案。
- 拿最优解直接排产:最优解是模型的最优,不是公司的决策,中间还隔着一次判断。
- 以为模型会提醒你越界:它不会。它只会在你划定的框里找最好的点。
自测三问(点开对答案)
1. 为什么价格变了最优解却没变? —— 因为约束把它钉在了那个顶点上。价格只影响「这个点值多少钱」,不影响「哪个点可行」。 2. 利润 −2.0 说明什么? —— 说明在保底 130 的约束下,最好的做法也是每天亏 200 元。这时候该谈的是约束,不是配方。 3. 模型为什么不会提醒你「这个方案不该做」? —— 因为它的任务只是最大化目标函数,没有「该不该」这个维度。判断是人的事。模型算得再准,也只对写进去的假设有效。最后一篇讲怎么校正假设,以及那个被忽略的负相关。
- ☐ (本篇完,下一篇 3.6 校正假设)