You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在JSprit中构建乘客乘车时长惩罚软约束?

在JSprit中实现乘客额外乘车时长软约束的方案

首先明确:MaxTimeInVehicleConstraint和你要的需求不是一回事,它是硬约束,限制的是车辆的总行驶时长,而非乘客的乘车时长,更不涉及额外时长的计算。

下面是具体的实现思路和步骤:

核心思路

我们需要自定义一个软约束,核心逻辑是:

  1. 提前计算每个乘客的「直达时长」(从上车点直接到下车点的最短时间);
  2. 在路线规划时,统计每个乘客的实际乘车时长(上车时间到下车时间的差值);
  3. 计算实际时长与直达时长的差值(即额外乘车时长),对这部分时长设置惩罚系数,最终将总惩罚加入路线成本中。

如果觉得计算额外时长太麻烦,也可以退而求其次,直接对乘客的总乘车时长设置惩罚——逻辑类似,只是跳过「减直达时长」的步骤,直接统计所有乘客的实际乘车时长总和并乘以惩罚系数。

具体实现步骤

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 00:06:19