案例087 先送那个容易给差评的

案例087 先送那个容易给差评的

摘要:8 单外卖里有 2 位 4.2 分(易怒)客户、6 位 4.9 分客户,一条差评赔 20 元,差评概率随超时线性上升(\(0.08+0.03\ell\) 与 \(0.01+0.005\ell\))。以位置变量 \(x_{i,k}\)、完成时刻 \(C_k\)、超时量 \(L_i\) 建 8 单顺序指派模型,目标为差评期望 + 3.5 元/单超时罚款。线性概率使目标对 \(L_i\) 的导数为常数,得到风险单价 4.2 分 0.60 元/min、4.9 分 0.10 元/min,相差 6 倍。Gurobi 求得最优顺序 O1 → O7 → O4 → O6 → O3 → O8 → O2 → O5,总成本 31.5 元(差评期望 21.0、罚款 10.5),里程 28.64 km;与 \(8!\) 全枚举的最优值相差 0.0000 元。对照实验:低分优先 49.9 元、里程最短 36.8 元,差评期望分别多 4.4 与 5.3 元。承诺收紧 1 min 的数值影子价恰好读出 0.60 与 0.10 元;赔付额扫描给出换序临界值 \(f^*=35.30\) 元。

关键词:风险加权调度;差评期望;风险单价;顺序指派;影子价格;帕累托前沿

1 问题提出

8 单外卖、一台车、一个晚上。8 位客户里有 2 位历史评分只有 4.2 分(容易给差评),另外 6 位是 4.9 分的老好人。一条差评赔 20 元。评分不是标签,它决定一条概率曲线:4.2 分客户超时 \(\ell\) 分钟时给差评的概率是 \(0.08+0.03\ell\),4.9 分客户只有 \(0.01+0.005\ell\)。

顺序决定一切。第一位客户 8 分钟后就吃上了饭,最后一位要等 67 分钟。把谁放在前面,等于把谁的"超时时钟"往后拨。这里头有一个容易被忽略的换算:一条差评 20 元,则 4.2 分客户每多超时 1 分钟,期望赔付增加 \(0.60\) 元;4.9 分客户只增加 \(0.10\) 元。前者的"每分钟风险"是后者的 6 倍——这才是"先送那个容易给差评的"的量化理由,而不是一句服务态度。

但先送易怒客户要多跑路。里程得折成时间才能和风险比:绕 1 km 多花 1.5 min,落在 4.2 分客户身上值 \(0.90\) 元,落在 4.9 分客户身上只值 \(0.15\) 元。同一公里,送给易怒客户的"路费"是送给稳定客户的 6 倍——所以为低分客户绕的路,每公里不超过 \(0.90\) 都划算;为高分客户绕路则几乎永远不划算。

2 数学模型

设客户集 \(\mathcal{N}=\{1,\dots,8\}\),\(0\) 为站点;\(t_{ij}\) 为行程时间(min),\(s=3\) 为单点服务时间,\(D_i\) 为承诺时刻,\(f=20\) 为一条差评的赔付额。客户 \(i\) 的差评概率取线性式

\[P_i(L_i)=a_i+b_iL_i,\qquad a_i=\begin{cases}0.08,&\text{评分 4.2}\\ 0.01,&\text{评分 4.9}\end{cases},\qquad b_i=\begin{cases}0.03,&\text{评分 4.2}\\ 0.005,&\text{评分 4.9}\end{cases}\tag{1} \]

决策变量:\(x_{i,k}\in\{0,1\}\) 表示客户 \(i\) 排在第 \(k\) 位;\(C_k\ge 0\) 为第 \(k\) 位的完成时刻;\(A_k\ge 0\) 为进入第 \(k\) 位的行程时间;\(L_i\ge 0\) 为客户 \(i\) 的超时量;\(u_i\in\{0,1\}\) 表示该单是否超时。

顺序指派。 每位客户恰占一个位置、每个位置恰放一位客户:

\[\sum_{k=1}^{8}x_{i,k}=1\ (\forall i),\qquad \sum_{i=1}^{8}x_{i,k}=1\ (\forall k)\tag{2} \]

时间递推。 第 \(k\) 段的行程时间由相邻两位客户决定,用大 \(M\) 从位置变量里"取"出来:

\[A_1\ge t_{0i}-M_A(1-x_{i,1}),\qquad A_k\ge t_{ij}-M_A(2-x_{i,k-1}-x_{j,k})\tag{3} \]

\[C_1=s+A_1,\qquad C_k=C_{k-1}+s+A_k,\qquad k=2,\dots,8\tag{4} \]

