此示例展示了如何使用过渡属性来优先选择由同一辆车在一个时间段内完成附近取件和送货的路线。如需详细了解转换属性,请参阅使用转换属性对业务逻辑进行建模。
在此示例中:
- 运单 A、B 和 C 的送货地点在同一条道路上,彼此相距不远。
- 后续还会交付更多功能。
- 配送没有指定的送货时长。
- 无论拜访时间表如何,车辆都需要在这条路上行驶两次:一次是在早上从车库出发的路上,另一次是在晚上返回的路上。
- 无论 A、B 和 C 何时执行,路线的总行程距离和时长始终相同。

在这种情况下,对于仅使用每小时费用和每公里费用的请求,优化后的路线可能会在上午处理 A 和 B,在晚上处理 C,而解决方案的费用与同时处理这三个任务的费用相同。
每公里费用(有阈值)
如需对附近的到访进行分组,您首先需要选择一个阈值距离。这是您认为距离较近的两次访问之间的最大距离。此示例使用 100 米的阈值,大致相当于市区中的一个街区。您可以提高或降低阈值,以满足您的业务需求和司机的偏好。
如需将彼此相距 100 米以内的附近地点归为一组,您可以为每次转换的前 100 米设置较高的费用,并为转换的任何额外距离设置较低的费用。由于前 100 米的费用最高,因此优化器通过使用短于 100 米阈值的过渡路段来实现最大程度的节省,即使这意味着延长路线的总长度。
如需设置费用,请向 ShipmentModel.transition_attributes 添加一个具有以下属性的新条目:
- 如需匹配所有可能的转换,请选择模型中未使用的标记,例如
UNUSED_TAG。将TransitionAttributes.excluded_src_tag和TransitionAttributes.excluded_dst_tag设置为此标记。 - 使用阈值距离和费用设置
TransitionAttributes.distance_limit:- 将
DistanceLimit.soft_max_meters设置为所选阈值。 - 将
DistanceLimit.cost_per_kilometer_below_soft_max设置为低于阈值的每公里费用。 - 将
DistanceLimit.cost_per_kilometer_above_soft_max设置为超出阈值后每公里的费用。
- 将
{
"model": {
"transitionAttributes": [
{
"excluded_dst_tag": "UNUSED_TAG",
"excluded_src_tag": "UNUSED_TAG",
"distanceLimit": {
"softMaxMeters": 100,
"costPerKilometerBelowSoftMax": 50,
"costPerKilometerAboveSoftMax": 1,
}
}
]
}
}
任何运单或车辆都不得使用标记 #unused_tag# 来匹配所有可能的转换。如需了解详情,请参阅如何匹配所有访问请求。
“费用高但低于阈值”的工作原理
本部分展示了低于和高于阈值的费用如何影响示例场景中不同解决方案的总体费用。
解决方案 1:在去程中执行 A、B,在回程中执行 C
在此解决方案中,配送任务被拆分到这条道路的两次穿越中。 其中两个在第一次遍历时传送,其余一个在第二次遍历时传送。共有 5 种过渡效果:
| 过渡 | 距离 | 低于阈值 | 高于阈值 | ||
|---|---|---|---|---|---|
| 距离 | 费用 | 距离 | 费用 | ||
| depot →A | 1000 米 | 100 m | 5 | 900 米 | 0.9 |
| A→B | 50 米 | 50 米 | 2.5 | 0 米 | 0 |
| B→other | 1030 米 | 100 m | 5 | 930 米 | 0.93 |
| other→C | 1000 米 | 100 m | 5 | 900 米 | 0.9 |
| C→depot | 1080 米 | 100 m | 5 | 980 米 | 0.98 |
| 总计 | 450 米 | 22.5 | 3710 米 | 3.71 | |
总费用按每公里两项费用的总和计算:
- 低于阈值的每公里费用 (50) 乘以低于阈值的总行驶距离(450 米 = 0.45 公里),
- 超出阈值的每公里费用 (1) 乘以超出阈值的总行驶距离(3710 米 = 3.71 公里)。
因此,总费用为 0.45 * 50 + 3.71 * 1 = 22.5 + 3.71 = 26.21。
解决方案 2:在去程中执行 A、B、C,在回程中不执行任何操作
在此解决方案中,与解决方案 1 不同,所有三批货物都会在一次道路遍历期间“作为一个组”交付。在另一次遍历中,车辆根本不会停下来。同样,有 5 个过渡,但它们的长度和组成不同:
| 过渡 | 距离 | 低于阈值 | 高于阈值 | ||
|---|---|---|---|---|---|
| 距离 | 费用 | 距离 | 费用 | ||
| depot →A | 1000 米 | 100 m | 5 | 900 米 | 0.9 |
| A→B | 50 米 | 50 米 | 2.5 | 0 米 | 0 |
| B→C | 30 米 | 30 米 | 1.5 | 0 米 | 0 |
| C→其他 | 1000 米 | 100 m | 5 | 900 米 | 0.9 |
| other→depot | 2080 米 | 100 m | 5 | 1980 米 | 1.98 |
| 总计 | 380 米 | 19 | 3780 米 | 3.78 | |
使用与解决方案 1 相同的计算方法,总费用为 0.38 * 50 + 3.78 * 1 = 19 + 3.78 = 22.78,在一个时间段内执行所有访问的费用低于分两组执行的费用。您可以通过增加 DistanceLimit.cost_per_kilometer_below_soft_max 来强化此效果。
为什么低于阈值的每公里费用无法正常运作
由于您希望优先选择短过渡,而不是长过渡,因此您可能会倾向于为长过渡提供较高的每公里费用,并为短过渡保持较低的每公里费用。但实际上,这会产生相反的效果:由于过渡的前 100 米最便宜,优化器会优先选择接近或超过 100 米的过渡,从而最大限度地利用这些“便宜”的米数。
您可以在两个示例解决方案中看到这种效果。如果您交换阈值以下和以上的每公里费用,路线费用会发生变化:
| 费用高于阈值 | 费用高,低于阈值 | |||
|---|---|---|---|---|
| 解决方案 1 | 解决方案 2 | 解决方案 1 | 解决方案 2 | |
| 低于阈值的知识管理 | 0.45 | 0.38 | 0.45 | 0.38 |
| 每公里费用低于阈值 | 1.00 | 1.00 | 50.00 | 50.00 |
| 高于阈值的公里数 | 3.71 | 3.78 | 3.71 | 3.78 |
| 超出阈值后每公里的费用 | 50.00 | 50.00 | 1.00 | 1.00 |
| 总费用 | 185.95 | 189.38 | 26.21 | 22.78 |
对于每个版本,两种解决方案中总费用较低者会以粗体突出显示。您可以看到,当您使用高于阈值的高费用时,访问分组的路线的总费用现在更高,这与您想要实现的目标相反。