我正在使用JSprit解决多个旅行商问题的时间窗口:我有一个推销员,必须在一周内尽快访问n个客户,时间有限。我已经为每天配置了一辆车,并为每个客户配置了服务。
它实际上正在运作,但我找不到办法处理特定时间计划的工作。例如,推销员有15个客户可以看,但是在周一下午3点,他预约了预约。考虑到这种约束,我希望优化路线。
我曾尝试使用与约会相对应的时间窗口指定作业,但通常不会分配作业,但仍有车辆可用! 我已经尝试将所需的技能放在工作中和附在正确的日子和相同结果的车辆上。
所以这是我的问题: - 我不能在特定时间设置义务(不能分配)。 - 我不明白为什么在没有使用车辆的情况下有未分配的工作。
编辑:我修改我的代码以增加优先级并将时间窗口扩大为建议。 没有运气,工作1仍未分配。
public class test {
private ArrayList<Service> serviceList; // list of services
private ArrayList<Location> locationList; // list of location
private Location dep; // departure & arrival for each day
private String parameters;
// distances between locations
private double times[][];
public test()
{
this.serviceList = new ArrayList<>();
this.locationList = new ArrayList<>();
this.parameters = "";
}
public void test(int nb)
{
this.times = new double[nb+1][nb+1];
// collect data from json for the list of geocoded services
try (InputStream in = new ByteArrayInputStream(json.getBytes(StandardCharsets.UTF_8));
JsonReader read = javax.json.Json.createReader(in)) {
JsonObject obj = read.readObject();
JsonArray results = obj.getJsonArray("clients");
// departure
dep = Location.Builder.newInstance().setId("0").setIndex(0).setCoordinate(Coordinate.newInstance(48.88626, 2.22135)).build();
// parameters for OSRM request
parameters = "&loc=48.88626, 2.22135";
locationList.add(dep);
// get locations from json
for(int i=0; i<nb;i++)
{
JsonObject result = results.getValuesAs(JsonObject.class).get(i);
Location l = Location.Builder.newInstance().setId(Integer.toString(i+1)).setIndex(i+1).setCoordinate(Coordinate.newInstance(Double.valueOf(result.getString("latitude")), Double.valueOf(result.getString("longitude")))).build();
locationList.add(l);
// parameters for OSRM request
parameters += "&loc="+result.getString("latitude")+","+result.getString("longitude");
}
// Vehicles
VehicleTypeImpl.Builder vehicleTypeBuilder = VehicleTypeImpl.Builder.newInstance("vehicleType");
VehicleType vehicleType = vehicleTypeBuilder.setCostPerTransportTime(1).setCostPerWaitingTime(1).setCostPerServiceTime(1).setCostPerDistance(0).build();
// vehicule for each day
VehicleImpl vehicleMon = VehicleImpl.Builder.newInstance("vehicleMon").setType(vehicleType).setStartLocation(dep).setEarliestStart(32400).setLatestArrival(64800).build();
VehicleImpl vehicleTue = VehicleImpl.Builder.newInstance("vehicleTue").setType(vehicleType).setStartLocation(dep).setEarliestStart(118800).setLatestArrival(151200).build();
VehicleImpl vehicleWed = VehicleImpl.Builder.newInstance("vehicleWed").setType(vehicleType).setStartLocation(dep).setEarliestStart(205200).setLatestArrival(237600).build();
VehicleImpl vehicleThu = VehicleImpl.Builder.newInstance("vehicleThu").setType(vehicleType).setStartLocation(dep).setEarliestStart(291600).setLatestArrival(324000).build();
VehicleImpl vehicleFry = VehicleImpl.Builder.newInstance("vehicleFry").setType(vehicleType).setStartLocation(dep).setEarliestStart(378000).setLatestArrival(410400).build();
VehicleImpl vehicleSat = VehicleImpl.Builder.newInstance("vehicleSat").setType(vehicleType).setStartLocation(dep).setEarliestStart(464400).setLatestArrival(496800).build();
VehicleImpl vehicleSun = VehicleImpl.Builder.newInstance("vehicleSun").setType(vehicleType).setStartLocation(dep).setEarliestStart(550800).setLatestArrival(583200).build();
// the job with a specific time window which represent a scheduled appointment
Service serviceTW = Service.Builder.newInstance(Integer.toString(1))
.setName("1")
.addTimeWindow(124200, 131400) // 2 hours window
.setPriority(1) // high priority
.setServiceTime(3600).setLocation(locationList.get(1)).build();
serviceList.add(serviceTW);
// jobs
for(int i=1; i<nb; i++) // we skip the first one which is above
{
Service service = Service.Builder.newInstance(Integer.toString(i+1))
.setName(Integer.toString(i+1))
.setPriority(3) // low priority
.addTimeWindow(32400, 583200) // time window for all week
.setServiceTime(3600).setLocation(locationList.get(i+1)).build();
serviceList.add(service);
}
// matrix time
// retrieve traveling time from OSRM
getOSRM_times();
VehicleRoutingTransportCostsMatrix.Builder costMatrixBuilder = VehicleRoutingTransportCostsMatrix.Builder.newInstance(true);
for(int i=0; i<nb ;i++)
{
for(int j=0; j<nb+1;j++)
{
costMatrixBuilder.addTransportTime(Integer.toString(i), Integer.toString(j), times[i][j]/10);
}
}
// adding vehicles
VehicleRoutingTransportCosts costMatrix = costMatrixBuilder.build();
VehicleRoutingProblem.Builder vrpBuilder = VehicleRoutingProblem.Builder.newInstance();
vrpBuilder
.addVehicle(vehicleMon)
.addVehicle(vehicleTue)
.addVehicle(vehicleWed)
.addVehicle(vehicleThu)
.addVehicle(vehicleFry)
.addVehicle(vehicleSat)
.addVehicle(vehicleSun)
.addAllJobs(serviceList).setFleetSize(FleetSize.FINITE).setRoutingCost(costMatrix);
// Problem/Solution
VehicleRoutingProblem problem = vrpBuilder.build();
VehicleRoutingAlgorithm algorithm = Jsprit.createAlgorithm(problem);
Collection<VehicleRoutingProblemSolution> solutions = algorithm.searchSolutions();
VehicleRoutingProblemSolution bestSolution = Solutions.bestOf(solutions);
File dir = new File("output");
// if the directory does not exist, create it
if (!dir.exists()){
System.out.println("creating directory ./output");
boolean result = dir.mkdir();
if(result) System.out.println("./output created");
}
// RESULTS
//xml
new VrpXMLWriter(problem, solutions).write("output/problem-with-solution.xml");
// console
SolutionPrinter.print(problem, bestSolution, Print.VERBOSE);
// image
new Plotter(problem,bestSolution).setLabel(Plotter.Label.ID).plot("output/solution.png", "solution");
}catch (IOException ex) {Logger.getLogger(Json.class.getName()).log(Level.SEVERE, null, ex);}
}
我注意到如果我改变了我固定工作的日期,那么分配到这一天的车辆将永远满载且工作未分配。
答案 0 :(得分:1)
实际上,我认为您过度复杂了可能导致问题的服务时间窗口的定义。当没有车辆可用时,Jsprit无法分配服务。所以,我会继续像你一样每天定义你的车辆,但我会用.addTimeWindow(32400, 583200)
创建每个工作。每辆车只能维修一次,只能在车辆可用的情况下进行维修,因此它可以满足您的需求,而不需要花费大量的时间窗口。
如果固定承诺比本周任何时候可以处理的事情更重要,那么现在是测试job priorities的新功能的好时机:)