📈 第3章 最优化
📄 一句话概括

目标函数就是把「想要什么」写成一个能求最大值的式子。这里它是利润: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 秒实验

  1. 把鱼的利润从 5 改成 8,等利润线斜率变成多少?哪个点会变成最优?
  2. 把鸭的利润改成 0,最优解会落在哪条轴上?
  3. 想一想:如果两个产品利润一样,最优解会是一条边还是一点?

常见误区

  1. 只写收入不写成本:最优解永远顶到产能上限,因为那样「收入」最大。
  2. 把固定成本算进单位利润:固定成本不随产量变,会让单位利润随产量变化,模型就不是线性的了。
  3. 系数拍脑袋:目标函数的系数必须能对着账本核,拍出来的系数会给你精确的错答案。
  4. 以为算出小数就能生产:x = 110.4 只鸭子不存在,整数规划是另一个问题。
自测三问(点开对答案) 1. 等利润线的斜率说明了什么? —— 两种产品之间的交换比:少做一个鸭,需要多做 0.6 条鱼才能补回同样的利润。 2. 为什么最优解一定在顶点上? —— 可行区域是凸多边形,目标函数是线性函数,直线平移最后离开多边形的位置只能是顶点或整条边。 3. 目标函数系数从哪里来? —— 从账本。每一个系数都要能指出它对应哪一项收入或成本,指不出来就是猜的。

目标函数有了,接下来把三条约束画进一张图,看那块地长什么样,最优解落在哪个角上。

相关笔记