案例 023 同一家店出 5 单:一次排队带来的合并红利
案例 023 同一家店出 5 单:一次排队带来的合并红利
摘要:体育西一家茶餐厅一次出 5 单,取餐只排一次队(9 min),但送餐点分散、份数 1—2、分装 1.5 min/单。本文建立带聚点的 TSP 模型(商家必访一次且先于所有客户),用 Gurobi 求最优,并用 120 种顺序全枚举校验。结果表明:合并取餐路径 13.63 km、总时长 71.9 min,比分开取的 156.0 min 省 84.0 min(54%);省下的主要是 36 min 重复排队。同店单量从 3 增到 8,单均时长由 16.2 min 降到 10.7 min。
关键词:外卖配送;带聚点 TSP;同店拼单;排队时间;Gurobi
1 问题提出
1.1 问题是什么
体育西一家茶餐厅一次出 5 单,取餐只排一次队(9 min),但送餐点分散在 5 个方向,份数 1—2 份、分装 1.5 min/单、交付 2 min/单。
5 单必须全部取到才能出发(一次排队),排一条从出发点到商家、再送完 5 单回出发点的路径,使总时长最小。
表 1 同店五单
| 客户 | 坐标 /km | 份数 | 距商家 /km |
|---|---|---|---|
| K1 | (1.9, 1.4) | 2 | 1.64 |
| K2 | (2.6, 0.5) | 1 | 2.41 |
| K3 | (1.2, 2.6) | 1 | 2.04 |
| K4 | (3.1, 2.1) | 2 | 3.22 |
| K5 | (0.4, -1.5) | 1 | 2.82 |
1.2 为什么要优化
- 排队是固定成本:分开取要排 5 次队(45 min),合并只要 9 min;
- 但合并要"等齐":5 单必须同时出餐,等餐时间变成了硬等待;
- 顺序仍值钱:5 个点有 120 种顺序,最短 13.63 km、最长 21.41 km。
1.3 Gurobi 如何求解
带聚点的 TSP:商家 \(s\) 必须恰好访问一次,且先于所有客户。
模型 49 个变量、49 条约束,0.00 s 求得最优;120 种顺序全枚举作校验。
2 数学模型
2.1 符号说明
\(V=\{0,s\}\cup I\),0 为出发点、\(s\) 为商家、\(I\) 为 5 个客户;\(d_{ij}\) 为路网距离(直线 × 1.15),\(v=18\) km/h;\(\tau_q=9\) min 为一次排队、\(\tau_p=1.5\) min/单 为分装、\(\tau_s=2\) min/单 为交付;\(u_i\) 为顺序变量。
2.2 目标函数
2.3 约束条件
每点一进一出:
子回路消除与"先取餐":
2.4 规模与性质
7 个节点、42 条弧,0-1 变量 42 个、约束 49 条。带聚点 TSP 属 NP-难,但 5 个客户只有 120 种顺序,可全枚举校验;工程上常用的做法是先跑 Clarke-Wright 节约算法得快解、再作 MIPStart。
3 Gurobi 求解实现
3.1 建模要点
for i in range(NN):
m.addConstr(y.sum(i, "*") == 1); m.addConstr(y.sum("*", i) == 1)
for i in range(1, NN): # MTZ 消子回路
for j in range(1, NN):
if i != j:
m.addConstr(u[i] - u[j] + NN * y[i, j] <= NN - 1)
for i in range(2, NN): # 商家必须先于所有客户
m.addConstr(u[1] + 1 <= u[i])
一条实现细节:"先取餐"只用一条 \(u_s+1\le u_i\) 就够,不要写 \(u_s<u_i\) 的严格不等。MIP 里不能出现严格不等式,必须写成整数差至少 1;\(u\) 是连续变量时也成立,因为 \(u_s,u_i\) 在最优解处会被 MTZ 推成整数。
3.2 求解结果
表 2 合并与分开的对比
| 指标 | 分开取(5 趟) | 合并取(1 趟) |
|---|---|---|
| 里程 / km | 30.29 | 13.63 |
| 骑行 / min | 101.0 | 45.4 |
| 排队 / min | 45 | 9 |
| 分装 / min | 0 | 7.5 |
| 总时长 / min | 156.0 | 71.9 |
合并省 84.0 min(54%),其中排队省 36 min、空驶省 56 min,代价是多 7.5 min 的分装。
4 结果分析
4.1 空间结构与份数
图 1 里商家在出发点东北 1.1 km,5 个客户散在三个方向:K1、K2 在东南,K3、K4 在东北,K5 在西南。最优路径是"商家 → K1 → K4 → K3 → K2 → K5 → 出发点",先从近处顺时针收一圈、最后单跑西南角。