式 (3) 只有一对 \((i,j)\) 的下标同时成立,\(A_k\) 取到真实行程;式 (4) 是等式,完成时刻被完全钉死,不需要子回路消除。

超时量与超时指示。

\[L_i\ \ge\ C_k-D_i-M_C(1-x_{i,k}),\qquad L_i\ \le\ \bar C\,u_i,\qquad \forall i,k\tag{5} \]

目标。 差评期望与超时罚款同价相加:

\[\min\ Z=f\sum_{i\in\mathcal{N}}(a_i+b_iL_i)+3.5\sum_{i\in\mathcal{N}}u_i+0.001\sum_{k=1}^{8}A_k\tag{6} \]

式 (6) 的第一项对 \(L_i\) 求导就是风险单价 \(\rho_i=f b_i\):4.2 分客户 \(0.60\) 元/min,4.9 分客户 \(0.10\) 元/min,比值 6。它与里程项同量纲(元),可以直接比较;而 3.5 元的固定罚款是阶跃的,与分钟数无关。

3 Gurobi 求解实现

模型共 64 个 \(x_{i,k}\)、8 个 \(C_k\)、8 个 \(A_k\)、8 个 \(L_i\)、8 个 \(u_i\)。式 (6) 完全线性——\(P_i(L_i)\) 是线性的,直接展开即可,不需要任何 0-1 变量做分段,这是本题与案例 086 阶梯罚款最大的区别。

bb = np.array([B0[i] * (kb if LOW[i] else 1.0) for i in range(N)])
risk = fine * gp.quicksum(A0[i] + bb[i] * L[i] for i in range(N))
pen  = TH   * gp.quicksum(u)
mile = gp.quicksum(Ak) / SPD
m.setObjective(risk + pen + 0.001 * mile, GRB.MINIMIZE)

最后一项 \(0.001\sum A_k\) 是字典序近似:量级 0.043 元,远小于最小的风险单价 0.10 元/min,改不动排序,只在总成本相同的方案里挑一条最省油的路,保证报告的路径可复现。

大 \(M\)。 \(M_C\) 取任何路径的完成时刻上界 \(\bar C=127.3\) min(最长 8 段行程 + 8 次服务),\(M_A\) 取最长单段 \(+1\)。都按"最坏路径"估,不留 \(10^6\)。

校验。 8 单只有 \(8!=40320\) 种排列,全部穷举一遍即可得到精确响应面。Gurobi 的 \(Z^*\) 与穷举最优值之差为 0.0000 元(数值零),说明大 \(M\) 与位置递推没有切掉任何可行排法;后文的参数扫描因此全部由穷举完成,单次不到 0.1 s。

影子价的算法。 模型给出的是排序,不是价格。要读出"早送一分钟值多少",把客户 \(i\) 的承诺时刻单独收紧 1 min 重解,目标增量就是该单的数值影子价:

表 1 各单承诺收紧 1 min 的目标增量(数值影子价)

订单 评分 风险单价 / (元/min) 收紧 1 min 的增量 / 元
O8 4.2 0.60 +0.600
O5 4.9 0.10 +0.100
O2 4.9 0.10 +0.100
O6 4.9 0.10 +0.000
O7 4.9 0.10 +0.000
O4 4.2 0.60 +0.000
O1 4.9 0.10 +0.000
O3 4.9 0.10 +0.000

3 单的影子价非零:4.2 分的 O8 读出 \(0.60\) 元/min、4.9 分的 O2 读出 \(0.10\) 元/min,与式 (6) 的解析导数逐位吻合。影子价为 0 的单说明它有余量,承诺再收紧 1 min 也不用改顺序——余量本身就是钱,只是这钱退不回来。

4 结果分析

4.1 结构:风险单价与概率曲线

按最优顺序排列的各单风险单价(柱)与差评概率截距(菱形,右轴)

图 1 把"评分"翻译成钱:红柱是 4.2 分客户、绿柱是 4.9 分客户,柱高即 \(\rho_i=f b_i\)。两类客户之间是 6 倍的断崖——0.60 对 0.10 元/min,中间没有任何过渡。菱形(右轴)是概率截距 \(a_i\):易怒客户即使一秒不超时也有 8% 的差评概率,折 1.60 元;稳定客户只有 1%,折 0.20 元。截距是沉没的、斜率才是可优化的,所以排程该盯的是柱高,不是菱形。

超时分钟—超时引致的期望风险散点(两条虚线为两类客户的风险单价)

