目标函数就是把「想要什么」写成一个能求最大值的式子。这里它是利润:3x + 5y。 等利润线是一组平行线,斜率说明两种产品之间的交换比。线越往外推,利润越高。 为什么最优解一定在顶点上——这一篇给一个能看懂的论证,不给证明。
上一篇写清了变量和约束。这一篇补上第三行:目标函数。 「最优在顶点」这条事实,是后面用顶点枚举代替搜索的全部依据,第 3.3 篇会真的把顶点全列出来。
一、把「赚得多」写成式子
橡皮鸭每百只赚 300 元,橡皮鱼每百只赚 500 元。 为了算起来省事,单位统一成「百元」:鸭 3、鱼 5。目标函数就是 3x + 5y。 这一步看着简单,其实是整个模型里最容易出错的地方——利润的定义方式决定了答案。
是毛利还是净利?含不含分摊的固定成本?换个口径,最优解就换了。
二、等利润线
固定利润等于 300,就有 3x + 5y = 300,一条直线。利润取 400、500,是一组平行线。 斜率是 −3/5:少做一个鸭,得多做 0.6 条鱼才补得回来。这个 0.6 就是两种产品的交换比。 线越往右上推,利润越高。推到不能再往外推,就是最优。
三、为什么最优在顶点
可行区域是一个凸多边形,等利润线是直线。 直线往一个方向平移,最后离开多边形的位置,只能是顶点,或者整整一条边。 如果是整整一条边,说明那条边上的所有点利润相同——这叫多重最优解,实际生产中通常是好事, 可以按别的标准挑。
所以不用搜索整个区域,只要枚举顶点。这是线性规划最基本的一条事实。
四、目标函数写错的三种样子
漏掉成本项:只写收入,最优解永远是「生产到产能上限」。 把固定成本写进去:固定成本不随产量变,写进去不影响最优解的位置,只会让利润数变难看。 两个目标混在一个式子:既要利润高又要库存低,这不是一个目标函数,是两个,得先定权重。
五、一条经验
目标函数里出现的每一个系数,都应该能对着账本指出来源。 指不出来的系数,就是猜的。猜的系数会给你一个精确到小数点的错答案。
六、动手 Demo:目标函数:把「赚得多」写成一个式子
三条约束化成等式,求出它们各自与两个轴的交点。 再拿几个点代进目标函数,看利润怎么变。
cons = [('molding', 2, 4, 400), ('assembly', 3, 2, 420), ('material', 1, 1, 180)]
for name, a, b, cap in cons:
print('%-9s %dx + %dy = %d x 轴 %d y 轴 %d'
% (name, a, b, cap, cap // a, cap // b))
print()
for x, y in ((0, 0), (60, 60), (110, 45), (140, 0)):
print('(%3d, %3d) 利润 %4d' % (x, y, 3 * x + 5 * y))
print('等利润线斜率 -3/5 = %.2f:少做一个鸭,得多做 %.1f 条鱼' % (-3 / 5.0, 3 / 5.0))
实跑输出:
--------------------------------------------------------------------
目标函数:把「赚得多」写成一个式子
--------------------------------------------------------------------
① 利润怎么来的
--------------------------------------------------------------------
橡皮鸭每百只赚 300 元,橡皮鱼每百只赚 500 元。
为了算起来省事,下面把单位统一成「百元」:鸭 3、鱼 5。
目标函数:最大化 3x + 5y。
② 三条约束线穿过哪些点
--------------------------------------------------------------------
| 资源 | 等式 | 与 x 轴交点 | 与 y 轴交点 |
|---|---|---|---|
| 注塑工时(分钟) | 2x + 4y = 400 | (200, 0) | (0, 100) |
| 装配工时(分钟) | 3x + 2y = 420 | (140, 0) | (0, 210) |
| 原料(公斤) | 1x + 1y = 180 | (180, 0) | (0, 180) |
③ 目标函数也画一条线
--------------------------------------------------------------------
3x + 5y = 利润。利润取不同值,就是一组平行线。
线越往外推,利润越高——推到再往外就没有可行点了,那就是最优。
这条线叫等利润线。它的斜率 −3/5 说明:少做一个鸭,得多做 0.6 条鱼才补得回来。
④ 为什么最优一定在顶点上
--------------------------------------------------------------------
可行区域是一个凸多边形,等利润线是直线。
直线往一个方向平移,最后离开多边形的那个点,一定是顶点(或者整条边)。
所以不用搜遍整个区域,只要把顶点全枚举出来,比一比就行。
! 这是线性规划最基本的一条事实,Excel 的 Solver 也是这么干的。
⑤ 那要是我有三个产品呢
--------------------------------------------------------------------
三个变量就是三维空间里的多面体,顶点还是顶点,只是画不出来了。
四个以上就只能靠算法,但原理没变:最优解在顶点,顶点是约束边界的交点。
(110, 45) 的 555 是这四个点里最高的,但「这四个点里最高」不等于「全局最高」。 要证明它是全局最优,得把所有顶点都枚举一遍——下一篇做。
30 秒实验
- 把鱼的利润从 5 改成 8,等利润线斜率变成多少?哪个点会变成最优?
- 把鸭的利润改成 0,最优解会落在哪条轴上?
- 想一想:如果两个产品利润一样,最优解会是一条边还是一点?
常见误区
- 只写收入不写成本:最优解永远顶到产能上限,因为那样「收入」最大。
- 把固定成本算进单位利润:固定成本不随产量变,会让单位利润随产量变化,模型就不是线性的了。
- 系数拍脑袋:目标函数的系数必须能对着账本核,拍出来的系数会给你精确的错答案。
- 以为算出小数就能生产:x = 110.4 只鸭子不存在,整数规划是另一个问题。
自测三问(点开对答案)
1. 等利润线的斜率说明了什么? —— 两种产品之间的交换比:少做一个鸭,需要多做 0.6 条鱼才能补回同样的利润。 2. 为什么最优解一定在顶点上? —— 可行区域是凸多边形,目标函数是线性函数,直线平移最后离开多边形的位置只能是顶点或整条边。 3. 目标函数系数从哪里来? —— 从账本。每一个系数都要能指出它对应哪一项收入或成本,指不出来就是猜的。目标函数有了,接下来把三条约束画进一张图,看那块地长什么样,最优解落在哪个角上。
- ☐ (本篇完,下一篇 3.3 约束条件与可行区域)