├── src ├── main │ ├── resources │ │ └── application.properties │ └── java │ │ └── dispatch │ │ └── demo │ │ ├── dto │ │ ├── DispatchSolution.java │ │ ├── CourierPlan.java │ │ ├── DispatchRequest.java │ │ ├── Location.java │ │ ├── Order.java │ │ ├── Courier.java │ │ ├── Response.java │ │ └── ActionNode.java │ │ ├── core │ │ ├── solver │ │ │ ├── Solver.java │ │ │ ├── packed │ │ │ │ ├── RichAction.java │ │ │ │ ├── uitls.java │ │ │ │ ├── PackedSolver.java │ │ │ │ └── PlanningUiils.java │ │ │ └── BaseSolver.java │ │ ├── route │ │ │ ├── Planner.java │ │ │ ├── TailAppendPlan.java │ │ │ └── PackedTrailPlaner.java │ │ ├── pool │ │ │ ├── CourierPool.java │ │ │ └── OrderPool.java │ │ ├── context │ │ │ └── DispatchContext.java │ │ └── DispatchService.java │ │ ├── DispatchDemoApplication.java │ │ ├── utils │ │ └── DistanceUtils.java │ │ └── controller │ │ └── DispatchController.java └── test │ └── java │ └── dispatch │ └── dispatchdemo │ └── DispatchDemoApplicationTests.java ├── pipline.png ├── .mvn └── wrapper │ ├── maven-wrapper.jar │ ├── maven-wrapper.properties │ └── MavenWrapperDownloader.java ├── .gitignore ├── readme.md ├── pom.xml ├── mvnw.cmd └── mvnw /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /pipline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zen1995/dispatch-demo/HEAD/pipline.png -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zen1995/dispatch-demo/HEAD/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /src/main/java/dispatch/demo/dto/DispatchSolution.java: -------------------------------------------------------------------------------- 1 | package dispatch.demo.dto; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * @author eleme.demo 9 | */ 10 | @Data 11 | public class DispatchSolution { 12 | private List courierPlans; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/dispatch/demo/core/solver/Solver.java: -------------------------------------------------------------------------------- 1 | package dispatch.demo.core.solver; 2 | 3 | import dispatch.demo.dto.CourierPlan; 4 | 5 | import java.util.List; 6 | 7 | public interface Solver { 8 | public List solve(); 9 | 10 | public List getAssignedOrderIds(); 11 | } -------------------------------------------------------------------------------- /src/main/java/dispatch/demo/dto/CourierPlan.java: -------------------------------------------------------------------------------- 1 | package dispatch.demo.dto; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Collections; 7 | import java.util.List; 8 | 9 | /** 10 | * @author eleme.demo 11 | */ 12 | @Data 13 | public class CourierPlan { 14 | private String courierId; 15 | private List planRoutes; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/** 5 | !**/src/test/** 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | 30 | ### VS Code ### 31 | .vscode/ 32 | -------------------------------------------------------------------------------- /src/test/java/dispatch/dispatchdemo/DispatchDemoApplicationTests.java: -------------------------------------------------------------------------------- 1 | package dispatch.dispatchdemo; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class DispatchDemoApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/dispatch/demo/DispatchDemoApplication.java: -------------------------------------------------------------------------------- 1 | package dispatch.demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | /** 7 | * @author eleme.demo 8 | */ 9 | @SpringBootApplication 10 | public class DispatchDemoApplication { 11 | 12 | public static void main(String[] args) { 13 | 14 | SpringApplication.run(DispatchDemoApplication.class, args); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/dispatch/demo/dto/DispatchRequest.java: -------------------------------------------------------------------------------- 1 | package dispatch.demo.dto; 2 | 3 | import lombok.Data; 4 | import lombok.experimental.Accessors; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * @author eleme.demo 10 | */ 11 | @Data 12 | public class DispatchRequest { 13 | private long requestTime; 14 | private String areaId; 15 | private boolean isFirstRound; 16 | private boolean isLastRound; 17 | private List couriers; 18 | private List orders; 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/dispatch/demo/core/solver/packed/RichAction.java: -------------------------------------------------------------------------------- 1 | package dispatch.demo.core.solver.packed; 2 | 3 | import dispatch.demo.dto.ActionNode; 4 | import dispatch.demo.dto.Order; 5 | import lombok.Data; 6 | 7 | @Data 8 | public class RichAction { 9 | int action_type = -1; 10 | ActionNode actionNode; 11 | double cost = -1; 12 | Order order; 13 | public RichAction(ActionNode actionNode,Order order,int action_type){ 14 | this.actionNode = actionNode; 15 | this.order = order; 16 | this.action_type = action_type; 17 | } 18 | 19 | 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/dispatch/demo/dto/Location.java: -------------------------------------------------------------------------------- 1 | package dispatch.demo.dto; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @author eleme.demo 7 | */ 8 | @Data 9 | public class Location { 10 | private Double latitude; 11 | private Double longitude; 12 | @Override 13 | public Location clone(){ 14 | Location loc = new Location(); 15 | loc.setLatitude(latitude); 16 | loc.setLongitude(longitude); 17 | return loc; 18 | } 19 | public double distanceTo(final Location loc){ 20 | return Math.sqrt(Math.pow(loc.getLatitude()-getLatitude(),2)+Math.pow(loc.getLongitude()-getLongitude(),2)); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/dispatch/demo/dto/Order.java: -------------------------------------------------------------------------------- 1 | package dispatch.demo.dto; 2 | 3 | import com.alibaba.fastjson.annotation.JSONField; 4 | import lombok.Data; 5 | 6 | /** 7 | * @author eleme.demo 8 | */ 9 | 10 | @Data 11 | public class Order { 12 | private String areaId; 13 | private String id; 14 | private Location srcLoc; 15 | private Location dstLoc; 16 | private long createTime; 17 | private long promiseDeliverTime; 18 | private long estimatedPrepareCompletedTime; 19 | 20 | /** 21 | * 0 dispatching 22 | * 1 goingRst 23 | * 2 picking 24 | * 3 delivering 25 | * 4 complete 26 | */ 27 | @JSONField(serialize = false) 28 | int status = 0; 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/dispatch/demo/dto/Courier.java: -------------------------------------------------------------------------------- 1 | package dispatch.demo.dto; 2 | 3 | import com.alibaba.fastjson.annotation.JSONField; 4 | import lombok.Data; 5 | import lombok.experimental.Accessors; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | /** 11 | * @author eleme.demo 12 | */ 13 | @Data 14 | public class Courier { 15 | private String areaId; 16 | private String id; 17 | private Location loc; 18 | private Double speed; 19 | private Integer maxLoads; 20 | 21 | @JSONField(serialize = false) 22 | private List planRoutes = new ArrayList<>(0); 23 | 24 | @JSONField(serialize = false) 25 | private List orders = new ArrayList<>(0); 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/dispatch/demo/dto/Response.java: -------------------------------------------------------------------------------- 1 | package dispatch.demo.dto; 2 | 3 | import lombok.Data; 4 | import lombok.NoArgsConstructor; 5 | 6 | /** 7 | * @author eleme.demo 8 | */ 9 | @Data 10 | public class Response { 11 | 12 | private int code; 13 | private T result; 14 | private String message; 15 | 16 | 17 | public Response(T result) { 18 | this.code = 200; 19 | this.result = result; 20 | } 21 | 22 | public Response(int code, T result, String message) { 23 | this.code = code; 24 | this.result = result; 25 | this.message = message; 26 | } 27 | 28 | public Response(int code, T result) { 29 | this.code = code; 30 | this.result = result; 31 | } 32 | 33 | public static Response NewErrResponse(String message) { 34 | return new Response(500,new DispatchSolution(), message); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/dispatch/demo/dto/ActionNode.java: -------------------------------------------------------------------------------- 1 | package dispatch.demo.dto; 2 | 3 | import com.alibaba.fastjson.annotation.JSONField; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | /** 9 | * @author eleme.demo 10 | */ 11 | @Data 12 | @AllArgsConstructor 13 | @NoArgsConstructor 14 | public class ActionNode { 15 | 16 | /** 运单ID */ 17 | private String orderId; 18 | 19 | /** 20 | * 1 到店完成 21 | * 2 取完成 22 | * 3 送完成 23 | */ 24 | private int actionType; 25 | 26 | /** 预计发生时刻 */ 27 | private long actionTime; 28 | 29 | /** 是否已提交 */ 30 | @JSONField(serialize = false) 31 | private boolean isSubmitted = false; 32 | 33 | /** 该动作需提交的时间,如果晚于该时间提交,可能造成评测系统判断骑手无法到达该点 */ 34 | @JSONField(serialize = false) 35 | private long needSubmitTime = -1L; 36 | 37 | public void setNeedSubmitTime(long t){ 38 | needSubmitTime = t; 39 | } 40 | 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/dispatch/demo/core/route/Planner.java: -------------------------------------------------------------------------------- 1 | package dispatch.demo.core.route; 2 | 3 | import dispatch.demo.core.context.DispatchContext; 4 | import dispatch.demo.core.solver.packed.RichAction; 5 | import dispatch.demo.dto.ActionNode; 6 | import dispatch.demo.dto.Courier; 7 | import dispatch.demo.dto.Order; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * @author eleme.demo 13 | * route planner interface 14 | */ 15 | public abstract class Planner { 16 | 17 | /** 18 | * 路径规划 19 | * 20 | * @param courier 给定骑手 21 | * @param order 待规划的单 22 | * @param context 上下文信息 23 | * @return 骑手路径 24 | */ 25 | public List plan(Courier courier, Order order, DispatchContext context){ 26 | assert false; 27 | return null; 28 | } 29 | public List plan(Courier courier, List actinos, DispatchContext context){ 30 | assert false; 31 | return null; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/dispatch/demo/core/pool/CourierPool.java: -------------------------------------------------------------------------------- 1 | package dispatch.demo.core.pool; 2 | 3 | import dispatch.demo.core.context.DispatchContext; 4 | import dispatch.demo.dto.ActionNode; 5 | import dispatch.demo.dto.Courier; 6 | import lombok.Data; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import java.util.stream.Collectors; 11 | 12 | /** 13 | * @author eleme.demo 14 | */ 15 | @Data 16 | public class CourierPool { 17 | private List couriers = new ArrayList<>(0); 18 | public List getAvailableCouriers(final DispatchContext context,final long range){ 19 | return couriers.stream().filter( 20 | x->{ 21 | if(x.getPlanRoutes().isEmpty()){ 22 | return true; 23 | } 24 | List actionNodes = x.getPlanRoutes(); 25 | if (context.getTimeStamp() + range >= actionNodes.get(actionNodes.size() - 1).getActionTime()){ 26 | return true; 27 | } 28 | return false; 29 | } 30 | ).collect(Collectors.toList()); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # 智慧物流:新冠期间饿了么骑士行为预估 复赛 # 2 | 3 | ### 比赛链接 4 | 5 | 6 | 7 | ### 基本解题思路 8 | 9 | 1. 根据cost贪心派单 10 | 2. 订单合并策略 11 | * 当订单较少时,合并起点或终点相近的订单 12 | * 当订单较多时,合并起点和终点都相近的订单 13 | 3. 优先分派将要超时的订单 14 | 4. 优先给空闲时间较长的骑手派单 15 | 5. 仅对已经空闲和在一个调度间隔内的空闲骑手的模拟派单提交到系统 16 | 6. 尽量不分配超时订单 17 | 18 | 针对骑手i,订单组j的cost可以表示为 19 | 20 | $$ cost_{i,j}= deliverTimeCost_j+overTimeCost_j+emergencyCost_j+courierIdleCost_i $$ 21 | 22 | 其中 23 | 24 | •$deliverTimeCost_j$=最后一单运送花费时间/运单数量 25 | 26 | •$overTimeCost_j$=订单组超时的订单数*factor_a 27 | 28 | •$emergencyCost_j$=订单组中将要超时的订单的比例*factor_b 29 | 30 | •$courierIdleCost_i$=骑手等待时间 *factor_c 31 | 32 | 派单程序pipline: 33 | 34 | ![pipline](pipline.png) 35 | 36 | 37 | 38 | ### 线下得分 39 | 40 | AreaId 680507 overtimeCount: 248.000000 41 | 42 | AreaId 680507 avgDeliveryTime: 2515.270047 43 | 44 | AreaId 680507 overtimeRate: 0.292108 45 | 46 | AreaId 725011 overtimeCount: 202.000000 47 | 48 | AreaId 725011 avgDeliveryTime: 1835.453719 49 | 50 | AreaId 725011 overtimeRate: 0.166118 51 | 52 | AreaId 730221 overtimeCount: 256.000000 53 | 54 | AreaId 730221 avgDeliveryTime: 2542.765331 55 | 56 | AreaId 730221 overtimeRate: 0.208639 57 | 58 | total overtimeRate 0.214459 aveDeliverTime 2274.407367 59 | 60 | 61 | 62 | ### 线上得分 63 | 64 | avgDeliveryTime:3619.6574 65 | 66 | overtimeCount:9952.0000 67 | 68 | overtimeRate:0.2636 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /src/main/java/dispatch/demo/utils/DistanceUtils.java: -------------------------------------------------------------------------------- 1 | package dispatch.demo.utils; 2 | 3 | 4 | import dispatch.demo.dto.Location; 5 | 6 | /** 7 | * @author eleme.demo 8 | */ 9 | public class DistanceUtils { 10 | 11 | /** 12 | * 地球半径 13 | */ 14 | private static final double RADIUS = 6367000.0; 15 | 16 | /** 17 | * 导航距离/路面距离 经验系数 18 | */ 19 | private static final double COEFFICIENT = 1.4; 20 | 21 | 22 | public static int timeConsuming(Location from, Location to, double speed) { 23 | return (int) Math.ceil(getDistance(from, to) / speed); 24 | } 25 | 26 | /** 经验路面距离 = 球面距离 * 经验系数(1.4) */ 27 | public static double getDistance(Location from, Location to) { 28 | return greatCircleDistance(from.getLongitude(), from.getLatitude(), to.getLongitude(), to.getLatitude()) * COEFFICIENT; 29 | } 30 | 31 | /** 简化版球面距离 */ 32 | private static double greatCircleDistance(double lng1, double lat1, double lng2, double lat2) { 33 | // 经度差值 34 | double deltaLng = lng2 - lng1; 35 | // 纬度差值 36 | double deltaLat = lat2 - lat1; 37 | // 平均纬度 38 | double b = (lat1 + lat2) / 2.0; 39 | // 东西距离 40 | double x = Math.toRadians(deltaLng) * RADIUS * Math.cos(Math.toRadians(b)); 41 | // 南北距离 42 | double y = RADIUS * Math.toRadians(deltaLat); 43 | // 用平面的矩形对角距离公式计算总距离 44 | return Math.sqrt(x * x + y * y); 45 | } 46 | } 47 | 48 | -------------------------------------------------------------------------------- /src/main/java/dispatch/demo/core/solver/packed/uitls.java: -------------------------------------------------------------------------------- 1 | package dispatch.demo.core.solver.packed; 2 | 3 | import dispatch.demo.core.context.DispatchContext; 4 | 5 | import java.util.List; 6 | 7 | public class uitls { 8 | public static double calculateCost(DispatchContext context,List actions){ 9 | final double timeFactor = 1; 10 | 11 | assert actions.size() % 3 == 0; 12 | final int norder = actions.size()/3; 13 | double totalCost =0; 14 | 15 | int iOrder = 0; 16 | long pretime = context.getTimeStamp(); 17 | for(RichAction richAction : actions){ 18 | if(richAction.getAction_type() != 3){ 19 | continue; 20 | } 21 | iOrder+=1; 22 | double c = richAction.getActionNode().getActionTime()-pretime;//-richAction.getOrder().getCreateTime(); 23 | totalCost += c; 24 | pretime = richAction.getActionNode().getActionTime(); 25 | if(richAction.getActionNode().getActionTime() > richAction.getOrder().getPromiseDeliverTime() 26 | // && richAction.getActionNode().getActionTime() > richAction.getOrder().getPromiseDeliverTime()-60 * 10 27 | ){ 28 | totalCost += 60 * 5; 29 | } 30 | } 31 | double r = (totalCost-(norder-1)*60*4*0) / norder; 32 | // actions.get(actions.size()-1).setCost(r); 33 | return r;//((norder+1)*norder/2); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/dispatch/demo/core/pool/OrderPool.java: -------------------------------------------------------------------------------- 1 | package dispatch.demo.core.pool; 2 | 3 | import dispatch.demo.dto.Order; 4 | import lombok.Getter; 5 | 6 | import java.util.ArrayList; 7 | import java.util.HashMap; 8 | import java.util.List; 9 | import java.util.stream.Collectors; 10 | 11 | /** 12 | * @author eleme.demo 13 | */ 14 | 15 | public class OrderPool { 16 | 17 | private List orders; 18 | 19 | @Getter 20 | private HashMap orderMap; 21 | 22 | public OrderPool() { 23 | orders = new ArrayList<>(0); 24 | orderMap = new HashMap<>(0); 25 | } 26 | 27 | public void addDispatchingOrders(List dispatchingOrders) { 28 | this.orders.addAll(dispatchingOrders); 29 | for (Order order : orders) { 30 | orderMap.put(order.getId(), order); 31 | } 32 | } 33 | 34 | public List getDispatchingOrders() { 35 | return orders.stream().filter(o -> o.getStatus() == 0).collect(Collectors.toList()); 36 | } 37 | 38 | public void markAssignedOrder(String orderId){ 39 | orderMap.get(orderId).setStatus(1); 40 | } 41 | 42 | public void markArrivalCompleteOrder(String orderId){ 43 | orderMap.get(orderId).setStatus(2); 44 | } 45 | 46 | public void markPickCompleteOrder(String orderId){ 47 | orderMap.get(orderId).setStatus(3); 48 | } 49 | 50 | public void markDeliverCompleteOrder(String orderId){ 51 | orderMap.get(orderId).setStatus(4); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/dispatch/demo/core/route/TailAppendPlan.java: -------------------------------------------------------------------------------- 1 | package dispatch.demo.core.route; 2 | 3 | import dispatch.demo.core.context.DispatchContext; 4 | import dispatch.demo.dto.ActionNode; 5 | import dispatch.demo.dto.Courier; 6 | import dispatch.demo.dto.Location; 7 | import dispatch.demo.dto.Order; 8 | import dispatch.demo.utils.DistanceUtils; 9 | 10 | import java.util.ArrayList; 11 | import java.util.Arrays; 12 | import java.util.List; 13 | 14 | /** 15 | * @author eleme.demo 16 | * the courier always append new order to tail 17 | */ 18 | public class TailAppendPlan extends Planner { 19 | 20 | @Override 21 | public List plan(Courier courier, Order order, DispatchContext context) { 22 | Location loc; 23 | long planTime; 24 | if (courier.getOrders().isEmpty()) { 25 | loc = courier.getLoc(); 26 | planTime = context.getTimeStamp(); 27 | } else { 28 | int size = courier.getPlanRoutes().size(); 29 | ActionNode lastNode = courier.getPlanRoutes().get(size - 1); 30 | Order lastOrder = context.getOrderPool().getOrderMap().get(lastNode.getOrderId()); 31 | loc = lastOrder.getDstLoc(); 32 | planTime = lastNode.getActionTime(); 33 | } 34 | List tailPlans = planOneOrder(courier, loc, planTime, order); 35 | List appendPlans = new ArrayList<>(courier.getPlanRoutes().size() + tailPlans.size()); 36 | appendPlans.addAll(courier.getPlanRoutes()); 37 | appendPlans.addAll(tailPlans); 38 | return appendPlans; 39 | } 40 | 41 | private List planOneOrder(Courier courier, Location loc, long planTime, Order order) { 42 | long arrivalTime = planTime + DistanceUtils.timeConsuming(loc, order.getSrcLoc(), courier.getSpeed()); 43 | long pickTime = Math.max(order.getEstimatedPrepareCompletedTime(), arrivalTime); 44 | long deliverTime = pickTime + DistanceUtils.timeConsuming(order.getSrcLoc(), order.getDstLoc(), courier.getSpeed()); 45 | ActionNode arrivalNode = new ActionNode(order.getId(), 1, arrivalTime, false, planTime); 46 | ActionNode pickNode = new ActionNode(order.getId(), 2, pickTime, false, arrivalTime); 47 | ActionNode deliveryNode = new ActionNode(order.getId(), 3, deliverTime, false, pickTime); 48 | return Arrays.asList(arrivalNode, pickNode, deliveryNode); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.1.13.RELEASE 9 | 10 | 11 | dispatch 12 | dispatch-demo 13 | 0.0.1-SNAPSHOT 14 | dispatch-demo 15 | Demo project for Spring Boot 16 | 17 | 18 | 1.8 19 | 20 | 21 | 22 | 23 | org.projectlombok 24 | lombok 25 | 26 | 27 | 28 | 29 | com.alibaba 30 | fastjson 31 | 1.2.68 32 | 33 | 34 | 35 | org.springframework.boot 36 | spring-boot-starter-web 37 | 38 | 39 | 40 | org.springframework.boot 41 | spring-boot-devtools 42 | runtime 43 | true 44 | 45 | 46 | org.springframework.boot 47 | spring-boot-starter-test 48 | test 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | org.apache.commons 59 | commons-lang3 60 | 3.1 61 | 62 | 63 | 64 | 65 | 66 | 67 | org.springframework.boot 68 | spring-boot-maven-plugin 69 | 70 | 71 | dispatch-demo 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /src/main/java/dispatch/demo/core/context/DispatchContext.java: -------------------------------------------------------------------------------- 1 | package dispatch.demo.core.context; 2 | 3 | import dispatch.demo.core.pool.CourierPool; 4 | import dispatch.demo.core.pool.OrderPool; 5 | import dispatch.demo.dto.ActionNode; 6 | import dispatch.demo.dto.Courier; 7 | import dispatch.demo.dto.Location; 8 | import dispatch.demo.dto.Order; 9 | import lombok.Data; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | import java.util.stream.Collectors; 14 | 15 | /** 16 | * @author eleme.demo 17 | */ 18 | @Data 19 | public class DispatchContext { 20 | private String areaId; 21 | private CourierPool courierPool = new CourierPool(); 22 | private OrderPool orderPool = new OrderPool(); 23 | private long timeStamp; 24 | private boolean isEndOfTest = false; 25 | 26 | public void addOnlineCouriers(List courierList) { 27 | courierPool.getCouriers().addAll(courierList); 28 | } 29 | 30 | public void addDispatchingOrders(List orders) { 31 | orderPool.addDispatchingOrders(orders); 32 | } 33 | 34 | public void markAllocatedOrders(List orderIds) { 35 | orderIds.forEach(id -> orderPool.markAssignedOrder(id)); 36 | } 37 | 38 | public void refresh(long refreshTime) { 39 | this.timeStamp = refreshTime; 40 | courierPool.getCouriers().forEach(c -> { 41 | refreshCourier(c, refreshTime); 42 | }); 43 | } 44 | 45 | private void refreshCourier(Courier courier, long refreshTime) { 46 | List actionNodeList = courier.getPlanRoutes(); 47 | List refreshNodeList = new ArrayList<>(0); 48 | for (ActionNode node : actionNodeList) { 49 | if (node.isSubmitted() && node.getActionTime() <= refreshTime) { 50 | if (node.getActionType() == 1) { 51 | //到店完成 52 | orderPool.markArrivalCompleteOrder(node.getOrderId()); 53 | } 54 | if (node.getActionType() == 2) { 55 | //取餐完成 56 | orderPool.markPickCompleteOrder(node.getOrderId()); 57 | } 58 | if (node.getActionType() == 3) { 59 | //送达 60 | orderPool.markDeliverCompleteOrder(node.getOrderId()); 61 | } 62 | } else { 63 | refreshNodeList.add(node); 64 | } 65 | } 66 | List loadOrders = courier.getOrders().stream() 67 | .filter(o -> o.getStatus() != 4) 68 | .collect(Collectors.toList()); 69 | courier.setOrders(loadOrders); 70 | courier.setPlanRoutes(refreshNodeList); 71 | if(refreshNodeList.isEmpty() && !actionNodeList.isEmpty()){ 72 | Location latestLoc = orderPool.getOrderMap().get(actionNodeList.get(actionNodeList.size()-1).getOrderId()).getDstLoc(); 73 | courier.setLoc(latestLoc); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/dispatch/demo/core/DispatchService.java: -------------------------------------------------------------------------------- 1 | package dispatch.demo.core; 2 | 3 | import dispatch.demo.core.context.DispatchContext; 4 | import dispatch.demo.core.solver.BaseSolver; 5 | import dispatch.demo.core.solver.Solver; 6 | import dispatch.demo.core.solver.packed.PackedSolver; 7 | import dispatch.demo.dto.CourierPlan; 8 | import dispatch.demo.dto.DispatchRequest; 9 | import dispatch.demo.dto.DispatchSolution; 10 | 11 | import java.util.Collections; 12 | import java.util.HashMap; 13 | import java.util.List; 14 | import java.util.Map; 15 | /** 16 | * @author eleme.demo 17 | */ 18 | public class DispatchService { 19 | 20 | Map serviceContext = new HashMap<>(0); 21 | 22 | public DispatchSolution dispatch(DispatchRequest request) { 23 | 24 | String areaId = request.getAreaId(); 25 | DispatchContext context; 26 | if (request.isFirstRound()) { 27 | System.out.println("============first round "+areaId+"============"); 28 | context = new DispatchContext(); 29 | context.setAreaId(areaId) ; 30 | context.setTimeStamp(request.getRequestTime()); 31 | serviceContext.put(areaId, context); 32 | 33 | } else { 34 | context = serviceContext.get(areaId); 35 | if (null == context) { 36 | DispatchSolution emptySolution = new DispatchSolution(); 37 | emptySolution.setCourierPlans(Collections.emptyList()); 38 | return emptySolution; 39 | } else { 40 | if (request.isLastRound()) { 41 | context.setEndOfTest(true); 42 | } 43 | } 44 | context.refresh(request.getRequestTime()); 45 | } 46 | context.addOnlineCouriers(request.getCouriers()); 47 | context.addDispatchingOrders(request.getOrders()); 48 | Solver solver = context.isEndOfTest()?getBaseSolver(context): getSolver(context,request.isFirstRound()); 49 | List courierPlans = solver.solve(); 50 | courierPlans.forEach(cp -> { 51 | cp.getPlanRoutes().forEach(a -> a.setSubmitted(true)); 52 | }); 53 | List assignedIds = solver.getAssignedOrderIds(); 54 | context.markAllocatedOrders(assignedIds); 55 | while (!context.getOrderPool().getDispatchingOrders().isEmpty() && context.isEndOfTest()) { 56 | long aheadTime = 10 * 60; 57 | context.setTimeStamp(context.getTimeStamp() + aheadTime); 58 | Solver lastRoundSolver = getBaseSolver(context); 59 | List tmpPlans = lastRoundSolver.solve(); 60 | courierPlans.addAll(tmpPlans); 61 | tmpPlans.forEach(cp -> { 62 | cp.getPlanRoutes().forEach(a -> a.setSubmitted(true)); 63 | }); 64 | context.markAllocatedOrders(lastRoundSolver.getAssignedOrderIds()); 65 | } 66 | DispatchSolution solution = new DispatchSolution(); 67 | solution.setCourierPlans(courierPlans); 68 | return solution; 69 | } 70 | 71 | BaseSolver getBaseSolver(DispatchContext context) { 72 | return new BaseSolver(context); 73 | } 74 | Solver getSolver(DispatchContext context,boolean isFirst) { 75 | // if(isFirst ){ 76 | // PackedSolver.resetCache(); 77 | // } 78 | return new PackedSolver(context); 79 | // return new BaseSolver(context); 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/dispatch/demo/core/route/PackedTrailPlaner.java: -------------------------------------------------------------------------------- 1 | package dispatch.demo.core.route; 2 | 3 | import dispatch.demo.core.context.DispatchContext; 4 | import dispatch.demo.core.solver.packed.RichAction; 5 | import dispatch.demo.dto.ActionNode; 6 | import dispatch.demo.dto.Courier; 7 | import dispatch.demo.dto.Location; 8 | import dispatch.demo.dto.Order; 9 | import dispatch.demo.utils.DistanceUtils; 10 | 11 | import java.util.ArrayList; 12 | import java.util.Arrays; 13 | import java.util.List; 14 | 15 | /** 16 | * @author eleme.demo 17 | * the courier always append new order to tail 18 | */ 19 | public class PackedTrailPlaner extends Planner { 20 | 21 | /** 22 | * 23 | * @param courier 24 | * @param actions 25 | * @param context 26 | * @return RichAction with action time 27 | */ 28 | @Override 29 | public List plan(Courier courier, List actions, DispatchContext context) { 30 | Location loc; 31 | long planTime; 32 | if (courier.getOrders().isEmpty()) { 33 | loc = courier.getLoc(); 34 | planTime = context.getTimeStamp(); 35 | } else { 36 | int size = courier.getPlanRoutes().size(); 37 | ActionNode lastNode = courier.getPlanRoutes().get(size - 1); 38 | Order lastOrder = context.getOrderPool().getOrderMap().get(lastNode.getOrderId()); 39 | loc = lastOrder.getDstLoc(); 40 | planTime = Math.max(lastNode.getActionTime(),context.getTimeStamp()); 41 | } 42 | 43 | List r = executePlan(courier,loc,planTime,actions); 44 | return r; 45 | } 46 | 47 | private List executePlan(Courier courier, Location loc, long planTime,List actions){ 48 | ArrayList r = new ArrayList<>(); 49 | Location courierLocation = loc.clone(); 50 | for( RichAction action : actions){ 51 | Order order = action.getOrder(); 52 | // long arrivalTime = planTime + DistanceUtils.timeConsuming(loc,action.get, courier.getSpeed()); 53 | if(action.getAction_type() == 1){ 54 | long arrivalTime = planTime + DistanceUtils.timeConsuming(courierLocation,action.getOrder().getSrcLoc(), courier.getSpeed()) ; 55 | long pickTime = Math.max(order.getEstimatedPrepareCompletedTime(), arrivalTime); 56 | ActionNode arrivalNode = new ActionNode(order.getId(), 1, arrivalTime, false, planTime); 57 | ActionNode pickNode = new ActionNode(order.getId(), 2, pickTime, false, arrivalTime); 58 | // RichAction r1 = new 59 | r.add(new RichAction(arrivalNode,action.getOrder(),1)); 60 | r.add(new RichAction(pickNode,action.getOrder(),2)); 61 | planTime = pickTime; 62 | courierLocation = action.getOrder().getSrcLoc(); 63 | continue; 64 | } 65 | else if (action.getAction_type() == 3){ 66 | long deliverTime = planTime + DistanceUtils.timeConsuming(courierLocation, order.getDstLoc(), courier.getSpeed()); 67 | ActionNode deliveryNode = new ActionNode(order.getId(), 3, deliverTime, false, planTime); 68 | r.add(new RichAction(deliveryNode,action.getOrder(),3)); 69 | planTime = deliverTime; 70 | courierLocation = action.getOrder().getDstLoc(); 71 | 72 | } 73 | else{ 74 | assert false; 75 | } 76 | } 77 | return r; 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/dispatch/demo/controller/DispatchController.java: -------------------------------------------------------------------------------- 1 | package dispatch.demo.controller; 2 | 3 | 4 | import com.alibaba.fastjson.JSON; 5 | import dispatch.demo.core.DispatchService; 6 | import dispatch.demo.dto.DispatchRequest; 7 | import dispatch.demo.dto.DispatchSolution; 8 | import dispatch.demo.dto.Response; 9 | import org.springframework.web.bind.annotation.*; 10 | 11 | import javax.servlet.http.HttpServletRequest; 12 | import java.io.PrintWriter; 13 | import java.io.StringWriter; 14 | import java.util.ArrayList; 15 | import java.util.concurrent.ArrayBlockingQueue; 16 | import java.util.concurrent.Future; 17 | import java.util.concurrent.ThreadPoolExecutor; 18 | import java.util.concurrent.TimeUnit; 19 | 20 | 21 | /** 22 | * @author eleme.demo 23 | */ 24 | @RestController() 25 | @RequestMapping("/api/v1") 26 | public class DispatchController { 27 | 28 | 29 | ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 30 | Runtime.getRuntime().availableProcessors(), 31 | Runtime.getRuntime().availableProcessors(), 32 | 30, 33 | TimeUnit.SECONDS, 34 | new ArrayBlockingQueue<>(100) 35 | ); 36 | 37 | DispatchService dispatchService = new DispatchService(); 38 | 39 | 40 | @RequestMapping(value = "/ping", method = RequestMethod.GET) 41 | public Response ping() { 42 | System.out.println("ping"); 43 | return new Response<>("PONG"); 44 | } 45 | 46 | @RequestMapping(value = "/score", method = RequestMethod.POST) 47 | public Response score(HttpServletRequest request) { 48 | System.out.println("ping"); 49 | return new Response<>("PONG"); 50 | } 51 | long preTimestamp = System.currentTimeMillis(); 52 | boolean prestatus = true; 53 | @RequestMapping(value = "/dispatch", method = RequestMethod.POST, produces = "application/json") 54 | public String dispatch(@RequestBody String jsonRequest) { 55 | long curTime = System.currentTimeMillis(); 56 | // System.out.println(String.format("last used time %d",curTime-preTimestamp)); 57 | preTimestamp = curTime; 58 | // System.out.println(jsonRequest); 59 | DispatchRequest request = JSON.parseObject(jsonRequest, DispatchRequest.class); 60 | DispatchSolution result = null; 61 | Future f = threadPoolExecutor.submit(() -> { 62 | return dispatchService.dispatch(request); 63 | }); 64 | try { 65 | //wait maximum 4s 66 | result = f.get(4500, TimeUnit.MILLISECONDS); 67 | 68 | } catch (Exception e) { 69 | prestatus = false; 70 | e.printStackTrace(); 71 | System.out.println(e.getMessage()); 72 | //downgrade solution here, downgrade solution must finish within 1s so that total request processing will be finished within 5s. 73 | if(prestatus){ 74 | prestatus = false; 75 | result = new DispatchSolution(); 76 | result.setCourierPlans(new ArrayList<>()); 77 | return JSON.toJSONString(new Response(200,result)); 78 | 79 | } 80 | StringWriter sw = new StringWriter(); 81 | PrintWriter pw = new PrintWriter(sw); 82 | e.printStackTrace(pw); 83 | String msg=sw.toString(); 84 | 85 | return JSON.toJSONString(Response.NewErrResponse(msg)); 86 | } 87 | prestatus=true; 88 | if (null != result) { 89 | // System.out.println(JSON.toJSONString(result)); 90 | } 91 | Response r = new Response(200, result); 92 | return JSON.toJSONString(r); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /.mvn/wrapper/MavenWrapperDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2007-present the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import java.util.Properties; 18 | 19 | public class MavenWrapperDownloader { 20 | 21 | private static final String WRAPPER_VERSION = "0.5.6"; 22 | /** 23 | * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. 24 | */ 25 | private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" 26 | + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; 27 | 28 | /** 29 | * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to 30 | * use instead of the default one. 31 | */ 32 | private static final String MAVEN_WRAPPER_PROPERTIES_PATH = 33 | ".mvn/wrapper/maven-wrapper.properties"; 34 | 35 | /** 36 | * Path where the maven-wrapper.jar will be saved to. 37 | */ 38 | private static final String MAVEN_WRAPPER_JAR_PATH = 39 | ".mvn/wrapper/maven-wrapper.jar"; 40 | 41 | /** 42 | * Name of the property which should be used to override the default download url for the wrapper. 43 | */ 44 | private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; 45 | 46 | public static void main(String args[]) { 47 | System.out.println("- Downloader started"); 48 | File baseDirectory = new File(args[0]); 49 | System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); 50 | 51 | // If the maven-wrapper.properties exists, read it and check if it contains a custom 52 | // wrapperUrl parameter. 53 | File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); 54 | String url = DEFAULT_DOWNLOAD_URL; 55 | if (mavenWrapperPropertyFile.exists()) { 56 | FileInputStream mavenWrapperPropertyFileInputStream = null; 57 | try { 58 | mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); 59 | Properties mavenWrapperProperties = new Properties(); 60 | mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); 61 | url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); 62 | } catch (IOException e) { 63 | System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); 64 | } finally { 65 | try { 66 | if (mavenWrapperPropertyFileInputStream != null) { 67 | mavenWrapperPropertyFileInputStream.close(); 68 | } 69 | } catch (IOException e) { 70 | // Ignore ... 71 | } 72 | } 73 | } 74 | System.out.println("- Downloading from: " + url); 75 | 76 | File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); 77 | if (!outputFile.getParentFile().exists()) { 78 | if (!outputFile.getParentFile().mkdirs()) { 79 | System.out.println( 80 | "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); 81 | } 82 | } 83 | System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); 84 | try { 85 | downloadFileFromURL(url, outputFile); 86 | System.out.println("Done"); 87 | System.exit(0); 88 | } catch (Throwable e) { 89 | System.out.println("- Error downloading"); 90 | e.printStackTrace(); 91 | System.exit(1); 92 | } 93 | } 94 | 95 | private static void downloadFileFromURL(String urlString, File destination) throws Exception { 96 | if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { 97 | String username = System.getenv("MVNW_USERNAME"); 98 | char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); 99 | Authenticator.setDefault(new Authenticator() { 100 | @Override 101 | protected PasswordAuthentication getPasswordAuthentication() { 102 | return new PasswordAuthentication(username, password); 103 | } 104 | }); 105 | } 106 | URL website = new URL(urlString); 107 | ReadableByteChannel rbc; 108 | rbc = Channels.newChannel(website.openStream()); 109 | FileOutputStream fos = new FileOutputStream(destination); 110 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); 111 | fos.close(); 112 | rbc.close(); 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /src/main/java/dispatch/demo/core/solver/BaseSolver.java: -------------------------------------------------------------------------------- 1 | package dispatch.demo.core.solver; 2 | 3 | import dispatch.demo.core.context.DispatchContext; 4 | import dispatch.demo.core.route.Planner; 5 | import dispatch.demo.core.route.TailAppendPlan; 6 | import dispatch.demo.dto.ActionNode; 7 | import dispatch.demo.dto.Courier; 8 | import dispatch.demo.dto.CourierPlan; 9 | import dispatch.demo.dto.Order; 10 | import lombok.Data; 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | import java.util.stream.Collectors; 15 | import java.util.stream.IntStream; 16 | 17 | /** 18 | * @author eleme.demo 19 | */ 20 | 21 | public class BaseSolver implements Solver { 22 | 23 | /** 两次调度最短间隔时间 */ 24 | private static final long MINIMUM_INTERVAL_SECONDS = 60; 25 | 26 | List orders; 27 | List couriers; 28 | DispatchContext context; 29 | 30 | boolean[] ordersAssigned; 31 | Cost[][] costTable; 32 | 33 | /** 路径规划:新单永远规划在末尾 */ 34 | Planner planner = new TailAppendPlan(); 35 | 36 | public BaseSolver(DispatchContext context) { 37 | this.context = context; 38 | this.orders = getCandidateOrders(context); 39 | this.couriers = getCandidateCouriers(context); 40 | this.ordersAssigned = new boolean[this.orders.size()]; 41 | } 42 | 43 | protected List getCandidateCouriers(DispatchContext dispatchContext) { 44 | return dispatchContext.getCourierPool().getCouriers(); 45 | } 46 | 47 | protected List getCandidateOrders(DispatchContext dispatchContext) { 48 | return dispatchContext.getOrderPool().getDispatchingOrders(); 49 | } 50 | @Override 51 | public List getAssignedOrderIds() { 52 | return IntStream.range(0, this.orders.size()) 53 | .filter(i -> this.ordersAssigned[i]) 54 | .mapToObj(j -> this.orders.get(j).getId()).collect(Collectors.toList()); 55 | } 56 | 57 | private void initTable() { 58 | int courierSize = couriers.size(); 59 | int orderSize = orders.size(); 60 | costTable = new Cost[courierSize][]; 61 | for (int i = 0; i < courierSize; i++) { 62 | costTable[i] = new Cost[orderSize]; 63 | for (int j = 0; j < orderSize; j++) { 64 | costTable[i][j] = getCost(i, j); 65 | } 66 | } 67 | } 68 | @Override 69 | public List solve() { 70 | initTable(); 71 | while (true) { 72 | Cost cost = getBest(); 73 | if (null == cost) { 74 | break; 75 | } 76 | dealWithCost(cost); 77 | } 78 | List results = new ArrayList<>(0); 79 | for (Courier courier : couriers) { 80 | CourierPlan submitPlan = getSubmitPlan(courier); 81 | if (!submitPlan.getPlanRoutes().isEmpty()) { 82 | results.add(submitPlan); 83 | } 84 | } 85 | return results; 86 | } 87 | 88 | 89 | private CourierPlan getSubmitPlan(Courier courier) { 90 | long submitThresholdTime = this.context.getTimeStamp() + MINIMUM_INTERVAL_SECONDS; 91 | List submittedNodes = courier.getPlanRoutes().stream() 92 | .filter(node -> !node.isSubmitted()) 93 | .filter(node -> node.getNeedSubmitTime() <= submitThresholdTime || context.isEndOfTest()) 94 | .collect(Collectors.toList()); 95 | CourierPlan plan = new CourierPlan(); 96 | plan.setCourierId(courier.getId()); 97 | plan.setPlanRoutes(submittedNodes); 98 | return plan; 99 | } 100 | 101 | private void dealWithCost(Cost cost) { 102 | cost.getCourier().setPlanRoutes(cost.getPlanActionNodes()); 103 | cost.getCourier().getOrders().add(cost.getOrder()); 104 | ordersAssigned[cost.j] = true; 105 | updateWeightRow(cost.i); 106 | updateWeightCol(cost.j); 107 | } 108 | 109 | private void updateWeightRow(int i) { 110 | for (int j = 0; j < orders.size(); ++j) { 111 | costTable[i][j] = getCost(i, j); 112 | } 113 | } 114 | 115 | private void updateWeightCol(int j) { 116 | for (int i = 0; i < couriers.size(); ++i) { 117 | costTable[i][j] = getCost(i, j); 118 | } 119 | } 120 | 121 | Cost getBest() { 122 | Cost best = null; 123 | int courierSize = couriers.size(); 124 | int orderSize = orders.size(); 125 | for (int i = 0; i < courierSize; i++) { 126 | for (int j = 0; j < orderSize; j++) { 127 | Cost tmpC = costTable[i][j]; 128 | if (null == tmpC) { 129 | continue; 130 | } 131 | if (null == best) { 132 | best = tmpC; 133 | continue; 134 | } 135 | if (costLess(tmpC, best)) { 136 | best = tmpC; 137 | } 138 | } 139 | } 140 | return best; 141 | } 142 | 143 | protected boolean costLess(Cost c1, Cost c2) { 144 | return c1.getCost() <= c2.getCost(); 145 | } 146 | 147 | protected Cost getCost(int i, int j) { 148 | if (ordersAssigned[j]) { 149 | return null; 150 | } 151 | Cost cost = new Cost(i, j); 152 | if (!cost.isValid()) { 153 | return null; 154 | } 155 | return cost; 156 | } 157 | 158 | @Data 159 | public class Cost { 160 | int i, j; 161 | Courier courier; 162 | Order order; 163 | double cost; 164 | List planActionNodes; 165 | 166 | Cost(int i, int j) { 167 | this.i = i; 168 | this.j = j; 169 | this.courier = couriers.get(i); 170 | this.order = orders.get(j); 171 | this.planActionNodes = planner.plan(courier, order, context); 172 | this.cost = calCost(); 173 | } 174 | 175 | boolean isValid() { 176 | int maxLoad = courier.getMaxLoads(); 177 | int cr = (int) courier.getOrders().stream().filter(c -> c.getStatus() == 3).count(); 178 | for (ActionNode node : planActionNodes) { 179 | if (node.getActionType() == 2) { 180 | if (++cr > maxLoad) { 181 | return false; 182 | } 183 | } 184 | if (node.getActionType() == 3) { 185 | --cr; 186 | } 187 | } 188 | return true; 189 | } 190 | 191 | /** 192 | * 此处是一个最简单的cost函数例子,谁能最快送达就给谁 193 | */ 194 | protected double calCost() { 195 | double cost = Double.MAX_VALUE; 196 | if (!isValid()) { 197 | return Double.MAX_VALUE; 198 | } 199 | for (ActionNode node : this.planActionNodes) { 200 | if (node.getActionType() == 3 && node.getOrderId().equals(order.getId())) { 201 | cost = node.getActionTime(); 202 | break; 203 | } 204 | } 205 | return cost; 206 | } 207 | 208 | double getCost() { 209 | return cost; 210 | } 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 124 | 125 | FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 127 | ) 128 | 129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 131 | if exist %WRAPPER_JAR% ( 132 | if "%MVNW_VERBOSE%" == "true" ( 133 | echo Found %WRAPPER_JAR% 134 | ) 135 | ) else ( 136 | if not "%MVNW_REPOURL%" == "" ( 137 | SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 138 | ) 139 | if "%MVNW_VERBOSE%" == "true" ( 140 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 141 | echo Downloading from: %DOWNLOAD_URL% 142 | ) 143 | 144 | powershell -Command "&{"^ 145 | "$webclient = new-object System.Net.WebClient;"^ 146 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 147 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 148 | "}"^ 149 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ 150 | "}" 151 | if "%MVNW_VERBOSE%" == "true" ( 152 | echo Finished downloading %WRAPPER_JAR% 153 | ) 154 | ) 155 | @REM End of extension 156 | 157 | @REM Provide a "standardized" way to retrieve the CLI args that will 158 | @REM work with both Windows and non-Windows executions. 159 | set MAVEN_CMD_LINE_ARGS=%* 160 | 161 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 162 | if ERRORLEVEL 1 goto error 163 | goto end 164 | 165 | :error 166 | set ERROR_CODE=1 167 | 168 | :end 169 | @endlocal & set ERROR_CODE=%ERROR_CODE% 170 | 171 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 172 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 173 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 174 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 175 | :skipRcPost 176 | 177 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 178 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 179 | 180 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 181 | 182 | exit /B %ERROR_CODE% 183 | -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # https://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Mingw, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | fi 118 | 119 | if [ -z "$JAVA_HOME" ]; then 120 | javaExecutable="`which javac`" 121 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 122 | # readlink(1) is not available as standard on Solaris 10. 123 | readLink=`which readlink` 124 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 125 | if $darwin ; then 126 | javaHome="`dirname \"$javaExecutable\"`" 127 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 128 | else 129 | javaExecutable="`readlink -f \"$javaExecutable\"`" 130 | fi 131 | javaHome="`dirname \"$javaExecutable\"`" 132 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 133 | JAVA_HOME="$javaHome" 134 | export JAVA_HOME 135 | fi 136 | fi 137 | fi 138 | 139 | if [ -z "$JAVACMD" ] ; then 140 | if [ -n "$JAVA_HOME" ] ; then 141 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 142 | # IBM's JDK on AIX uses strange locations for the executables 143 | JAVACMD="$JAVA_HOME/jre/sh/java" 144 | else 145 | JAVACMD="$JAVA_HOME/bin/java" 146 | fi 147 | else 148 | JAVACMD="`which java`" 149 | fi 150 | fi 151 | 152 | if [ ! -x "$JAVACMD" ] ; then 153 | echo "Error: JAVA_HOME is not defined correctly." >&2 154 | echo " We cannot execute $JAVACMD" >&2 155 | exit 1 156 | fi 157 | 158 | if [ -z "$JAVA_HOME" ] ; then 159 | echo "Warning: JAVA_HOME environment variable is not set." 160 | fi 161 | 162 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 163 | 164 | # traverses directory structure from process work directory to filesystem root 165 | # first directory with .mvn subdirectory is considered project base directory 166 | find_maven_basedir() { 167 | 168 | if [ -z "$1" ] 169 | then 170 | echo "Path not specified to find_maven_basedir" 171 | return 1 172 | fi 173 | 174 | basedir="$1" 175 | wdir="$1" 176 | while [ "$wdir" != '/' ] ; do 177 | if [ -d "$wdir"/.mvn ] ; then 178 | basedir=$wdir 179 | break 180 | fi 181 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 182 | if [ -d "${wdir}" ]; then 183 | wdir=`cd "$wdir/.."; pwd` 184 | fi 185 | # end of workaround 186 | done 187 | echo "${basedir}" 188 | } 189 | 190 | # concatenates all lines of a file 191 | concat_lines() { 192 | if [ -f "$1" ]; then 193 | echo "$(tr -s '\n' ' ' < "$1")" 194 | fi 195 | } 196 | 197 | BASE_DIR=`find_maven_basedir "$(pwd)"` 198 | if [ -z "$BASE_DIR" ]; then 199 | exit 1; 200 | fi 201 | 202 | ########################################################################################## 203 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 204 | # This allows using the maven wrapper in projects that prohibit checking in binary data. 205 | ########################################################################################## 206 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then 207 | if [ "$MVNW_VERBOSE" = true ]; then 208 | echo "Found .mvn/wrapper/maven-wrapper.jar" 209 | fi 210 | else 211 | if [ "$MVNW_VERBOSE" = true ]; then 212 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." 213 | fi 214 | if [ -n "$MVNW_REPOURL" ]; then 215 | jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 216 | else 217 | jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 218 | fi 219 | while IFS="=" read key value; do 220 | case "$key" in (wrapperUrl) jarUrl="$value"; break ;; 221 | esac 222 | done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" 223 | if [ "$MVNW_VERBOSE" = true ]; then 224 | echo "Downloading from: $jarUrl" 225 | fi 226 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" 227 | if $cygwin; then 228 | wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` 229 | fi 230 | 231 | if command -v wget > /dev/null; then 232 | if [ "$MVNW_VERBOSE" = true ]; then 233 | echo "Found wget ... using wget" 234 | fi 235 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 236 | wget "$jarUrl" -O "$wrapperJarPath" 237 | else 238 | wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" 239 | fi 240 | elif command -v curl > /dev/null; then 241 | if [ "$MVNW_VERBOSE" = true ]; then 242 | echo "Found curl ... using curl" 243 | fi 244 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 245 | curl -o "$wrapperJarPath" "$jarUrl" -f 246 | else 247 | curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f 248 | fi 249 | 250 | else 251 | if [ "$MVNW_VERBOSE" = true ]; then 252 | echo "Falling back to using Java to download" 253 | fi 254 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" 255 | # For Cygwin, switch paths to Windows format before running javac 256 | if $cygwin; then 257 | javaClass=`cygpath --path --windows "$javaClass"` 258 | fi 259 | if [ -e "$javaClass" ]; then 260 | if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 261 | if [ "$MVNW_VERBOSE" = true ]; then 262 | echo " - Compiling MavenWrapperDownloader.java ..." 263 | fi 264 | # Compiling the Java class 265 | ("$JAVA_HOME/bin/javac" "$javaClass") 266 | fi 267 | if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 268 | # Running the downloader 269 | if [ "$MVNW_VERBOSE" = true ]; then 270 | echo " - Running MavenWrapperDownloader.java ..." 271 | fi 272 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") 273 | fi 274 | fi 275 | fi 276 | fi 277 | ########################################################################################## 278 | # End of extension 279 | ########################################################################################## 280 | 281 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 282 | if [ "$MVNW_VERBOSE" = true ]; then 283 | echo $MAVEN_PROJECTBASEDIR 284 | fi 285 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 286 | 287 | # For Cygwin, switch paths to Windows format before running java 288 | if $cygwin; then 289 | [ -n "$M2_HOME" ] && 290 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 291 | [ -n "$JAVA_HOME" ] && 292 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 293 | [ -n "$CLASSPATH" ] && 294 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 295 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 296 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 297 | fi 298 | 299 | # Provide a "standardized" way to retrieve the CLI args that will 300 | # work with both Windows and non-Windows executions. 301 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" 302 | export MAVEN_CMD_LINE_ARGS 303 | 304 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 305 | 306 | exec "$JAVACMD" \ 307 | $MAVEN_OPTS \ 308 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 309 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 310 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 311 | -------------------------------------------------------------------------------- /src/main/java/dispatch/demo/core/solver/packed/PackedSolver.java: -------------------------------------------------------------------------------- 1 | package dispatch.demo.core.solver.packed; 2 | 3 | import dispatch.demo.core.context.DispatchContext; 4 | import dispatch.demo.core.route.PackedTrailPlaner; 5 | import dispatch.demo.core.route.Planner; 6 | import dispatch.demo.core.solver.Solver; 7 | import dispatch.demo.dto.ActionNode; 8 | import dispatch.demo.dto.Courier; 9 | import dispatch.demo.dto.CourierPlan; 10 | import dispatch.demo.dto.Order; 11 | import lombok.Data; 12 | 13 | import java.util.*; 14 | import java.util.concurrent.*; 15 | import java.util.concurrent.atomic.AtomicInteger; 16 | import java.util.stream.Collectors; 17 | 18 | import org.apache.commons.lang3.tuple.ImmutablePair; 19 | import org.apache.commons.lang3.tuple.Pair; 20 | import org.apache.commons.lang3.tuple.MutablePair; 21 | /** 22 | * @author eleme.demo 23 | */ 24 | 25 | public class PackedSolver implements Solver { 26 | 27 | /** 两次调度最短间隔时间 */ 28 | private static final long MINIMUM_INTERVAL_SECONDS = 60; 29 | protected static long maxUsedTime = 0; 30 | List orders; 31 | List couriers; 32 | DispatchContext context; 33 | 34 | List ordersAssigned; 35 | 36 | 37 | /** 路径规划:新单永远规划在末尾 */ 38 | Planner planner = new PackedTrailPlaner(); 39 | public static ForkJoinPool forkJoinPool = new ForkJoinPool(8); 40 | public PackedSolver(DispatchContext context) { 41 | this.context = context; 42 | this.orders = getCandidateOrders(context); 43 | this.couriers = getCandidateCouriers(context); 44 | this.ordersAssigned = new ArrayList<>(); 45 | } 46 | private final long candidateCouriersRange = MINIMUM_INTERVAL_SECONDS*10; 47 | protected List getCandidateCouriers(DispatchContext dispatchContext) { 48 | return dispatchContext.getCourierPool().getCouriers().stream() 49 | .filter(x->x.getOrders().isEmpty() 50 | ||x.getPlanRoutes().get(x.getPlanRoutes().size()-1).getActionTime() < context.getTimeStamp()+candidateCouriersRange) 51 | .collect(Collectors.toList()); 52 | } 53 | 54 | protected List getCandidateOrders(DispatchContext dispatchContext) { 55 | 56 | List r = dispatchContext.getOrderPool().getDispatchingOrders(); 57 | r.sort((a,b)->-((int) (a.getPromiseDeliverTime()-b.getPromiseDeliverTime()))); 58 | return r; 59 | } 60 | @Override 61 | public List getAssignedOrderIds() { 62 | return ordersAssigned.stream().map(Order::getId).collect(Collectors.toList()); 63 | } 64 | 65 | 66 | 67 | 68 | 69 | private List> generatePlanning(Courier courier,List orders,final int norder){ 70 | List> r = new ArrayList<>(); 71 | if(norder == 1){ 72 | for(Order o : orders){ 73 | List actions = new ArrayList<>(3); 74 | actions.add(new RichAction(null,o,1)); 75 | actions.add(new RichAction(null,o,3)); 76 | List temp = planner.plan(courier,new ArrayList<>(actions),this.context); 77 | assert temp.get(0) != null; 78 | temp.get(temp.size()-1).setCost(uitls.calculateCost(context,temp)); 79 | r.add(temp); 80 | } 81 | 82 | return r; 83 | } 84 | 85 | 86 | 87 | 88 | 89 | if(norder == 2){ 90 | List> r2 = PlanningUiils.planTwo(context, courier,orders,planner,1e-3 ); 91 | return r2; 92 | } 93 | List> orderset = new ArrayList<>(); 94 | long generateStartTime = System.currentTimeMillis(); 95 | dfsOrderGenerating(orders,norder,0,new ArrayList<>(),orderset,new HashMap<>(),3); 96 | for( List subOrders : orderset){ 97 | List> allPlans = new ArrayList<>(); 98 | dfsPlanning(subOrders.stream().map(x-> new MutablePair(x,0)).collect(Collectors.toList()), 0,new ArrayList(),allPlans); 99 | if(allPlans.isEmpty()){ 100 | continue; 101 | } 102 | 103 | double minCost = Double.MAX_VALUE; 104 | List tempr = null; 105 | for (List actions : allPlans){ 106 | 107 | actions = planner.plan(courier,actions,this.context); 108 | 109 | double c = uitls.calculateCost(context,actions); 110 | actions.get(actions.size()-1).setCost(c); 111 | if(c < minCost){ 112 | tempr = actions; 113 | minCost = c; 114 | } 115 | } 116 | r.add(tempr); 117 | } 118 | 119 | return r; 120 | 121 | 122 | } 123 | 124 | private void dfsOrderGenerating(final List allOrders,final int targetLength,final int index, 125 | List buffer,final List> result,final Map orderUsed,final int orderUsedLimit){ 126 | if(targetLength == buffer.size()){ 127 | List t = new ArrayList<>(buffer); 128 | result.add(t); 129 | for(Order order : buffer){ 130 | orderUsed.put(order.getId(),orderUsed.getOrDefault(order.getId(),0)+1); 131 | } 132 | return; 133 | } 134 | for(int i=index;i < allOrders.size();i++){ 135 | if(buffer.size() > 0 && orders.get(i).getSrcLoc().distanceTo(buffer.get(0).getSrcLoc()) > 0.0001*1e-5 136 | && orders.get(i).getDstLoc().distanceTo(buffer.get(0).getDstLoc()) > 0.0001*1e-5 137 | ){ 138 | continue; 139 | } 140 | if(orderUsed.getOrDefault(orders.get(i).getId(),0) > orderUsedLimit){ 141 | continue; 142 | } 143 | buffer.add(orders.get(i)); 144 | dfsOrderGenerating(allOrders,targetLength,i+1,buffer,result,orderUsed,orderUsedLimit); 145 | buffer.remove(buffer.size()-1); 146 | } 147 | } 148 | 149 | 150 | private void dfsPlanning(List> orders, int index, ArrayList buffer, List> result){ 151 | if(index == orders.size()*2 && !orders.isEmpty()){ 152 | assert !buffer.isEmpty(); 153 | result.add(new ArrayList<>(buffer) ); 154 | return; 155 | } 156 | for(Pair orderp : orders){ 157 | Order order = orderp.getKey(); 158 | int status = orderp.getValue(); 159 | if(status == 2){ 160 | continue; 161 | } 162 | int targetActionType = status == 0?1:3; 163 | orderp.setValue(status+1); 164 | buffer.add(new RichAction(null,order,targetActionType)); 165 | dfsPlanning(orders,index+1,buffer,result); 166 | buffer.remove(buffer.size()-1); 167 | orderp.setValue(status); 168 | } 169 | } 170 | 171 | 172 | @Override 173 | public List solve() { 174 | Map>> courier2ActionList= new ConcurrentHashMap<>(); 175 | final int maxPlaningOrder = 2; 176 | long startTime = System.currentTimeMillis(); 177 | long planStartTime = System.currentTimeMillis(); 178 | boolean flagOvertime = true; 179 | List>> groupOrder = PlanningUiils.groupOrderAll(orders,1e-3); 180 | List>> processedGroupOrder = PlanningUiils.processGroupedOrder(groupOrder,20); 181 | 182 | for(int iplanning=1;iplanning <= maxPlaningOrder;iplanning++){ 183 | final long limitTIme = iplanning == 1?1400:1400; 184 | List>>>> fs = new ArrayList<>(); 185 | for(Courier courier : couriers){ 186 | int finalIplanning = iplanning; 187 | Future>> f = forkJoinPool.submit(()->{ 188 | if(finalIplanning == 1 ){ 189 | return this.generatePlanning(courier,this.orders, finalIplanning); 190 | } 191 | List> couierPlan = null; 192 | couierPlan= PlanningUiils.computeCost(context,courier,planner,processedGroupOrder); 193 | return couierPlan; 194 | }); 195 | fs.add(new ImmutablePair<>(courier,f)); 196 | } 197 | for(int ifs=0;ifs < fs.size();ifs++){ 198 | Pair>>> p = fs.get(ifs); 199 | Courier courier = p.getKey(); 200 | List> courierAction=null; 201 | try { 202 | try { 203 | courierAction = p.getValue().get(Math.max(0,limitTIme-(System.currentTimeMillis()-startTime)),TimeUnit.MILLISECONDS); 204 | } catch (InterruptedException | ExecutionException | TimeoutException | CancellationException e) { 205 | 206 | flagOvertime=false; 207 | for(int j=ifs;j < fs.size();j++){ 208 | if(!fs.get(j).getValue().isCancelled()){ 209 | fs.get(j).getValue().cancel(true); 210 | } 211 | } 212 | } 213 | } 214 | catch (Exception e){ 215 | e.printStackTrace(); 216 | } 217 | if(!flagOvertime){ 218 | break; 219 | } 220 | if(courierAction == null){ 221 | continue; 222 | } 223 | for(List t : courierAction){ 224 | assert (t.get(0) != null); 225 | } 226 | if(!courier2ActionList.containsKey(courier)){ 227 | courier2ActionList.put(courier,new ArrayList<>()); 228 | } 229 | courier2ActionList.get(courier).addAll(courierAction); 230 | 231 | } 232 | if(!flagOvertime){ 233 | break; 234 | } 235 | 236 | } 237 | long planEndTime = System.currentTimeMillis(); 238 | 239 | 240 | Map m = new ConcurrentHashMap<>();//int[2] [total,exceed limit] 241 | orders.stream().forEach((o)->{ 242 | AtomicInteger[] x = new AtomicInteger[2]; 243 | x[0] = new AtomicInteger(0); 244 | x[1] = new AtomicInteger(0); 245 | m.put(o.getId(),x); 246 | }); 247 | try { 248 | forkJoinPool.submit(()->{ 249 | courier2ActionList.entrySet().stream().parallel().forEach( 250 | (e)->{ 251 | for(List actioneList : e.getValue()){ 252 | for(RichAction ra : actioneList){ 253 | if(ra.getAction_type() != 3){ 254 | continue; 255 | } 256 | if(ra.getActionNode().getActionTime() > ra.getOrder().getPromiseDeliverTime()){ 257 | m.get(ra.getOrder().getId())[1].incrementAndGet(); 258 | } 259 | m.get(ra.getOrder().getId())[0].incrementAndGet(); 260 | } 261 | } 262 | } 263 | ); 264 | 265 | }).get(); 266 | } catch (InterruptedException | ExecutionException e) { 267 | e.printStackTrace(); 268 | return new ArrayList<>(); 269 | } 270 | 271 | try { 272 | forkJoinPool.submit(()-> 273 | courier2ActionList.entrySet().stream().parallel().forEach( 274 | (e)->{ 275 | long ctime = -1; 276 | Courier courier = e.getKey(); 277 | if(courier.getPlanRoutes().isEmpty()){ 278 | ctime = context.getTimeStamp(); 279 | } 280 | else { 281 | ctime=courier.getPlanRoutes().get(courier.getPlanRoutes().size()-1).getActionTime(); 282 | } 283 | for(List actions : e.getValue()){ 284 | float importrtance = 0; 285 | int importanceFactor = 60 * 5; 286 | float courierFactor = 8.0f ; 287 | for(RichAction richAction : actions){ 288 | if(richAction.getAction_type() != 3){ 289 | continue; 290 | } 291 | AtomicInteger data[] = m.get(richAction.getOrder().getId()); 292 | if((double)data[1].get()/(data[0].get()) > 0.7 ){ 293 | importrtance++; 294 | } 295 | } 296 | double preCost = actions.get(actions.size()-1).getCost(); 297 | if(importrtance != 0){ 298 | importrtance /= ((double)actions.size())/3 ; 299 | } 300 | long timeDelta = Math.max(Math.min(ctime-context.getTimeStamp(),60 * 2),-60*5); 301 | 302 | actions.get(actions.size()-1).setCost(preCost - importanceFactor*importrtance + timeDelta* courierFactor); 303 | 304 | } 305 | } 306 | 307 | ) 308 | ).get(); 309 | } catch (InterruptedException | ExecutionException e) { 310 | e.printStackTrace(); 311 | return new ArrayList<>(); 312 | } 313 | 314 | AtomicInteger maxCandidateCount = new AtomicInteger(); 315 | try { 316 | forkJoinPool.submit(()-> 317 | courier2ActionList.entrySet().stream().parallel().forEach( 318 | (e)->{ 319 | Courier courier = e.getKey(); 320 | List> plans = e.getValue(); 321 | plans.sort((a,b)->{ 322 | double sign = a.get(a.size()-1).getCost() - b.get(b.size()-1).getCost(); 323 | if(sign == 0){ 324 | return 0; 325 | } 326 | return sign<0?-1:1; 327 | }); 328 | maxCandidateCount.set(Math.max(maxCandidateCount.get(), plans.size())); 329 | final int maxCandidate = 200; 330 | if(plans.size() > maxCandidate){ 331 | plans = plans.subList(0,maxCandidate); 332 | } 333 | e.setValue(plans); 334 | } 335 | ) 336 | ).get(); 337 | } catch (InterruptedException | ExecutionException e) { 338 | e.printStackTrace(); 339 | return new ArrayList<>(); 340 | } 341 | Set usedCourer = new HashSet<>(); 342 | Set usedOrder = new HashSet<>(); 343 | List submmitLength = new ArrayList<>(); 344 | List submitPlan = new ArrayList<>(); 345 | 346 | 347 | List>> allplans = new ArrayList<>(); 348 | for(Map.Entry>> e : courier2ActionList.entrySet()){ 349 | for(List actions : e.getValue()){ 350 | allplans.add(new ImmutablePair<>(e.getKey(),actions)); 351 | } 352 | } 353 | allplans.sort((a,b)->{ 354 | List a1 = a.getValue(); 355 | List a2 = b.getValue(); 356 | double sign = a1.get(a1.size()-1).getCost() - a2.get(a2.size()-1).getCost(); 357 | if(sign == 0){ 358 | return 0; 359 | } 360 | return sign<0?-1:1; 361 | }); 362 | for(Pair> p : allplans){ 363 | List minActions = p.getValue(); 364 | Courier minCourier = p.getKey(); 365 | if(usedCourer.contains(minCourier.getId())){ 366 | continue; 367 | } 368 | boolean flag = true; 369 | for(RichAction action : minActions){ 370 | if(usedOrder.contains(action.getOrder().getId())){ 371 | flag = false; 372 | break; 373 | } 374 | } 375 | if(!flag){ 376 | continue; 377 | } 378 | for(RichAction richAction : minActions){ 379 | //assert !usedOrder.contains(richAction.getOrder().getId()): usedOrder.toString()+"==="+richAction.getOrder().getId(); 380 | usedOrder.add(richAction.getOrder().getId()); 381 | } 382 | assert !usedCourer.contains(minCourier.getId()); 383 | usedCourer.add(minCourier.getId()); 384 | CourierPlan _ = new CourierPlan(); 385 | _.setPlanRoutes(minActions.stream().map(RichAction::getActionNode).collect(Collectors.toList())); 386 | _.setCourierId(minCourier.getId()); 387 | if(_.getPlanRoutes().get(0).getNeedSubmitTime() <= this.context.getTimeStamp() + MINIMUM_INTERVAL_SECONDS*1 388 | // && orders.size() < this.couriers.size()*5 389 | // || orders.size() > this.couriers.size()*5 && ordersAssigned.size() < 8 390 | // || true 391 | ){ 392 | submitPlan.add(_); 393 | 394 | minCourier.setPlanRoutes(minActions.stream().map(RichAction::getActionNode).collect(Collectors.toList())); 395 | Set addOrders = new HashSet<>(minActions.stream().map(RichAction::getOrder).collect(Collectors.toList())); 396 | List addOrders_ = new ArrayList<>(addOrders); 397 | minCourier.setOrders(addOrders_); 398 | ordersAssigned.addAll(addOrders_); 399 | 400 | submmitLength.add(addOrders.size()); 401 | 402 | } 403 | } 404 | 405 | 406 | maxUsedTime = Math.max(maxUsedTime,System.currentTimeMillis()-planStartTime); 407 | System.out.println(String.format("solve end plan Time %d total %d" + 408 | " max used time %d " + 409 | "",planEndTime-planStartTime,System.currentTimeMillis()-planStartTime,maxUsedTime)); 410 | 411 | // System.out.println(String.format("nSubmit %d nOrder %d nCourer usedCourer %d ",submitPlan.size(),this.orders.size(),this.couriers.size(),usedCourer.size())); 412 | // System.out.println("subbmits "+ submmitLength.toString()); 413 | return submitPlan; 414 | 415 | 416 | } 417 | 418 | 419 | private CourierPlan getSubmitPlan(Courier courier) { 420 | long submitThresholdTime = this.context.getTimeStamp() + MINIMUM_INTERVAL_SECONDS; 421 | List submittedNodes = courier.getPlanRoutes().stream() 422 | .filter(node -> !node.isSubmitted()) 423 | .filter(node -> node.getNeedSubmitTime() <= submitThresholdTime || context.isEndOfTest()) 424 | .collect(Collectors.toList()); 425 | CourierPlan plan = new CourierPlan(); 426 | plan.setCourierId(courier.getId()); 427 | plan.setPlanRoutes(submittedNodes); 428 | return plan; 429 | } 430 | 431 | } 432 | -------------------------------------------------------------------------------- /src/main/java/dispatch/demo/core/solver/packed/PlanningUiils.java: -------------------------------------------------------------------------------- 1 | package dispatch.demo.core.solver.packed; 2 | 3 | import dispatch.demo.core.context.DispatchContext; 4 | import dispatch.demo.core.route.Planner; 5 | import dispatch.demo.dto.Courier; 6 | import dispatch.demo.dto.Location; 7 | import dispatch.demo.dto.Order; 8 | import lombok.SneakyThrows; 9 | import org.apache.commons.lang3.tuple.ImmutablePair; 10 | 11 | import java.util.*; 12 | import java.util.concurrent.ConcurrentLinkedQueue; 13 | import java.util.concurrent.Future; 14 | import java.util.function.BiConsumer; 15 | import java.util.function.BiFunction; 16 | import java.util.stream.Collectors; 17 | import java.util.function.Function; 18 | import java.util.stream.IntStream; 19 | import org.apache.commons.lang3.tuple.Pair; 20 | import org.apache.logging.log4j.util.TriConsumer; 21 | 22 | 23 | public class PlanningUiils { 24 | public static List> planTwo(DispatchContext context, Courier courier, List orders, Planner planner,final double threshold){ 25 | // double threshold = 1e-5; 26 | List> r = new ArrayList<>(); 27 | final int maxUsed = 20 ; 28 | int MaxRangeUsed[] = new int[orders.size()]; 29 | int totalUsed[] = new int[orders.size()]; 30 | for(int i=0;i < orders.size();i++){ 31 | final Order o1 = orders.get(i); 32 | if(MaxRangeUsed[i] > maxUsed){ 33 | continue; 34 | } 35 | for(int j=i+1;j < orders.size();j++){ 36 | final Order o2 = orders.get(j); 37 | double d1 = o1.getSrcLoc().distanceTo(o2.getSrcLoc() ); 38 | double d2 = o1.getDstLoc().distanceTo(o2.getDstLoc()); 39 | if(d1> threshold 40 | && d2> threshold 41 | ){ 42 | continue; 43 | } 44 | if(d1> 1e-5 && d2 > 1e-5){ 45 | if(MaxRangeUsed[i] > maxUsed || MaxRangeUsed[j] > maxUsed){ 46 | continue; 47 | } 48 | MaxRangeUsed[i]++; 49 | MaxRangeUsed[j]++; 50 | } 51 | else{ 52 | if(totalUsed[i] > maxUsed*3 || totalUsed[j] > maxUsed*3 ){ 53 | continue; 54 | } 55 | totalUsed[i]++; 56 | totalUsed[j]++; 57 | } 58 | 59 | List> allPlanns = new ArrayList<>(); 60 | if(o1.getSrcLoc().distanceTo(o2.getSrcLoc() )<= threshold){ 61 | List p1 = Arrays.asList( 62 | new RichAction(null,o1,1),new RichAction(null,o2,1), 63 | new RichAction(null,o1,3),new RichAction(null,o2,3) 64 | ); 65 | List p2 = Arrays.asList( 66 | new RichAction(null,o1,1),new RichAction(null,o2,1), 67 | new RichAction(null,o2,3),new RichAction(null,o1,3) 68 | ); 69 | allPlanns.add(p1); 70 | allPlanns.add(p2); 71 | } 72 | if(o1.getDstLoc().distanceTo(o2.getDstLoc()) <= threshold){ 73 | List p3 = Arrays.asList( 74 | new RichAction(null,o2,1),new RichAction(null,o1,1), 75 | new RichAction(null,o1,3),new RichAction(null,o2,3) 76 | ); 77 | List p4 = Arrays.asList( 78 | new RichAction(null,o2,1),new RichAction(null,o1,1), 79 | new RichAction(null,o2,3),new RichAction(null,o1,3) 80 | ); 81 | allPlanns.add(p3); 82 | allPlanns.add(p4); 83 | } 84 | if(allPlanns.isEmpty()){ 85 | continue; 86 | } 87 | double minCost = Double.MAX_VALUE; 88 | List minPlan = null; 89 | 90 | for(List actionList : allPlanns){ 91 | List t = planner.plan(courier,actionList,context); 92 | double c = uitls.calculateCost(context,t); 93 | if(c < minCost){ 94 | minCost = c; 95 | minPlan = t; 96 | } 97 | } 98 | minPlan.get(minPlan.size()-1).setCost(minCost); 99 | r.add(minPlan); 100 | } 101 | } 102 | return r; 103 | } 104 | 105 | 106 | 107 | 108 | public static List> groupOrder(List orders,Function keySelector,double threshold){ 109 | Map,Set> msrc = new HashMap<>(); 110 | for ( Order o : orders){ 111 | long srclng = Math.round(keySelector.apply(o).getLongitude()/threshold); 112 | long srclat = Math.round(keySelector.apply(o).getLatitude()/threshold); 113 | List key = new ArrayList<>(2); 114 | key.add(srclng); 115 | key.add(srclat); 116 | if(!msrc.containsKey(key)){ 117 | msrc.put(key,new HashSet<>()); 118 | } 119 | msrc.get(key).add(o); 120 | 121 | 122 | srclng = (long) Math.floor(keySelector.apply(o).getLongitude()/threshold); 123 | srclat = (long) Math.round(keySelector.apply(o).getLatitude()/threshold); 124 | key.clear();key.add(srclng);key.add(srclat); 125 | if(!msrc.containsKey(key)){ 126 | msrc.put(key,new HashSet<>()); 127 | } 128 | msrc.get(key).add(o); 129 | 130 | srclng = (long) Math.round(keySelector.apply(o).getLongitude()/threshold); 131 | srclat = (long) Math.floor(keySelector.apply(o).getLatitude()/threshold); 132 | key.clear();key.add(srclng);key.add(srclat); 133 | if(!msrc.containsKey(key)){ 134 | msrc.put(key,new HashSet<>()); 135 | } 136 | msrc.get(key).add(o); 137 | 138 | 139 | srclng = (long) Math.floor(keySelector.apply(o).getLongitude()/threshold); 140 | srclat = (long) Math.floor(keySelector.apply(o).getLatitude()/threshold); 141 | key.clear();key.add(srclng);key.add(srclat); 142 | if(!msrc.containsKey(key)){ 143 | msrc.put(key,new HashSet<>()); 144 | } 145 | msrc.get(key).add(o); 146 | } 147 | List> rsrc = msrc.values().stream().map(ArrayList::new).collect(Collectors.toList()); 148 | return rsrc; 149 | } 150 | 151 | /** 152 | * 153 | * @param orders 154 | * @param threshold 155 | * @return (same start,groupid,group) 156 | */ 157 | @SneakyThrows 158 | public static List>> groupOrderAll(List orders, double threshold){ 159 | List>> r = new ArrayList<>(); 160 | Future>> f1 = PackedSolver.forkJoinPool.submit( 161 | ()-> { 162 | List> r1 = groupOrder(orders,Order::getSrcLoc,threshold); 163 | r1.stream().parallel().flatMap( 164 | (os)->{ 165 | List> t = new ArrayList<>(); 166 | if(os.size() < 20){ 167 | t.add(os); 168 | } 169 | else { 170 | t.addAll(groupOrder(orders,Order::getDstLoc,threshold)); 171 | } 172 | return t.stream(); 173 | } 174 | ).collect(Collectors.toList()); 175 | 176 | return r1; 177 | }); 178 | Future>> f2 = PackedSolver.forkJoinPool.submit( 179 | ()-> groupOrder(orders,Order::getDstLoc,threshold)); 180 | 181 | r.add(f1.get()); 182 | r.add(f2.get()); 183 | return r; 184 | } 185 | 186 | public static List>> processGroupedOrder(List>> orders,final int maxUsed){ 187 | Queue>> r2 = new ConcurrentLinkedQueue<>(); 188 | for(int oi=0;oi < 2;oi++){ 189 | orders.get(oi).stream().parallel().forEach( 190 | (groupOrders)->{ 191 | int[] used = new int[groupOrders.size()];; 192 | int[] usedSmall = new int[groupOrders.size()]; 193 | groupOrders.sort((a,b)-> (int) (a.getPromiseDeliverTime()-b.getPromiseDeliverTime())); 194 | 195 | for(int i=0;i < groupOrders.size();i++){ 196 | final Order o1 = groupOrders.get(i); 197 | if(used[i] > maxUsed && usedSmall[i] > maxUsed){ 198 | continue; 199 | } 200 | for(int j=i+1;j < groupOrders.size();j++){ 201 | final Order o2 = groupOrders.get(j); 202 | if(o1.getSrcLoc().distanceTo(o2.getSrcLoc()) < 1e-16 || o1.getDstLoc().distanceTo(o2.getDstLoc()) < 1e-16 ){ 203 | if(usedSmall[i] > maxUsed || usedSmall[j] > maxUsed){ 204 | continue; 205 | } 206 | usedSmall[i]++; 207 | usedSmall[j]++; 208 | } 209 | else{ 210 | if(used[i] > maxUsed || used[j] > maxUsed){ 211 | continue; 212 | } 213 | used[i]++; 214 | used[j]++; 215 | } 216 | 217 | double d1 = o1.getSrcLoc().distanceTo(o2.getSrcLoc() ); 218 | double d2 = o1.getDstLoc().distanceTo(o2.getDstLoc()); 219 | 220 | 221 | List> allPlanns = new ArrayList<>(); 222 | List p1 = Arrays.asList( 223 | new RichAction(null,o1,1),new RichAction(null,o2,1), 224 | new RichAction(null,o1,3),new RichAction(null,o2,3) 225 | ); 226 | List p2 = Arrays.asList( 227 | new RichAction(null,o1,1),new RichAction(null,o2,1), 228 | new RichAction(null,o2,3),new RichAction(null,o1,3) 229 | ); 230 | allPlanns.add(p1); 231 | allPlanns.add(p2); 232 | 233 | List p3 = Arrays.asList( 234 | new RichAction(null,o2,1),new RichAction(null,o1,1), 235 | new RichAction(null,o1,3),new RichAction(null,o2,3) 236 | ); 237 | List p4 = Arrays.asList( 238 | new RichAction(null,o2,1),new RichAction(null,o1,1), 239 | new RichAction(null,o2,3),new RichAction(null,o1,3) 240 | ); 241 | allPlanns.add(p3); 242 | allPlanns.add(p4); 243 | 244 | r2.add(allPlanns); 245 | } 246 | } 247 | } 248 | ); 249 | } 250 | 251 | List>> r = new ArrayList<>(r2); 252 | return r; 253 | } 254 | 255 | /** 256 | * 针对起点相近的订单进行处理 257 | * @param orders 258 | * @param maxUsed 259 | * @return 260 | */ 261 | public static List>> processExtraGroupedOrder(List> orders,final int maxUsed,double threshold){ 262 | // orders = groupOrder(orders,Order::getDstLoc,threshold); 263 | Queue> groups = new ConcurrentLinkedQueue<>(); 264 | orders.stream().parallel(). 265 | forEach(os->{ 266 | List> temp = groupOrder(os,Order::getDstLoc,threshold); 267 | groups.addAll(temp); 268 | }); 269 | Queue>> r2 = new ConcurrentLinkedQueue<>(); 270 | 271 | groups.stream().filter(x->x.size() >= 2).parallel() 272 | .forEach((g)->{ 273 | for(int end = 3;end < maxUsed && end< g.size();end++){ 274 | List temp = new ArrayList<>(); 275 | for(int s =0;s < end;s++){ 276 | temp.add(new RichAction(null, g.get(s),1)); 277 | } 278 | for(int s =0;s < end;s++){ 279 | temp.add(new RichAction(null, g.get(s),3)); 280 | } 281 | List> t= new ArrayList<>(); 282 | t.add(temp); 283 | r2.add(t); 284 | } 285 | }); 286 | 287 | List>> r = new ArrayList<>(); 288 | r.addAll(r2); 289 | return r; 290 | } 291 | 292 | 293 | static class DfsInterface{ 294 | public T func; 295 | }; 296 | 297 | public static List> generateExtra(final List>> orders,final long filterTime, 298 | final int minPlanning,final int maxPlanning){ 299 | Function keyselector = Order::getSrcLoc; 300 | assert orders.size() ==2; 301 | final int solveSize = 3; 302 | 303 | ConcurrentLinkedQueue> r = new ConcurrentLinkedQueue<>(); 304 | for(int temp0 = 0;temp0< 2;temp0++){ 305 | orders.get(temp0).stream().parallel().filter(os->os.size()>2).forEach( 306 | (os_)->{ 307 | //filter out new node 308 | final List os = os_.stream().filter(o->o.getEstimatedPrepareCompletedTime() > filterTime).collect(Collectors.toList()); 309 | //buildGraph 310 | List>> edges = new ArrayList<>(os.size()); 311 | for(int i=0;i < os.size();i++){ 312 | edges.add(new ArrayList<>()); 313 | } 314 | for(int i=0;i < os.size();i++){ 315 | for(int j=i+1;j < os.size();j++){ 316 | double distance = keyselector.apply(os.get(i)).distanceTo(keyselector.apply(os.get(j))); 317 | edges.get(i).add(new ImmutablePair<>(j,distance)); 318 | edges.get(j).add(new ImmutablePair<>(i,distance)); 319 | } 320 | } 321 | //greedy Search 322 | edges = edges.stream().map(x->{ 323 | x.sort((a,b)->{ 324 | double temp = a.getValue()-b.getValue(); 325 | return temp == 0?0:temp<0?1:-1; 326 | }); 327 | return x; 328 | }).map(x->{ 329 | if(x.size() > solveSize){ 330 | x = x.subList(0,solveSize); 331 | } 332 | return x; 333 | }).collect(Collectors.toList()); 334 | 335 | DfsInterface,Set>> dfsFunc= new DfsInterface<>(); 336 | List>> finalEdges = edges; 337 | TriConsumer,Set> dfs = (cur, buffer, used)->{ 338 | if(buffer.size() >maxPlanning){ 339 | return; 340 | } 341 | if(buffer.size() >= minPlanning){ 342 | r.add(new ArrayList<>(buffer)); 343 | } 344 | for(int i = 0; i< finalEdges.get(cur).size(); i++){ 345 | Order o = os.get(finalEdges.get(cur).get(i).getKey()); 346 | if(used.contains(o)){ 347 | continue; 348 | } 349 | used.add(o); 350 | buffer.add(o); 351 | dfsFunc.func.accept(i,buffer,used); 352 | used.remove(o); 353 | buffer.remove(buffer.size()-1); 354 | } 355 | }; 356 | dfsFunc.func = dfs; 357 | 358 | 359 | IntStream.range(0,edges.size()).parallel().forEach( 360 | (istart)->{ 361 | Set used = new HashSet<>(); 362 | used.add(os.get(istart)); 363 | dfsFunc.func.accept(istart,new ArrayList<>(),used); 364 | } 365 | ); 366 | } 367 | 368 | ); 369 | }; 370 | List> result = r.stream().parallel().map( 371 | (planOrders)->{ 372 | final List temp = new ArrayList<>(); 373 | planOrders.stream().forEach((o)->{ 374 | temp.add(new RichAction(null,o,1)); 375 | }); 376 | planOrders.stream().forEach((o)->{ 377 | temp.add(new RichAction(null,o,3)); 378 | }); 379 | return temp; 380 | } 381 | ).collect(Collectors.toList()); 382 | return result; 383 | } 384 | 385 | 386 | 387 | public static List> computeCost(DispatchContext context,Courier courier,Planner planner,List>> allPlans){ 388 | List> r = new ArrayList<>(); 389 | allPlans.stream().forEach( 390 | (plans)->{ 391 | if(plans.isEmpty()){ 392 | return; 393 | } 394 | double minCost = Double.MAX_VALUE; 395 | List minPlan = null; 396 | 397 | for(List actionList : plans){ 398 | List t = planner.plan(courier,actionList,context); 399 | double c = uitls.calculateCost(context,t); 400 | if(c < minCost){ 401 | minCost = c; 402 | minPlan = t; 403 | } 404 | } 405 | if(minPlan == null) { 406 | return; 407 | } 408 | if(minPlan.size()/3 >= courier.getMaxLoads()){ 409 | return; 410 | } 411 | minPlan.get(minPlan.size()-1).setCost(minCost); 412 | r.add(minPlan); 413 | } 414 | ); 415 | return r; 416 | } 417 | } 418 | --------------------------------------------------------------------------------