图 1 合并取餐的配送路径
图 2 用"距商家距离—份数"看这 5 单:K4 最远(3.22 km)且有 2 份,K5 只需 1 份但要绕到西南。份数与距离并不成正比——这就是为什么最优解不按份数排。

图 2 各客户距商家的距离与份数
4.2 省在哪
图 3 把总时长拆开:分开取 156.0 min,减掉 36 min 的重复排队、减掉 56 min 的空驶,得到合并方案。排队是最大的一块:5 趟各排一次,光排队就 45 min。

图 3 分开取与合并取的时长分解
图 4 用累计曲线比较:合并方案在商家处"停一次、跳 5 级",之后一路小步;分开方案要爬 5 次台阶,每次都先回出发点。两条曲线的差距在第二次排队时就定型了。

图 4 两种方式的累计完成单数
4.3 分装与排队的灵敏度
图 5 扫分装时间:0.5 min/单 时总时长 66.9 min、等待占比 17%;3.0 min/单 时涨到 79.4 min、占比 30%。分装每多 0.5 min/单,总时长就多 2.5 min——5 单的线性叠加。

图 5 总时长与等待占比随分装时间的变化
图 6 同时扫分装与排队:横带随分装加深,纵带随排队加深,等值线是标准的"正交网格"——两个参数完全可加,没有交互。星标(1.5, 9)落在中间色带。

图 6 总时长关于分装与排队时间的热图
4.4 单量、前沿与顺序分布
图 7 把同店单量从 3 加到 8:总时长由 48.7 min 升到 86.0 min,而单均时长从 16.2 min 降到 10.7 min。同店单越多,"一次排队"被摊得越薄——这就是拼单的经济学。

图 7 总时长与单均时长随同店单量的变化
图 8 把排队时间从 4 扫到 15 min:总时长从 66.9 升到 77.9 min,顶部灰区是超过 60 min(一节课)的部分。排队超过 -2 min 就送不进一节课的窗口。

图 8 总时长随排队时间的变化
图 9 是 120 种访问顺序的里程分布:均值 18.22 km、最短 13.63 km、最长 21.41 km。最优顺序比均值省 4.59 km(25%),比最差省 7.78 km。

图 9 120 种访问顺序的里程分布
图 10 把每单的时间拆成"骑行分摊 + 分装 + 交付":交付与分装合计固定,骑行是唯一随顺序变的部分。5 单里骑行只占 63%,固定开销占 37%——所以拼单的收益主要来自摊薄固定开销。

图 10 各单的骑行、分装与交付时间构成
5 结论
- 同店 5 单、一次排队 9 min 下,合并取餐路径 13.63 km、总时长 71.9 min,比分开取的 156.0 min 省 84.0 min(54%)。
- 省钱的主项是排队:5 趟各排一次要 45 min,合并只要 9 min;其次是空驶省 56 min。
- 代价是多 7.5 min 的分装,以及"等齐出餐"的硬等待——分装每多 0.5 min/单,总时长多 2.5 min。
- 同店单量从 3 加到 8,单均时长由 16.2 min 降到 10.7 min,拼单红利随单量摊薄。
- 访问顺序仍值钱:120 种顺序里最优比均值省 4.59 km;"先取餐"用 \(u_s+1\le u_i\) 一条约束即可,不能写严格不等。
参考文献
[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.py;数据与结果见同目录 .csv / .json,图见 figs/

浙公网安备 33010602011771号