算法描述
目前在物流,企业用工等领域,都有着大量的通过算法对接到的订单进行智能分配的需求。本文模拟的是用户下订单,然后商家接到订单,由配送人员进行派送的场景。在实际的应用中类似于百度外卖等有着非常多的实际应用。这种问题因为算法的复杂度太高,很难在短的时间周期内求解成功,所以有了像遗传算法,退火算法等启发式算法,以便在短的时间内能够求出近似的最优解。

本文模拟8个骑士,40个订单和40个商家进行计算。
算法描述
目前在物流,企业用工等领域,都有着大量的通过算法对接到的订单进行智能分配的需求。首先设计bean,商家接到订单的bean,这里需要商家和订单的坐标信息。
public class BusinessmenAndOrder {
private String id;//job标号
private String businessX;//商家坐标x
private String businessy;//商家坐标y
private String orderx;//订单坐标x
private String ordery;//订单坐标y
public BusinessmenAndOrder(){
}
public BusinessmenAndOrder(String id,String businessX,String businessy,String orderx,String ordery){
this.id=id;
this.businessX=businessX;
this.businessy=businessy;
this.orderx=orderx;
this.ordery=ordery;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getBusinessX() {
return businessX;
}
public void setBusinessX(String businessX) {
this.businessX = businessX;
}
public String getBusinessy() {
return businessy;
}
public void setBusinessy(String businessy) {
this.businessy = businessy;
}
public String getOrderx() {
return orderx;
}
public void setOrderx(String orderx) {
this.orderx = orderx;
}
public String getOrdery() {
return ordery;
}
public void setOrdery(String ordery) {
this.ordery = ordery;
}
}
模拟40个商家接到40个订单,其中商家和订单都可以是同一坐标位置,后续会考虑将坐标相近的位置进行聚类计算。
// 得到取件坐标和送达坐标(相当于商家和订单)
List<BusinessmenAndOrder> baoList = new ArrayList<BusinessmenAndOrder>();
BusinessmenAndOrder bao1 = new BusinessmenAndOrder("商家订单1", "137.45", "554.19", "138.83", "557.56");
BusinessmenAndOrder bao2 = new BusinessmenAndOrder("商家订单2", "840.6", "532.46", "139.37", "558.54");
BusinessmenAndOrder bao3 = new BusinessmenAndOrder("商家订单3", "157.38", "573.96", "359.96", "794.99");
BusinessmenAndOrder bao4 = new BusinessmenAndOrder("商家订单4", "120.45", "604.18", "193.49", "571.18");
BusinessmenAndOrder bao5 = new BusinessmenAndOrder("商家订单5", "137.50", "564.16", "357.33", "784.03");
BusinessmenAndOrder bao6 = new BusinessmenAndOrder("商家订单6", "131.90", "570.68", "118.60", "597.49");
BusinessmenAndOrder bao7 = new BusinessmenAndOrder("商家订单7", "150.21", "557.68", "140.98", "573.79");
BusinessmenAndOrder bao8 = new BusinessmenAndOrder("商家订单8", "115.13", "585.43", "115.01", "596.83");
BusinessmenAndOrder bao9 = new BusinessmenAndOrder("商家订单9", "120.13", "641.55", "141.20", "593.01");
BusinessmenAndOrder bao10 = new BusinessmenAndOrder("商家订单10", "150.06", "575.78", "355.11", "784.26");
BusinessmenAndOrder bao11 = new BusinessmenAndOrder("商家订单11", "114.50", "588.19", "119.16", "583.38");
BusinessmenAndOrder bao12 = new BusinessmenAndOrder("商家订单12", "137.28", "563.04", "355.24", "790.13");
BusinessmenAndOrder bao13 = new BusinessmenAndOrder("商家订单13", "151.04", "609.23", "179.37", "570.66");
BusinessmenAndOrder bao14 = new BusinessmenAndOrder("商家订单14", "113.73", "583.88", "139.83", "538.04");
BusinessmenAndOrder bao15 = new BusinessmenAndOrder("商家订单15", "185.75", "551.86", "187.18", "543.81");
BusinessmenAndOrder bao16 = new BusinessmenAndOrder("商家订单16", "116.39", "500.71", "173.63", "583.75");
BusinessmenAndOrder bao17 = new BusinessmenAndOrder("商家订单17", "112.73", "534.10", "108.60", "604.41");
BusinessmenAndOrder bao18 = new BusinessmenAndOrder("商家订单18", "137.66", "590.41", "139.63", "577.69");
BusinessmenAndOrder bao19 = new BusinessmenAndOrder("商家订单19", "161.38", "558.01", "161.83", "560.24");
BusinessmenAndOrder bao20 = new BusinessmenAndOrder("商家订单20", "137.28", "561.46", "143.01", "616.94");
BusinessmenAndOrder bao21 = new BusinessmenAndOrder("商家订单21", "128.48", "570.59", "135.86", "597.34");
BusinessmenAndOrder bao22 = new BusinessmenAndOrder("商家订单22", "136.45", "564.88", "139.55", "558.59");
BusinessmenAndOrder bao23 = new BusinessmenAndOrder("商家订单23", "128.96", "568.38", "139.37", "559.08");
BusinessmenAndOrder bao24 = new BusinessmenAndOrder("商家订单24", "133.41", "581.50", "357.09", "789.24");
BusinessmenAndOrder bao25 = new BusinessmenAndOrder("商家订单25", "134.83", "573.03", "136.14", "578.20");
BusinessmenAndOrder bao26 = new BusinessmenAndOrder("商家订单26", "146.76", "516.91", "165.01", "483.61");
BusinessmenAndOrder bao27 = new BusinessmenAndOrder("商家订单27", "127.41", "634.78", "152.30", "598.38");
BusinessmenAndOrder bao28 = new BusinessmenAndOrder("商家订单28", "137.48", "549.33", "141.20", "561.10");
BusinessmenAndOrder bao29 = new BusinessmenAndOrder("商家订单29", "178.75", "595.65", "204.53", "619.03");
BusinessmenAndOrder bao30 = new BusinessmenAndOrder("商家订单30", "97.72", "564.24", "640.4", "556.01");
BusinessmenAndOrder bao31 = new BusinessmenAndOrder("商家订单31", "628.48", "260.59", "125.86", "497.34");
BusinessmenAndOrder bao32 = new BusinessmenAndOrder("商家订单32", "336.45", "364.88", "439.55", "458.59");
BusinessmenAndOrder bao33 = new BusinessmenAndOrder("商家订单33", "228.96", "268.38", "239.37", "559.08");
BusinessmenAndOrder bao34 = new BusinessmenAndOrder("商家订单34", "153.41", "291.50", "257.09", "489.24");
BusinessmenAndOrder bao35 = new BusinessmenAndOrder("商家订单35", "136.83", "363.03", "276.14", "168.20");
BusinessmenAndOrder bao36 = new BusinessmenAndOrder("商家订单36", "446.76", "216.91", "465.01", "283.61");
BusinessmenAndOrder bao37 = new BusinessmenAndOrder("商家订单37", "527.41", "674.78", "158.30", "528.38");
BusinessmenAndOrder bao38 = new BusinessmenAndOrder("商家订单38", "637.48", "549.33", "191.20", "591.10");
BusinessmenAndOrder bao39 = new BusinessmenAndOrder("商家订单39", "637.48", "549.33", "291.20", "491.10");
BusinessmenAndOrder bao40 = new BusinessmenAndOrder("商家订单40", "637.48", "549.33", "691.20", "291.10");
然后将商家和订单加入到集合中。
baoList.add(bao1);
baoList.add(bao2);
baoList.add(bao3);
baoList.add(bao4);
baoList.add(bao5);
baoList.add(bao6);
baoList.add(bao7);
baoList.add(bao8);
baoList.add(bao9);
baoList.add(bao10);
baoList.add(bao11);
baoList.add(bao12);
baoList.add(bao13);
baoList.add(bao14);
baoList.add(bao15);
baoList.add(bao16);
baoList.add(bao17);
baoList.add(bao18);
baoList.add(bao19);
baoList.add(bao20);
baoList.add(bao21);
baoList.add(bao22);
baoList.add(bao23);
baoList.add(bao24);
baoList.add(bao25);
baoList.add(bao26);
baoList.add(bao27);
baoList.add(bao28);
baoList.add(bao29);
baoList.add(bao30);
baoList.add(bao31);
baoList.add(bao32);
baoList.add(bao33);
baoList.add(bao34);
baoList.add(bao35);
baoList.add(bao36);
baoList.add(bao37);
baoList.add(bao38);
baoList.add(bao39);
baoList.add(bao40);
然后将商家和订单加入到集合中。
接着模拟骑士,假设骑士位置散落在不同的坐标点。
public class Driver {
private String driverId;//骑士编号
private String driverx;//骑士坐标x
private String drivery;//骑士坐标y
public Driver(){
}
public Driver(String driverId,String driverx,String drivery){
this.driverId=driverId;
this.driverx=driverx;
this.drivery=drivery;
}
public String getDriverId() {
return driverId;
}
public void setDriverId(String driverId) {
this.driverId = driverId;
}
public String getDriverx() {
return driverx;
}
public void setDriverx(String driverx) {
this.driverx = driverx;
}
public String getDrivery() {
return drivery;
}
public void setDrivery(String drivery) {
this.drivery = drivery;
}
}
模拟8个骑士
// 模拟骑士
List<Driver> driveList = new ArrayList<Driver>();
Driver driver1 = new Driver("driveA", "137.50", "575.78");
Driver driver2 = new Driver("driveB", "151.04", "534.10");
Driver driver3 = new Driver("driveC", "137.28", "558.01");
Driver driver4 = new Driver("driveD", "127.41", "634.78");
Driver driver5 = new Driver("driveE", "46.76", "285.75");
Driver driver6 = new Driver("driveF", "176.76", "485.75");
Driver driver7 = new Driver("driveG", "386.76", "405.75");
Driver driver8 = new Driver("driveH", "246.76", "685.75");
driveList.add(driver1);
driveList.add(driver2);
driveList.add(driver3);
driveList.add(driver4);
driveList.add(driver5);
driveList.add(driver6);
driveList.add(driver7);
driveList.add(driver8);
将模拟数据代入算法
List<DistributionSep> distList = new ArrayList<DistributionSep>();
distList = autoSeperateOrder(baoList, driveList);
for (DistributionSep distribution : distList) {
System.out.println("配送人员 "+distribution.getDriverName());
List<OrderSort> orderSet = distribution.getDist();
for (OrderSort order : orderSet) {
System.out.println(order.getOrder() + " " + order.getPors() + " " + order.getBussinessOrder());
}
}
结果查看
查看运行结果,可知,迭代了1024次,耗时10秒。
2017-06-30 14:49:38,888 INFO [main] problem.VehicleRoutingProblem - setup problem: [fleetSize=FINITE][#jobs=40][#vehicles=8][#vehicleTypes=1][transportCost=[name=crowFlyCosts]][activityCosts=com.graphhopper.jsprit.core.problem.cost.WaitingTimeCosts@47d90b9e]
2017-06-30 14:49:38,970 INFO [main] algorithm.VehicleRoutingAlgorithm - algorithm starts: [maxIterations=2000]
2017-06-30 14:49:38,972 INFO [main] algorithm.InsertionInitialSolutionFactory - create initial solution
2017-06-30 14:49:39,823 INFO [main] algorithm.VehicleRoutingAlgorithm - iterations start
2017-06-30 14:49:39,825 INFO [main] algorithm.VehicleRoutingAlgorithm$Counter - iterations 1
2017-06-30 14:49:39,843 INFO [main] algorithm.VehicleRoutingAlgorithm$Counter - iterations 2
2017-06-30 14:49:39,910 INFO [main] algorithm.VehicleRoutingAlgorithm$Counter - iterations 4
2017-06-30 14:49:40,032 INFO [main] algorithm.VehicleRoutingAlgorithm$Counter - iterations 8
2017-06-30 14:49:40,142 INFO [main] algorithm.VehicleRoutingAlgorithm$Counter - iterations 16
2017-06-30 14:49:41,014 INFO [main] algorithm.VehicleRoutingAlgorithm$Counter - iterations 32
2017-06-30 14:49:41,194 INFO [main] algorithm.VehicleRoutingAlgorithm$Counter - iterations 64
2017-06-30 14:49:41,728 INFO [main] algorithm.VehicleRoutingAlgorithm$Counter - iterations 128
2017-06-30 14:49:42,439 INFO [main] algorithm.VehicleRoutingAlgorithm$Counter - iterations 256
2017-06-30 14:49:43,599 INFO [main] algorithm.VehicleRoutingAlgorithm$Counter - iterations 512
2017-06-30 14:49:45,723 INFO [main] algorithm.VehicleRoutingAlgorithm$Counter - iterations 1024
2017-06-30 14:49:49,703 INFO [main] algorithm.VehicleRoutingAlgorithm - iterations end at 2000 iterations
2017-06-30 14:49:49,703 INFO [main] algorithm.VehicleRoutingAlgorithm - took 10.732 seconds
分配的结果及cost计算结果为:
+--------------------------------------------------------------------------------------------------------------------------------+
| detailed solution |
+---------+----------------------+-----------------------+-----------------+-----------------+-----------------+-----------------+
| route | vehicle | activity | job | arrTime | endTime | costs |
+---------+----------------------+-----------------------+-----------------+-----------------+-----------------+-----------------+
| 1 | driveA | start | - | undef | 0 | 0 |
| 1 | driveA | pickupShipment | 商家订单25 | 4 | 4 | 4 |
| 1 | driveA | deliverShipment | 商家订单25 | 9 | 9 | 9 |
| 1 | driveA | pickupShipment | 商家订单21 | 20 | 20 | 20 |
| 1 | driveA | pickupShipment | 商家订单23 | 22 | 22 | 22 |
| 1 | driveA | pickupShipment | 商家订单22 | 30 | 30 | 30 |
| 1 | driveA | pickupShipment | 商家订单20 | 34 | 34 | 34 |
| 1 | driveA | deliverShipment | 商家订单22 | 38 | 38 | 38 |
| 1 | driveA | deliverShipment | 商家订单23 | 38 | 38 | 38 |
| 1 | driveA | pickupShipment | 商家订单18 | 70 | 70 | 70 |
| 1 | driveA | deliverShipment | 商家订单18 | 82 | 82 | 82 |
| 1 | driveA | deliverShipment | 商家订单21 | 102 | 102 | 102 |
| 1 | driveA | deliverShipment | 商家订单20 | 123 | 123 | 123 |
| 1 | driveA | end | - | 123 | undef | 123 |
+---------+----------------------+-----------------------+-----------------+-----------------+-----------------+-----------------+
| 2 | driveB | start | - | undef | 0 | 0 |
| 2 | driveB | pickupShipment | 商家订单7 | 24 | 24 | 24 |
| 2 | driveB | deliverShipment | 商家订单7 | 42 | 42 | 42 |
| 2 | driveB | pickupShipment | 商家订单19 | 68 | 68 | 68 |
| 2 | driveB | deliverShipment | 商家订单19 | 70 | 70 | 70 |
| 2 | driveB | pickupShipment | 商家订单15 | 96 | 96 | 96 |
| 2 | driveB | deliverShipment | 商家订单15 | 104 | 104 | 104 |
| 2 | driveB | end | - | 104 | undef | 104 |
+---------+----------------------+-----------------------+-----------------+-----------------+-----------------+-----------------+
| 3 | driveC | start | - | undef | 0 | 0 |
| 3 | driveC | pickupShipment | 商家订单1 | 4 | 4 | 4 |
| 3 | driveC | pickupShipment | 商家订单28 | 9 | 9 | 9 |
| 3 | driveC | deliverShipment | 商家订单1 | 17 | 17 | 17 |
| 3 | driveC | deliverShipment | 商家订单28 | 21 | 21 | 21 |
| 3 | driveC | pickupShipment | 商家订单12 | 26 | 26 | 26 |
| 3 | driveC | pickupShipment | 商家订单5 | 27 | 27 | 27 |
| 3 | driveC | pickupShipment | 商家订单6 | 35 | 35 | 35 |
| 3 | driveC | pickupShipment | 商家订单8 | 58 | 58 | 58 |
| 3 | driveC | pickupShipment | 商家订单11 | 61 | 61 | 61 |
| 3 | driveC | deliverShipment | 商家订单8 | 69 | 69 | 69 |
| 3 | driveC | deliverShipment | 商家订单6 | 73 | 73 | 73 |
| 3 | driveC | deliverShipment | 商家订单11 | 87 | 87 | 87 |
| 3 | driveC | pickupShipment | 商家订单24 | 101 | 101 | 101 |
| 3 | driveC | pickupShipment | 商家订单10 | 119 | 119 | 119 |
| 3 | driveC | pickupShipment | 商家订单3 | 127 | 127 | 127 |
| 3 | driveC | pickupShipment | 商家订单29 | 157 | 157 | 157 |
| 3 | driveC | deliverShipment | 商家订单29 | 192 | 192 | 192 |
| 3 | driveC | deliverShipment | 商家订单10 | 415 | 415 | 415 |
| 3 | driveC | deliverShipment | 商家订单5 | 418 | 418 | 418 |
| 3 | driveC | deliverShipment | 商家订单24 | 423 | 423 | 423 |
| 3 | driveC | deliverShipment | 商家订单12 | 425 | 425 | 425 |
| 3 | driveC | deliverShipment | 商家订单3 | 432 | 432 | 432 |
| 3 | driveC | end | - | 432 | undef | 432 |
+---------+----------------------+-----------------------+-----------------+-----------------+-----------------+-----------------+
| 4 | driveD | start | - | undef | 0 | 0 |
| 4 | driveD | pickupShipment | 商家订单27 | 0 | 0 | 0 |
| 4 | driveD | pickupShipment | 商家订单9 | 10 | 10 | 10 |
| 4 | driveD | pickupShipment | 商家订单13 | 55 | 55 | 55 |
| 4 | driveD | deliverShipment | 商家订单27 | 66 | 66 | 66 |
| 4 | driveD | deliverShipment | 商家订单9 | 78 | 78 | 78 |
| 4 | driveD | pickupShipment | 商家订单14 | 107 | 107 | 107 |
| 4 | driveD | pickupShipment | 商家订单30 | 132 | 132 | 132 |
| 4 | driveD | deliverShipment | 商家订单14 | 182 | 182 | 182 |
| 4 | driveD | deliverShipment | 商家订单13 | 233 | 233 | 233 |
| 4 | driveD | pickupShipment | 商家订单37 | 596 | 596 | 596 |
| 4 | driveD | deliverShipment | 商家订单30 | 760 | 760 | 760 |
| 4 | driveD | pickupShipment | 商家订单40 | 768 | 768 | 768 |
| 4 | driveD | pickupShipment | 商家订单39 | 768 | 768 | 768 |
| 4 | driveD | pickupShipment | 商家订单38 | 768 | 768 | 768 |
| 4 | driveD | pickupShipment | 商家订单2 | 971 | 971 | 971 |
| 4 | driveD | deliverShipment | 商家订单40 | 1255 | 1255 | 1255 |
| 4 | driveD | pickupShipment | 商家订单31 | 1325 | 1325 | 1325 |
| 4 | driveD | deliverShipment | 商家订单39 | 1734 | 1734 | 1734 |
| 4 | driveD | deliverShipment | 商家订单38 | 1875 | 1875 | 1875 |
| 4 | driveD | deliverShipment | 商家订单2 | 1936 | 1936 | 1936 |
| 4 | driveD | deliverShipment | 商家订单37 | 1972 | 1972 | 1972 |
| 4 | driveD | deliverShipment | 商家订单31 | 2017 | 2017 | 2017 |
| 4 | driveD | end | - | 2017 | undef | 2017 |
+---------+----------------------+-----------------------+-----------------+-----------------+-----------------+-----------------+
| 5 | driveE | start | - | undef | 0 | 0 |
| 5 | driveE | pickupShipment | 商家订单35 | 119 | 119 | 119 |
| 5 | driveE | pickupShipment | 商家订单34 | 192 | 192 | 192 |
| 5 | driveE | pickupShipment | 商家订单33 | 271 | 271 | 271 |
| 5 | driveE | deliverShipment | 商家订单35 | 382 | 382 | 382 |
| 5 | driveE | pickupShipment | 商家订单36 | 559 | 559 | 559 |
| 5 | driveE | deliverShipment | 商家订单36 | 628 | 628 | 628 |
| 5 | driveE | deliverShipment | 商家订单34 | 921 | 921 | 921 |
| 5 | driveE | deliverShipment | 商家订单33 | 993 | 993 | 993 |
| 5 | driveE | end | - | 993 | undef | 993 |
+---------+----------------------+-----------------------+-----------------+-----------------+-----------------+-----------------+
| 6 | driveF | start | - | undef | 0 | 0 |
| 6 | driveF | pickupShipment | 商家订单26 | 43 | 43 | 43 |
| 6 | driveF | deliverShipment | 商家订单26 | 81 | 81 | 81 |
| 6 | driveF | pickupShipment | 商家订单16 | 133 | 133 | 133 |
| 6 | driveF | pickupShipment | 商家订单17 | 166 | 166 | 166 |
| 6 | driveF | deliverShipment | 商家订单17 | 237 | 237 | 237 |
| 6 | driveF | pickupShipment | 商家订单4 | 249 | 249 | 249 |
| 6 | driveF | deliverShipment | 商家订单16 | 306 | 306 | 306 |
| 6 | driveF | deliverShipment | 商家订单4 | 329 | 329 | 329 |
| 6 | driveF | end | - | 329 | undef | 329 |
+---------+----------------------+-----------------------+-----------------+-----------------+-----------------+-----------------+
| 7 | driveG | start | - | undef | 0 | 0 |
| 7 | driveG | pickupShipment | 商家订单32 | 65 | 65 | 65 |
| 7 | driveG | deliverShipment | 商家订单32 | 204 | 204 | 204 |
| 7 | driveG | end | - | 204 | undef | 204 |
+--------------------------------------------------------------------------------------------------------------------------------+
这样就可以得到
配送人员 driveA
1 取 商家订单25
2 送 商家订单25
3 取 商家订单21
4 取 商家订单23
5 取 商家订单22
6 取 商家订单20
7 送 商家订单22
8 送 商家订单23
9 取 商家订单18
10 送 商家订单18
11 送 商家订单21
12 送 商家订单20
配送人员 driveB
1 取 商家订单7
2 送 商家订单7
3 取 商家订单19
4 送 商家订单19
5 取 商家订单15
6 送 商家订单15
配送人员 driveC
1 取 商家订单1
2 取 商家订单28
3 送 商家订单1
4 送 商家订单28
5 取 商家订单12
6 取 商家订单5
7 取 商家订单6
8 取 商家订单8
9 取 商家订单11
10 送 商家订单8
11 送 商家订单6
12 送 商家订单11
13 取 商家订单24
14 取 商家订单10
15 取 商家订单3
16 取 商家订单29
17 送 商家订单29
18 送 商家订单10
19 送 商家订单5
20 送 商家订单24
21 送 商家订单12
22 送 商家订单3
配送人员 driveD
1 取 商家订单27
2 取 商家订单9
3 取 商家订单13
4 送 商家订单27
5 送 商家订单9
6 取 商家订单14
7 取 商家订单30
8 送 商家订单14
9 送 商家订单13
10 取 商家订单37
11 送 商家订单30
12 取 商家订单40
13 取 商家订单39
14 取 商家订单38
15 取 商家订单2
16 送 商家订单40
17 取 商家订单31
18 送 商家订单39
19 送 商家订单38
20 送 商家订单2
21 送 商家订单37
22 送 商家订单31
配送人员 driveE
1 取 商家订单35
2 取 商家订单34
3 取 商家订单33
4 送 商家订单35
5 取 商家订单36
6 送 商家订单36
7 送 商家订单34
8 送 商家订单33
配送人员 driveF
1 取 商家订单26
2 送 商家订单26
3 取 商家订单16
4 取 商家订单17
5 送 商家订单17
6 取 商家订单4
7 送 商家订单16
8 送 商家订单4
配送人员 driveG
1 取 商家订单32
2 送 商家订单32
配送人员的配送顺序。
总结
目前算法中使用的是欧式距离来进行两个点之间距离的计算,运算的时间随着单子数量的增加会加长,并且在某些情况下还会有很多的约束条件,这些都是下一步算法需要改进的重点。