演算法描述
目前在物流,企業用工等領域,都有著大量的通過演算法對接到的訂單進行智慧分配的需求。本文模擬的是使用者下訂單,然後商家接到訂單,由配送人員進行派送的場景。在實際的應用中類似於百度外賣等有著非常多的實際應用。這種問題因為演算法的複雜度太高,很難在短的時間週期內求解成功,所以有了像遺傳演算法,退火演算法等啟發式演算法,以便在短的時間內能夠求出近似的最優解。
本文模擬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
配送人員的配送順序。
總結
目前演算法中使用的是歐式距離來進行兩個點之間距離的計算,運算的時間隨著單子數量的增加會加長,並且在某些情況下還會有很多的約束條件,這些都是下一步演算法需要改進的重點。