
本文介绍通过自定义目标函数(而非约束)的方式,在 jsprit 路径优化中对乘客在车内的额外停留时间施加软性惩罚,从而优先选择“先远后近”的上下客顺序,提升乘客体验。
本文介绍通过自定义目标函数(而非约束)的方式,在 jsprit 路径优化中对乘客在车内的额外停留时间施加软性惩罚,从而优先选择“先远后近”的上下客顺序,提升乘客体验。
在 JSprit 中实现“乘客在车内时间越短越好”的业务目标,关键在于理解其设计哲学:软性偏好应优先通过目标函数建模,而非硬/软约束。直接编写 HardActivityConstraint 或 SoftActivityConstraint 不仅复杂、易出错,还可能干扰求解器的启发式搜索效率;而重写 ObjectiveFunction 则简洁、可控、且与 JSprit 的成本驱动优化机制天然契合。
核心思路是:对每位乘客在车内的每一秒(从上车到下车)施加线性惩罚成本,且该成本随车上乘客总数动态累积——即:当车辆载有 n 名乘客时,每行驶一秒产生的额外时间成本为 n × costPerPassengerSecond。这自然鼓励算法优先服务距离目的地更远的乘客(避免其长时间滞留),再接附近乘客,最后统一送达,从而隐式达成“先远后近、同车多送”的优质调度逻辑。
以下是一个生产就绪的目标函数实现示例:
将一个或多个视频上传至YouTube。当用户想“上传到YouTube”“发YouTube”“批量上传”“upload to YouTube”“post videos to YouTube”或发布到YouTube时使用此技能。
public double getCosts(VehicleRoutingProblemSolution solution) {
double costPerPassengerSecond = 0.001; // 每乘客每秒惩罚成本(可调参)
double totalCosts = 0.0;
for (VehicleRoute route : solution.getRoutes()) {
// 加入车辆固定成本(如启用)
totalCosts += route.getVehicle().getType().getVehicleCostParams().fix;
TourActivity prevAct = route.getStart();
// 遍历所有服务活动(Pickup / Delivery)
for (TourActivity act : route.getActivities()) {
// 累计路段运输成本(距离/时间基础成本)
double transportCost = vrp.getTransportCosts()
.getTransportCost(prevAct.getLocation(), act.getLocation(),
prevAct.getEndTime(), route.getDriver(), route.getVehicle());
totalCosts += transportCost;
// 【关键】累加乘客时间惩罚:
// 当前车上人数 × 路段行驶时间 × 单位时间惩罚
double travelTime = vrp.getTransportCosts()
.getTransportTime(prevAct.getLocation(), act.getLocation(),
prevAct.getEndTime(), route.getDriver(), route.getVehicle());
int currentPassengers = route.getActivities().stream()
.filter(a -> a instanceof Pickup || a instanceof Delivery)
.mapToInt(a -> a.getSize().get(0))
.sum(); // 注意:此计算为简化示意;实际应按实时载量动态维护
// 更推荐在遍历时增量更新:见下方说明
totalCosts += costPerPassengerSecond * currentPassengers * travelTime;
// 更新车上人数(假设 Pickup 增 +1,Delivery 减 -1)
if (act instanceof Pickup) {
currentPassengers += act.getSize().get(0);
} else if (act instanceof Delivery) {
currentPassengers -= act.getSize().get(0);
}
prevAct = act;
}
// 补充:从最后一项活动到终点的返程成本(含时间惩罚)
double finalTravelTime = vrp.getTransportCosts()
.getTransportTime(prevAct.getLocation(), route.getEnd().getLocation(),
prevAct.getEndTime(), route.getDriver(), route.getVehicle());
totalCosts += vrp.getTransportCosts()
.getTransportCost(prevAct.getLocation(), route.getEnd().getLocation(),
prevAct.getEndTime(), route.getDriver(), route.getVehicle());
totalCosts += costPerPassengerSecond * currentPassengers * finalTravelTime;
}
return totalCosts;
}⚠️ 重要注意事项:
-
动态载量维护:示例中
currentPassengers应在遍历过程中实时增减(如 Pickup +1、Delivery −1),而非每次重新统计,否则性能下降显著; -
单位一致性:确保
costPerPassengerSecond与运输时间单位(秒/毫秒)匹配,建议统一使用秒; -
参数调优:该惩罚系数需与车辆固定成本、单位距离成本等量级协调(例如设为
0.001表示每乘客每分钟惩罚约0.06单位成本),可通过小规模测试集校准; -
不替代基础约束:仍需保留
CapacityConstraint、TimeWindowConstraint等硬性业务规则,目标函数仅用于引导偏好排序。
总结而言,放弃“约束思维”,拥抱“成本建模”,是 JSprit 高级调度场景下的最佳实践。通过将乘客体验指标(车内时间)直接转化为可微分、可比较、可缩放的目标成本项,你不仅能精准控制优化方向,还能保持求解器的鲁棒性与扩展性——这才是面向真实世界的智能路径规划之道。

















