如何在JSprit中构建乘客乘车时长惩罚软约束?
在JSprit中实现乘客额外乘车时长软约束的方案
首先明确:MaxTimeInVehicleConstraint和你要的需求不是一回事,它是硬约束,限制的是车辆的总行驶时长,而非乘客的乘车时长,更不涉及额外时长的计算。
下面是具体的实现思路和步骤:
核心思路
我们需要自定义一个软约束,核心逻辑是:
- 提前计算每个乘客的「直达时长」(从上车点直接到下车点的最短时间);
- 在路线规划时,统计每个乘客的实际乘车时长(上车时间到下车时间的差值);
- 计算实际时长与直达时长的差值(即额外乘车时长),对这部分时长设置惩罚系数,最终将总惩罚加入路线成本中。
如果觉得计算额外时长太麻烦,也可以退而求其次,直接对乘客的总乘车时长设置惩罚——逻辑类似,只是跳过「减直达时长」的步骤,直接统计所有乘客的实际乘车时长总和并乘以惩罚系数。
具体实现步骤
1. 预计算乘客直达时长
用JSprit的RoutingTransportCosts提前算出每个乘客上车点到下车点的最短时间,存在Map里方便后续调用:
Map<String, Double> directRideTimeMap = new HashMap<>(); // shipments是你的所有乘客订单集合 for (Shipment shipment : shipments) { // 计算直达时长,第三个参数是出发时间(这里填0不影响最短时间计算) double directTime = transportCosts.getTransportTime( shipment.getPickupLocation(), shipment.getDeliveryLocation(), 0.0, null, null ); directRideTimeMap.put(shipment.getId(), directTime); }
2. 自定义软约束类
实现JSprit的SoftConstraint接口,重写getCosts方法来计算额外时长的总惩罚:
public class ExtraRideTimePenaltyConstraint implements SoftConstraint { private final RoutingTransportCosts transportCosts; private final Map<String, Double> directRideTimeMap; private final double penaltyPerMinute; // 每分钟额外时长的惩罚值,可自定义 public ExtraRideTimePenaltyConstraint(RoutingTransportCosts transportCosts, Map<String, Double> directRideTimeMap, double penaltyPerMinute) { this.transportCosts = transportCosts; this.directRideTimeMap = directRideTimeMap; this.penaltyPerMinute = penaltyPerMinute; } @Override public double getCosts(VehicleRoutingProblemSolution solution) { double totalPenalty = 0.0; // 遍历每条路线 for (VehicleRoute route : solution.getRoutes()) { // 遍历路线中的所有活动,匹配乘客的上下车点 for (TourActivity activity : route.getActivities()) { if (activity instanceof Pickup) { Pickup pickupAct = (Pickup) activity; String passengerId = pickupAct.getShipment().getId(); double pickupTime = pickupAct.getArrTime(); // 找到对应的下车活动 Delivery deliveryAct = (Delivery) route.getActivities().stream() .filter(act -> act instanceof Delivery && ((Delivery) act).getShipment().getId().equals(passengerId)) .findFirst().orElse(null); if (deliveryAct != null) { double dropoffTime = deliveryAct.getArrTime(); double actualRideTime = dropoffTime - pickupTime; double directTime = directRideTimeMap.getOrDefault(passengerId, 0.0); // 额外时长不能为负数,取与0的最大值 double extraTime = Math.max(0, actualRideTime - directTime); totalPenalty += extraTime * penaltyPerMinute; } } } } return totalPenalty; } }
3. 将约束注册到路由问题中
在构建VehicleRoutingProblem时,把自定义的软约束加进去:
// 假设你已经配置好其他基础参数(车辆、订单、运输成本等) VehicleRoutingProblem vrp = VehicleRoutingProblem.Builder.newInstance() .setFleetSize(VehicleRoutingProblem.FleetSize.FINITE) .addVehicles(vehicles) .addShipments(shipments) .setRoutingCost(transportCosts) // 添加自定义软约束,这里设置每分钟惩罚10,可根据需求调整 .addSoftConstraint(new ExtraRideTimePenaltyConstraint(transportCosts, directRideTimeMap, 10.0)) .build();
替代方案:总乘车时长惩罚
如果不想计算额外时长,直接对总乘车时长设置惩罚,只需修改自定义约束里的计算逻辑:去掉directTime的减法,直接把actualRideTime乘以惩罚系数累加即可,代码改动很小。
内容的提问来源于stack exchange,提问作者Sam Barnum
相关产品推荐
相关产品推荐