图 2 是"斜率证据":红点贴在 0.6 元/min 的陡线上,绿点贴在 0.1 元/min 的平线上。最优解里超时最多的是 O2(34.5 min,4.9 分),全部 74.8 min 超时中 56.4 min(75%)落在 4.9 分客户身上,只有 18.3 min 落在 4.2 分的 O8 身上。超时不是被消灭了,而是按 6:1 的价格被重新分配了——模型不是不给易怒客户超时,而是尽量少给。

三种排程的成本对比:低分优先 / 里程最短 / 模型最优

图 3 是本案的教学核心。三种排法:低分优先(两位 4.2 分客户顶到最前、其余按承诺时刻)总成本 49.9 元;里程最短(纯 TSP)36.8 元,其中差评期望 26.3 元;模型最优 31.5 元。里程最短多付的 5.3 元全部落在差评期望上(+5.3 元),超时单数一单不差。低分优先则两头都吃亏:多跑 12.78 km,超时罚款多 14.0 元(多 4 单超时)、差评期望多 4.4 元。方向对、力度过猛:最优解没有把两位都顶上去,而是把近的 O4 排到第 3 位、把远的 O8 留在第 6 位。

4.2 时序与灵敏度

最优顺序下 8 单的完成时刻与超时量(橙线为累计成本,右轴)

图 4 摊开时间轴:方块是承诺、圆点是完成、杆长是超时。最优顺序 O1 → O7 → O4 → O6 → O3 → O8 → O2 → O5,跑 28.64 km、用时 67 min、3 单超时。橙线是累计成本,它的局部斜率就是当前这一单的风险单价——经过 4.2 分客户时陡、经过 4.9 分客户时缓,钱是在哪几段流走的一眼可见。

差评赔付额 20—40 元对最优总成本(左轴)与超时单数(右轴)的影响

图 5 把赔付额从 20 抬到 40 元:总成本从 31.6 涨到 52.1 元。有意思的是超时单数从 3 增到 4、总超时分钟从 74.8 增到 85.8,成本却仍算"更优"——新增的分钟全压在 4.9 分客户身上,同时 4.2 分客户的超时从 18.3 min 压到 12.2 min。赔得越贵,模型越愿意"多挨一单 3.5 元、多超几分钟,但每一分钟都超在便宜客户身上"。临界点 \(f^*=35.30\) 元——低于它最优解是 O1 → O7 → O4 → O6 → O3 → O8 → O2 → O5,高于它切换成 O2 → O8 → O3 → O6 → O4 → O7 → O1 → O5。这就是"罚款额决定敢不敢迟到"的量化版本:临界值由固定罚款 3.5 元与 \(f b_i\) 的相对大小决定。

差评赔付额 × 4.2 分客户概率斜率 的总成本热图(星号为本案参数)

图 6 把两个参数一起动:纵轴赔付额 20—40 元,横轴 4.2 分客户斜率倍数 0.5—3。最热的角落是"又贵又易怒":\(f=40\)、斜率 3 倍时总成本 68.8 元,是本案 31.6 元的 2.2 倍。等值线是斜的,说明赔付额与斜率以乘积 \(f\cdot b\) 起作用——两者对目标可以互相替代,真正决定排序的是乘积(风险单价),不是其中任何一个。

斜率倍数 0.5—3 对总成本的影响(三条赔付额曲线)

图 7 单独切一刀:横轴只动斜率。三条线随 \(f\) 抬高而整体上移(\(f=20\) 时全程涨 17.1 元,\(f=40\) 时涨 27.2 元),形状几乎不变。这解释了一个运营事实:调高赔付标准只能整体抬高成本,改变不了"先送谁"的逻辑;要改变逻辑,只能改斜率,也就是改客户结构——把 4.2 分客户服务成 4.9 分,比事后加罚款有效得多。

4.3 前沿、分布与贡献

里程预算—最小总成本的前沿(阴影为里程不可行的预算区间)

图 8 换个问法:给定里程预算 \(\bar L\),最少要花多少。预算卡在 25.74 km(纯 TSP 最短里程)时成本 36.8 元;每放宽 1 km 成本跳降一档,到 29.74 km 落到 31.5 元后不再下降。前沿先降后平:25.7→29.7 km 这段是"花钱买风险",1 km 约值 1.32 元;再往后多跑的路一分钱也换不回。真正的杠杆不在里程,而在结构——把 4.2 分客户换成 4.9 分,或者把承诺时刻整体后移。

一晚差评条数的分布(柱:两个方案)与累计分布(线,右轴)

图 9 用 4000 次蒙特卡洛把概率摊开:最优方案平均 1.05 条差评、24% 的晚上一条都没有;里程最短方案平均 1.32 条、"完美夜晚"只剩 5%。均值只差 0.27 条(26%),但零差评的概率差了 5.2 倍,两条以上的概率从 25% 升到 33%。这正是期望值优化的软肋:它压均值,而骑手真正怕的是偶尔炸一次。

