前阵子给一个项目排期,又碰到了那道特别经典的“差异分配”问题。项目里有三条处理线,能力一个比一个强,可任务量就那么多。当时有同事提了一嘴“要不平均分,省得大家觉得偏心”,我当场就回了一句:平均分才是不公平,因为效率高的一方会被效率低的拖住,整条链路的总产能反而下降。这不只是项目管理里的“分工”话题,其实就是这类“三级火箭”模型的本质——当多个执行单元性能参差不齐时,怎么分工才算真正的最优解。也就是我接下来要拆解的这道例题11.5。
这篇内容适合几类人看:管团队排任务的负责人、做生产调度的朋友、搞容量规划或者后端服务拆分的工程师,甚至只是手头有一批活要分给几个能力不同的人的普通打工人。核心思路不复杂:先把“分工问题”变成“数学问题”,再用一套可复现的方法去求最优解。重要的是,我会把平均主义的坑、目标函数的建法、三种常见的求解方式、以及实际项目里的那些隐藏成本全串到一起,争取你看完就能直接用起来。
1. 例题11.5在问什么:先把“三级火箭”翻译成业务语言
1.1 三级火箭只是一个比喻,真实场景至少有三个
“三级火箭”听起来很高大上,其实它就是一个抽象模型:整个大目标被拆成三段或由三个执行单元分别承接,每一段/每个单元的性能不一样,但又必须协同完成最终的总目标。生活中的对应物太多太多了:
- 三条打包产线,一条每小时处理40件,一条25件,一条只有15件;
- 三个开发小组,一个成熟团队、一个中坚团队、一个刚上手的新团队;
- 三台API服务节点,一台是新机器,一台是旧机器,一台是共享资源池里抢CPU的“弱势机器”;
- 甚至一次活动的三个执行阶段:筹备、落地、复盘,每个阶段的人力密度完全不同。
所谓“性能参差不齐”,既可以是单位时间产出不同,也可以是稳定性、熟练度、可用性的差异。例题11.5本质上问的是一个很朴素的问题:既然大家都得完成自己那一份活,那么到底该按什么比例分任务,才能让整个项目完成得最快、总产出最多、资源利用最充分?
1.2 平均分配为什么会输:一道最简单的两个单元算例
先别急着上三级,我用最简单的两个单元把“平均主义”的荒谬算给你看。
假设有两个执行单元:A的效率是60件/小时,B的效率是30件/小时,总任务量是180件。平均分,A拿90件,B拿90件。A需要1.5小时完成,B需要3小时完成。如果这是一个要求“全部完成才算完工”的场景,那整体完工时间是3小时。也就是说,A干完1.5小时后,剩下1.5小时是在等B。
如果按效率比例分,情况完全不同。总效率是60+30=90件/小时,整体最短完工时间是180÷90=2小时。A分配到的任务是60×2=120件,B分配到30×2=60件。两个人都是刚好2小时完成,没有任何人闲置,整体完工时间从3小时缩短到2小时,提升了33%。
从这个例子里你能直观看到,平均分配的问题不是“公平不公平”,而是它把“整体完工时间”这个关键指标给牺牲了。能力强的人被能力弱的人拖住,能力强的人既没有多干活,也没有提前交付,反而是大家一起干等。这就是典型的“拖累型失衡”。
1.3 题目真正的考点:优化目标到底是什么
很多人在面对这类题时,第一反应是“快的人多干点”,但没有深究“多干多少才算对”。例题11.5的考点恰恰是把“多干点”变成可计算、可求解的量化问题。
目标函数通常有两种选择:
- 工期最短:总任务量固定,求“从开始到最后一个任务完成”的时间最短。这种场景下,“同时完工”是最优的特征。
- 总产出最大:总时间预算固定,求“在给定时间内做出的总量”最大。这种场景下,同样需要让高产能单元承担更多,并且通常也是在最优解处各自同时用满时间预算。
这两类问题在数学上本质上是一回事,都是把任务量往高效率单元倾斜,直到所有单元的“完工时间”对齐。后面我会详细推导。而题目最核心的考点,就是把“平均主义”从直觉上彻底推翻,建立起“按边际能力分配”的观念。我看到不少教材的例题11.5,考的就是这个建模范式和求解过程。
2. 动手建模:别急着算,先把目标函数和约束写对
2.1 变量与参数这样定义,后面才好办
我见过很多人在这一步就翻车,因为他们不写变量定义,直接拿着计算器硬凑。硬凑在两个单元的时候勉强能行,到三个及以上单元就彻底乱了。所以无论题目多简单,先把符号列出来。
假设总任务量为Q,有三个执行单元(对应三级火箭的每一级),各自的单位时间产能为p1、p2、p3,分别承接任务量x1、x2、x3。每个单元的完工时间分别为:
- t1 = x1 / p1
- t2 = x2 / p2
- t3 = x3 / p3
约束关系很简单:x1 + x2 + x3 = Q。
如果我们追求整体完工时间最短,那么工期T是所有单元完工时间的最大值,也就是:
T = max(t1, t2, t3)
目标是最小化T。注意,这里的T不是“平均完工时间”,而是“最晚完成的那个人/那台设备的时间”。很多做调度的人容易把“平均”当目标,但甲方真正关心的往往是“最后一单什么时候交付”,也就是最大完工时间。这个区别要刻在脑子里。
2.2 两种典型目标函数:工期最短和产出最大
先说工期最短。数学上可以写成:
min T
约束:xi / pi ≤ T,i=1,2,3
x1 + x2 + x3 = Q
xi ≥ 0
在约束里,xi / pi ≤ T 的意思是:每个单元的完工时间都不能超过T。这个约束非常直观。
再说产出最大。假设总共可用的时间预算为t,各单元在t_i时间内满负荷运行,总产出为sum(pi × ti),约束是t1 + t2 + t3 = t,目标是最大化总产出。
这两个问题解出来的规律完全一样:为了让最大完工时间最小,要让所有单元同时完工;为了让总产出最大,要让所有单元同时用满时间。换句话说,最优解里通常有:
x1 / p1 = x2 / p2 = x3 / p3 = T
也就是任务量和产能成正比。这个规律就是整道题的核心答案。
2.3 连续任务和离散任务,模型差距很大
上面说的是任务可以任意切分的情况,比如打包订单、处理消息、粉刷墙面,这些任务天然是连续可分或者近似可分的。
但现实里还有一种情况:任务不能切。比如你是做活动执行的,有三辆车要安排运输,每辆车的任务只能整体派给某一辆车,不能把一趟运输拆成三段。这时候,你是连续优化问题,变成整数规划或者组合优化问题。
连续模型可以给出漂亮的数学解,整数模型则通常只能求“近似最优”。举个例子,三个单元各自只能接“一个大任务包”时,你不能让产能40的单元分到1200单里再多带200单——前提是这个200单是独立的一批任务。遇到这种情况,我的建议是先用连续模型算出“理想比例”作为方向,再用整数/组合方法在附近搜索,不要直接放弃建模。
2.4 性能会衰减怎么办:边际递减模型的近似处理
还有一个进阶情况:单元长时间满负荷运转,产能可能下降。比如加班加点的团队,后两个小时效率会明显下滑;比如数据库连接池接近上限后,单请求耗时会上升。这种非线性会让“简单的比例分配”不再严格最优。
处理思路有两个,看你手头数据够不够:
- 如果只能拿到“平均产能”,那就接受线性模型,但把平均产能取保守值,不要用峰值产能。
- 如果能拿到“产能曲线”,那就把每个单元的时间-产出曲线当成分段函数,用更精确的规划求解。不过对大多数例题和常见实际场景,线性模型已经能解决80%的问题。
记住一个原则:模型不是越复杂越好,而是“够用且可解释”最好。你算出来的方案如果连自己都解释不清,现场执行层很难信服。
3. 三种解法,按你的场景选一种
3.1 最直观的“同时完工”解法:工作量与效率成正比
从上面的模型可以直接推导出一个非常好用的公式。假如让三个单元同时完工,每个单元的完工时间T相同,那么:
x1 = p1 × T
x2 = p2 × T
x3 = p3 × T
三者相加:
x1 + x2 + x3 = (p1 + p2 + p3) × T = Q
于是:
T = Q ÷ (p1 + p2 + p3)
每个单元的任务量:
xi = pi × Q ÷ (p1 + p2 + p3)
这就是最简单、最实用的分配公式。整个系统的最短完工时间,等于总任务量除以总产能。这个结论和直觉高度一致:如果所有人/所有设备一起开工,理想状态下就相当于一个“联合产能”从头干到尾。
我在实际项目里经常用这个公式快速估算:拿总任务量除以团队总产能,先得出“理想状态下最快完工时间”,再和现实排期对一下,差距立刻就能看出来。
3.2 复杂约束用线性规划求解:Excel Solver和Python二选一
当问题引入额外限制时,比如某个单元的任务量有上限或下限、任务必须分批交接、某段工序只能在特定时段开工,公式法就不够用了。这时候用线性规划是标准做法。
Excel Solver适合不写代码的同事。操作步骤大概是:
- 开三个可变单元格放x1、x2、x3,再加一个单元格放T;
- 目标单元格里放T,设置目标为“最小值”;
- 添加约束:x1/p1 ≤ T、x2/p2 ≤ T、x3/p3 ≤ T;
- 添加等式约束:x1+x2+x3 = 总任务量;
- 勾选“使无约束变量为非负数”,然后求解。
如果你习惯写代码,Python里可以用scipy.optimize.linprog。比如把“求最小T”转换成带线性约束的标准形式,之后只需要改产能和总量,重新跑一遍即可。完整代码我会在下一节给出。
3.3 任务不可拆分时,用贪心和整数规划兜底
如果任务是整数单位且不允许切分,线性规划求出的可能是小数,这时候你面临两类选择:
- 允许近似:把小数向下取整,然后手工调整几个任务包,尽量让分配比例贴近理论值。
- 必须精确最优:用整数规划。如果你手头有商业求解器或者开源求解器,可以直接加整数约束。
没有求解器的时候,贪心法是最实用的兜底方案。核心思路是每次把“当前完工时间最早的单元”填上下一块任务,直到任务分配完。这种“短作业优先”式启发式在实践中效果很好,能快速给出一套“不差”的方案。对大多数现场排产来说,比追求数学最优更重要的是方案能落地,贪心给的可执行性往往更好。
3.4 三种解法怎么选:一张对照表
| 方法 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 按效率比例分配 | 任务连续可分、无额外约束 | 计算简单,可解释性强 | 无法处理复杂限制 |
| 线性规划 | 有额外约束、目标明确 | 能得到精确最优解,支持增减限制 | 需要工具,非线性场景不适用 |
| 贪心/整数规划 | 任务不可拆、单元数量少 | 能直接给出可执行方案 | 不一定保证全局最优,计算量可能大 |
判断标准就三条:任务能不能拆、有没有额外限制、现场需不需要精确最优。大多数场景,按效率比例分配先算一版,再看要不要上线性规划,是性价比最高的路径。
4. 完整实操:从2400份订单开始,手把手算一遍
4.1 案例参数与常规平均方案的痛点
拿一个我处理过的模拟项目来走完整流程。某个项目组有三条打包产线,相当于三级火箭的三个执行单元,每小时处理能力分别是:
- 一级单元:40件/小时
- 二级单元:25件/小时
- 三级单元:15件/小时
现在来了2400件总任务,目标是在最短时间内全部完成。三条产线可以同时开工,任务可以任意拆分。
先看平均分配的方案。平均分,每个单元分到800件。那么三个单元的完工时间分别为:
- 一级单元:800÷40=20小时
- 二级单元:800÷25=32小时
- 三级单元:800÷15=53.33小时
整体完工时间是最晚的那个:53.33小时。你会发现,一级单元20小时干完后,干等了33小时;二级单元32小时干完后,也要干等21小时。大量产能白白闲置。
4.2 按效率比例分工的完整计算过程
套用前面推导的公式。总产能为:
40 + 25 + 15 = 80件/小时
理想最短工期:
T = 2400 ÷ 80 = 30小时
各单元分配任务量:
- 一级单元:40×30=1200件
- 二级单元:25×30=750件
- 三级单元:15×30=450件
验算一下:1200÷40=30小时,750÷25=30小时,450÷15=30小时。三个单元同时完工,总工期30小时。
对比一下平均分配的53.33小时,最优方案省下了23.33小时,优化幅度约44%。这就是拒绝平均主义的直接收益。别忘了,这还只是最理想、最简单的情况。如果一级单元再快一些,收益会更大。
4.3 加一个现实约束:强项产线有产能上限
现实里,强项产线未必能无限承接任务。比如一级单元的仓储空间有限,最多只能放1000件在制品。那么这个约束就必须加进模型。
此时一级单元最多承担x1=1000件,剩余任务量为1400件,由二级和三级单元完成。为了让总工期最短,二级和三级单元同样要“同时完工”。二级和三级的总产能是25+15=40件/小时,剩余任务在它们之间的分配比例为25比15,即:
- 二级单元:1400×25÷40=875件
- 三级单元:1400×15÷40=525件
分别计算完工时间:
- 一级单元:1000÷40=25小时
- 二级单元:875÷25=35小时
- 三级单元:525÷15=35小时
整体完工时间变成了35小时,比无约束时的30小时多了5小时。这就是产能瓶颈带来的代价:最强单元被卡住之后,哪怕另外两条产线再怎么压榨,总工期也没办法回到30小时。
这种敏感性分析在实际项目里非常重要。不要只给一个最优解,而是要让业务方看到“如果某项约束被打破,工期会恶化到什么程度”,这样安排资源的时候,你才能有理有据地争取追加资源或调整计划。
4.4 用Python批量复算,产能变了只改参数
手工算一次没问题,但产能一变你又得重算。我的习惯是直接写个小脚本,把产能、总量、上限都变成参数,以后每次排产只改参数就行。
from scipy.optimize import linprog # 产线产能 p1, p2, p3 = 40, 25, 15 # 总任务量 Q = 2400 # 一级单元产能上限,None 表示无上限 cap1 = 1000 # 变量: [x1, x2, x3, T] c = [0, 0, 0, 1] # 约束: xi/pi <= T => xi - pi*T <= 0 A_ub = [ [1, 0, 0, -p1], [0, 1, 0, -p2], [0, 0, 1, -p3], ] b_ub = [0, 0, 0] # 等式约束: x1 + x2 + x3 = Q A_eq = [[1, 1, 1, 0]] b_eq = [Q] # 边界 bounds = [ (0, cap1 if cap1 else None), (0, None), (0, None), (0, None), ] result = linprog(c, A_ub=A_ub, b_ub=b_ub, A_eq=A_eq, b_eq=b_eq, bounds=bounds, method="highs") x1, x2, x3, T = result.x print(f"一级任务量: {x1:.2f}") print(f"二级任务量: {x2:.2f}") print(f"三级任务量: {x3:.2f}") print(f"最短总工期: {T:.2f}")跑出来的结果和我们手算一致。你可以把p1、p2、p3改成任何产能组合,脚本会立刻告诉你新的最优分工方案。这个脚本我经常在项目群里直接甩出去,谁想讨论参数就自己改自己跑,省得反复解释。
5. 实际项目里的坑,我踩过的那些
5.1 只算效率不算切换成本,理论最优直接作废
有个词叫“切换成本”:从一个任务切到另一个任务,需要换模、换配置、重新上手、场景切换,这些都会产生损耗。模型里的效率是“稳定状态下的单位时间产出”,但如果你把大量任务频繁切给某一条产线,它切换的次数就会增加,实际产能根本达不到理论值。
我踩过这个坑。当时按效率比例算出一个分工方案,结果执行时发现产线换型太频繁,实际产出比理论低了将近15%。后来我在目标函数里加了一个“切换损耗系数”:任务包数量越少、连续性越强,损耗越小。在这个基础上重新算,方案就变得更有可操作性。
所以拿到理论最优解之后,先问自己一句:这个方案会让某个单元频繁切换吗?如果会,把任务包适当合并,宁可用稍微长一点的工期,也要换来稳定执行。
5.2 把峰值产能当成了可持续产能
还有一个特别常见的问题:用某个单元“状态最好、资源全给”时的峰值产能来建模。比如某个团队在特别顺的时候一周能交付10个需求,你就拿10当产能常数,结果任务分下去,第二周就疲了。
我的建议是取一个保守的可持续产能,比如近30天的中位数或80分位,而不是最大单日产能。宁可模型给出的工期偏长一点,最后提前完成,也好过拍脑袋压任务然后延期。排产不是数学竞赛,你不需要为了“最优解”好看而牺牲交付稳定性。
5.3 强单元过载带来的可靠性风险
“强者多干”这个方向是对的,但别把它推到极端。当某个单元分配的任务量占比过高,它就成了整个系统的单点。一旦它出故障、断电、人员请假,整个项目立刻暴露在风险里。
我在实际分工时通常会给最强单元加一个“可靠性上限”。这个上限不一定是物理上限,而是你心理上能接受的“单点风险阈值”。比如虽然理论上它能承担50%的任务,但我最多只给它压到40%,剩下10%留作缓冲或分给次强单元。这会让总工期稍微变长一点点,但换来了整体抗风险能力的显著提升。
“拒绝平均主义”不代表“无限压榨强者”。更准确的理解是:按能力梯度合理分配,同时给系统留出安全余量。
5.4 现场常见问题速查表
| 问题 | 可能原因 | 处理方式 |
|---|---|---|
| 模型给的最优解现场做不到 | 忽略了切换成本、在制品上限、人员排班约束 | 补充现实约束后重新求解,不要硬推方案 |
| 算出来是小数,任务不能拆 | 连续模型天然会产生小数 | 向下取整再手工微调,或用整数规划/贪心法 |
| 各单元都说“我这边干不完” | 产能数据取的是峰值,而非可持续值 | 换成保守产能,重新核算 |
| 任务量一变,原方案失效 | 没有做敏感性分析 | 用脚本参数化,随时重算 |
| 分配结果“太偏心”,执行层不认可 | 只给了结论,没给推导过程 | 把“同时完工”的推导和对比数据贴出来 |
5.5 两个可复用的检查动作
最后分享两个我每次排产都会做的检查动作。
第一,反方向验算。把算出来的任务量回代进每个单元的完工时间里,看是不是真的“同时完工”。如果不是,说明约束漏了或者算错了。
第二,浮动测试。把每个产能参数分别上下浮动10%,重新跑一遍模型,看分工方案变化大不大。如果浮动一点结果就剧烈变化,说明方案很脆弱,需要重新评估稳定性。
这两个动作花不了几分钟,却能拦住大多数低级错误。尤其对于“按效率比例分配”这种看起来简单的问题,很多人容易自信到跳过验算,结果一上线就出问题。
最后聊两句我自己的体会。这套“拒绝平均主义”的分配逻辑,我在不少场合都实践过,最直观的感受是:当方案带着推导过程和对比数据讲给执行层听的时候,接受度反而比“每个人都分一点”高得多。人们其实不怕干活多,怕的是“干多干少一个样、快了还要等慢的”那种付出被浪费的感觉。你把总工期能缩短多少、每个人为什么拿这个量,用数据和算式摆清楚,协调起来会顺畅很多。如果你也想在自己的项目里用这个方法,我的建议是从一个最小的两单元任务开始验算一遍公式,再慢慢加单元、加约束、加工具。等这套思路变成习惯,你会发现大部分看似复杂的排期问题,底层都是同一道例题11.5。