JSprit:剩余车辆时未分配的工作
Posted
技术标签:
【中文标题】JSprit:剩余车辆时未分配的工作【英文标题】:JSprit : unasigned jobs while there are vehicles remaning 【发布时间】:2016-05-26 17:50:43 【问题描述】:我正在使用 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);
我注意到,如果我更改固定工作的日期,分配给这一天的车辆将始终满员,而工作未分配。
【问题讨论】:
我建议您查看包含job priority 的最新版本。但是,我认为您关于有车辆可用的说法是不正确的。您每天只有一辆车可用,并且没有优先级,jsprit 没有理由偏爱您的固定约会而不是您分配的具有多个时间窗口的其他工作 我同意你的看法,当我的工作未分配车辆时,星期二整天忙于其他工作。尽管如此,为什么jsprit没有为这些工作分配另一辆车而使用vehicleTue来完成固定工作1? 【参考方案1】:实际上,我认为您对可能导致问题的服务时间窗口的定义过于复杂。当没有可用车辆时,Jsprit 无法分配服务。因此,我将继续像您一样每天定义您的车辆,但我会使用.addTimeWindow(32400, 583200)
创建每个工作。每个只能维修一次,并且只能在车辆可用时进行维修,因此它可以满足您寻找的目的,而无需花费大量时间来处理时间窗口。
如果固定承诺比一周中任何时候都可以处理的事情更重要,那么现在是测试job priorities 新功能的好时机:)
【讨论】:
以上是关于JSprit:剩余车辆时未分配的工作的主要内容,如果未能解决你的问题,请参考以下文章