各单成本分解:基础风险、超时增量风险与超时固定罚款

图 10 按单拆开:灰是截距(无论准不准都要付的 1.6 或 0.2 元),红是超时增量,蓝是 3.5 元固定罚款。两位 4.2 分客户合计贡献 56% 的成本,却只占单量的 25%。成本高度集中:O8 一人就吃掉 11.0 元(35%),来自那 18.3 min 超时;而它排在第 6 位,前面 5 单的行程已经把到达时刻钉死——顺序救不了它,只能改承诺或加运力。

4.4 为什么"6 倍"不是修辞,但也不是唯一标尺

把式 (6) 对 \(L_i\) 求导,得 \(\partial Z/\partial L_i=f b_i\),与排序无关、与时间无关,是一个常数。这意味着"早送 1 分钟"的价值在两类客户之间恒为 6:1,不随路况变化。表 1 的数值影子价印证了这一点:4.2 分客户读到 \(0.60\)、4.9 分客户读到 \(0.10\),误差在 \(10^{-3}\) 量级。

但"按 \(\rho\) 从大到小排"只是启发式,它默认"提前一位是免费的"。真实代价是里程:把一单往前插,要绕进更靠前的路段,多跑的路按 1.5 min/km 折算。本案把两位 4.2 分客户硬顶到最前,正是忽略了这个代价——多跑 12.78 km、多付 18.4 元。所以正确的优先级指数是

\[\pi_i=\rho_i\cdot\Delta t_i-c_{km}\cdot\Delta d_i\tag{7} \]

其中 \(\Delta t_i\) 是提前一位能省下的分钟数(约等于它前面那些单的行程时间之和),\(c_{km}\) 是绕路时间的风险折价。\(\rho\) 相同看距离、距离相同看 \(\rho\)——本案的 O1 → O7 → O4 → O6 → O3 → O8 → O2 → O5 就是这条指数排出来的:O4 近、单价又高,排到第 3 位;O8 在城东最远处,要把它提前得先绕过去再折回,低分优先方案实测多跑 12.8 km(19 min),这笔时间账超过它能省下的 11.0 元,于是宁可让它在第 6 位超 18.3 min。这就是"先送那个容易给差评的"的完整版本:先送,除非路太远。

5 结论

(1)风险单价是定价基准:4.2 分客户 0.60 元/min、4.9 分客户 0.10 元/min,比值 6,由 \(f b_i\) 直接给出,与路况、单量、承诺时刻都无关。但排序不能只看它,还要减去绕路的时间代价(式 7)。
(2)最优顺序 O1 → O7 → O4 → O6 → O3 → O8 → O2 → O5,总成本 31.5 元(差评期望 21.0 + 超时罚款 10.5),里程 28.64 km、3 单超时,其中 75% 的超时分钟压在 4.9 分客户身上。
(3)三方案对比:低分优先 49.9 元(多跑 12.78 km)、里程最短 36.8 元、模型最优 31.5 元。里程最短省了 2.90 km,代价是差评期望多 5.3 元——里程是连续量、差评是概率量,两者不同价,必须同进目标。
(4)临界赔付额 \(f^*=35.30\) 元:低于它最优解是 O1 → O7 → O4 → O6 → O3 → O8 → O2 → O5(3 单超时、合计 74.8 min);高于它切换成 O2 → O8 → O3 → O6 → O4 → O7 → O1 → O5(4 单超时、合计 85.8 min)。赔得越贵,模型越敢"多超一单、少超几分钟"。
(5)线性 \(P_i(L)\) 让模型保持纯 MIP,无需分段 0-1;而 3.5 元的固定罚款是阶跃的,两者同价相加后超时成了"按单价分配"的资源——这就是为什么最优解把 75% 的超时分钟留给了便宜客户。

参考文献

[1] Gurobi Optimization LLC. Gurobi Optimizer Reference Manual[Z]. 2024.

[2] Kellerer H, Pferschy U, Pisinger D. Knapsack Problems[M]. Berlin: Springer, 2004.

[3] Martello S, Toth P. Knapsack Problems: Algorithms and Computer Implementations[M]. Chichester: John Wiley & Sons, 1990.

[4] 司守奎, 孙玺菁. 数学建模算法与应用[M]. 3 版. 北京: 国防工业出版社, 2021.


复现方式:python batch_087.py;数据与结果见同目录 .csv / .json,图见 figs/

posted @ 2026-11-11 18:06  小青蛙电气  阅读(3)  评论(0)    收藏  举报