ИЛИ инструменты для перезарядки CVRP с использованием одного транспортного средства

#java #or-tools

Вопрос:

Следуя примерам python здесь

Я пытаюсь создать ту же функцию в java, которая заключается в том, чтобы загружать материалы и доставлять их клиентам, и если следующий спрос превышает оставшуюся емкость, перейдите в хранилище и перезагрузите. Все это происходит без каких-либо ограничений по времени, однако в моем случае есть только 1 транспортное средство. Проблема в том, что во многих случаях решение является нулевым.

Например: спрос: [0, -30, -30, -30, 17, 17, 18, 18, 18, 18] при вместимости транспортного средства 30 приводит к нулю. Тем не менее, один и тот же спрос с разной вместимостью транспортного средства прекрасно работает, например -18 , 36 , 48 , 96 .

Первое требование равно 0, потому что это начальное местоположение.

Вот код, который я использую:

 final OrDataModel data = buildDataModel();

RoutingIndexManager manager = new RoutingIndexManager(data.distanceMatrix.length, data.vehicleNumber, data.depot);

RoutingModel routing = new RoutingModel(manager);

final int transitCallbackIndex =
routing.registerTransitCallback((long fromIndex, long toIndex) -> {
  int fromNode = manager.indexToNode(fromIndex);
  int toNode = manager.indexToNode(toIndex);
  return data.distanceMatrix[fromNode][toNode];
});

routing.setArcCostEvaluatorOfAllVehicles(transitCallbackIndex);
routing.addDimension(transitCallbackIndex, 0, 1000000, true, "Distance");
RoutingDimension distanceDimension = routing.getDimensionOrDie("Distance");
distanceDimension.setGlobalSpanCostCoefficient(100);

final int demandCallbackIndex = routing.registerUnaryTransitCallback(
  (long fromIndex) -> {
  // Convert from routing variable Index to user NodeIndex.
  int fromNode = manager.indexToNode(fromIndex);
  return data.demands[fromNode];
});

routing.addDimension(demandCallbackIndex, data.vehicleCapacity, data.vehicleCapacity, true, "Capacity");
RoutingDimension capacityDimension = routing.getDimensionOrDie("Capacity");

//No penalty in case multi-trip visit is skipped when not required
for(int i = 1; i <= data.multiTrips - 1; i  ) {
  int fromNode = manager.indexToNode(i);
  routing.addDisjunction(new long[] { fromNode }, 0);
}

// Setting first solution heuristic.
RoutingSearchParameters searchParameters =
  main.defaultRoutingSearchParameters()
  .toBuilder()
  .setFirstSolutionStrategy(FirstSolutionStrategy.Value.PATH_CHEAPEST_ARC)
  .setLocalSearchMetaheuristic(LocalSearchMetaheuristic.Value.GUIDED_LOCAL_SEARCH)
  .setTimeLimit(Duration.newBuilder().setSeconds(1).build())
  .build();

// Solve the problem.
Assignment solution = routing.solveWithParameters(searchParameters);

if(solution != null) {
   printSolution(data, routing, manager, solution);
} else {
  System.out.print("no results");
}
 
 private static OrDataModel buildDataModel() {
    
    List<Long> volumes = new ArrayList<>();
    List<double[]> locations = new ArrayList<>();
    readCsvFile(volumes, locations);
    
    double totalVolume = LCollectionUtils.sumDoubleProperty(volumes, volume -> volume);
    
    int maxCapacity = 30;
    
    int multiTrips = calculateMultiTrips(totalVolume, maxCapacity);
    
    List<double[]> multiTripLocations = new ArrayList<>();
    for(int i = 0; i < multiTrips; i  ) {
        multiTripLocations.add(new double[] { 24.3653, 54.7136 });
    }
    
    List<double[]> allLocations = new ArrayList<double[]>(multiTripLocations);
    allLocations.addAll(locations);
    double[][] matrix = new double[allLocations.size()][];
    matrix = allLocations.toArray(matrix);
    
    long[][] distanceMatrix = buildDistanceMatrix(matrix, multiTrips);
    long[][] timeMatrix = buildTimeMatrix(matrix, multiTrips);
    long[] demands = fillDemands(multiTrips, matrix.length, volumes, maxCapacity);
    
    long[][] timeWindows = fillTimeWindow(allLocations.size(), multiTrips);
    
    return OrDataModel.from(matrix, distanceMatrix, timeMatrix, demands, allLocations.size(), multiTrips, maxCapacity, timeWindows, totalVolume);
}

private static long[] fillDemands(int minimumMultiTrips, int numberOfLocations, List<Long> allVolumes, long vehicleCapacity) {
    
    long[] demands = new long[numberOfLocations];
    
    //first demand is at depot so 0
    demands[0] = 0;
    
    int index;
    for(index = 1; index < minimumMultiTrips; index  ) {
        demands[index] = (long)-vehicleCapacity;
    }
    
    for(int j = 0; j < allVolumes.size(); j  ) {
        demands[index] = allVolumes.get(j);
        index  ;
    }
    
    return demands;
}


private static int calculateMultiTrips(double totalVolume, int capacity) {
    double remainder = totalVolume % capacity;
    double quotient = totalVolume / capacity;
    double multiTrip = remainder == 0 ? quotient : quotient   1;
    return (int)multiTrip;
}

private static long[][] buildDistanceMatrix(double[][] locations, int minimumMultiTrips) {

    long[][] distanceData = new long[locations.length][locations.length];

    for (int i = 0; i < locations.length; i  ) {
        
        LatLng fromLatLng = new LatLng().lat(locations[i][0]).lng(locations[i][1]);

        for (int j = 0; j < locations.length; j  ) {

            if (i == j) {
                distanceData[i][j] = 0;
            }
            
            else if(i < minimumMultiTrips amp;amp; j < minimumMultiTrips) {
                distanceData[i][j] = 100000;
            }
            
            else {
                LatLng toLatLng = new LatLng().lat(locations[j][0]).lng(locations[j][1]);
                LatLngQueryDataFetcher distanceFetcher = new HaversineDistanceFetcher(30, 1.25);
                sh.locus.pjp.server.model.distance.TimeDistancePair response = distanceFetcher.fetch(fromLatLng, toLatLng);
                distanceData[i][j] = response.getDistance();
            }
        }
    }

    return distanceData;
}
 

Код очень прост. Чтение по запросу, латинскому и длинному из файла csv. Жестко закодировано местоположение домашней базы для этого образца. Построил матрицу расстояний и времени, но только с использованием матрицы расстояний. Что, возможно, я упускаю?

Ответ №1:

примечание. AddDisjunction принимает индекс, а не узел, поэтому в основном ваш код :

     //No penalty in case multi-trip visit is skipped when not required
    for(int i = 1; i <= data.multiTrips - 1; i  ) {
        int fromNode = manager.indexToNode(i);
        routing.addDisjunction(new long[] { fromNode }, 0);
    }
 

это неправильно, вам нужно обратное

     //No penalty in case multi-trip visit is skipped when not required
    for(int i = 1; i <= data.multiTrips - 1; i  ) {
        int fromIndex = manager.nodeToIndex(i);
        routing.addDisjunction(new long[] { fromIndex }, 0);
    }
 

Комментарии:

1. это не помогло. Но все равно спасибо!