Loading

Java KSP 算法实现

思路

KSP算法

先用BFS/Dijkstra算出第一条路径P;

把P上除了终点以外的其他点,作为偏移点,并将偏移点在P上的出路作为必排,偏移点作为起点,重新算出偏移点到终点的新路,补上起点到偏移点的路径,取所有新路里权重最小的路作为P2;

以P2作为原路径,重复上一步,得到P3;

依次循环k次(k人为决定),得到Pk。

代码

涉及的数据结构复用Java 迪杰斯特拉 算法实现 里的结构

public class KSP {

    private final List<GraphNode> graph;

    private final String startNodeId;

    private final String endNodeId;

    private final Constraint constraint;

    private final Integer cycleNum;

    public KSP(List<GraphNode> graphNodes, String startNodeId, String endNodeId, Constraint constraint, int k) {
        this.graph = new ArrayList<>(graphNodes);
        this.startNodeId = startNodeId;
        this.endNodeId = endNodeId;
        this.constraint = constraint;
        this.cycleNum = k;
    }

    public CalcResult calcShortPath() {
        Dijkstra dijkstra = new Dijkstra(graph, startNodeId, endNodeId, constraint);
        CalcResult calcResult = dijkstra.calcShortPath(false);
        List<String> lastTimePath = new ArrayList<>(calcResult.getResultPath());
        List<CalcResult> curCalcResults = new ArrayList<>();
        for (int i = 1; i < cycleNum; i++) {
            for (int m = 0; m < lastTimePath.size() - 1; m++) {
                String offsetNode = lastTimePath.get(m);
                Pair<String, String> excludeLink = new Pair<>(offsetNode, lastTimePath.get(m + 1));
                // 偏移点之前的路径和权重
                Pair<List<String>, Integer> subResult = getSubOffsetPath(lastTimePath, m);
                // 重新组织约束
                Constraint cycleConstraint = createCycleConstraint(excludeLink, subResult);
                // 偏移点为起点,重新算路
                dijkstra = new Dijkstra(graph, offsetNode, endNodeId, cycleConstraint);
                CalcResult tempResult = dijkstra.calcShortPath(false);
                // 和偏移点前的路径组合,拼成完整路径
                stitchFullPath(calcResult, curCalcResults, subResult, tempResult);
            }
            if (curCalcResults.isEmpty()) {
                printResult(calcResult);
                return calcResult;
            }
            // 过滤出当前轮次权重最小的路径,放入最终结果中,并作为下一轮算路的P路径
            lastTimePath = filterMinWeightPath(calcResult, curCalcResults);
        }
        printResult(calcResult);
        return calcResult;
    }

    private List<String> filterMinWeightPath(CalcResult calcResult, List<CalcResult> curCalcResults) {
        List<String> lastTimePath = new ArrayList<>();
        Optional<CalcResult> curMinWeightResult = curCalcResults.stream().min(Comparator.comparing(CalcResult::getWeight));
        if(curMinWeightResult.isPresent()){
            lastTimePath = curMinWeightResult.get().getResultPath();
            calcResult.getOtherPaths().add(new Pair<>(curMinWeightResult.get().getResultPath(), curMinWeightResult.get().getWeight()));
            curCalcResults.clear();
        }
        return lastTimePath;
    }

    private void printResult(CalcResult calcResult) {
        System.out.println("first path:" + calcResult.getResultPath());
        System.out.println("first weight:" + calcResult.getWeight());
        calcResult.getOtherPaths().forEach(pair -> {
            System.out.println("second path:" + pair.getKey());
            System.out.println("second weight:" + pair.getValue());
        });
    }

    private void stitchFullPath(CalcResult calcResult, List<CalcResult> curCalcResults, Pair<List<String>, Integer> subResult, CalcResult tempResult) {
        subResult.getKey().addAll(tempResult.getResultPath());
        tempResult.setResultPath(subResult.getKey().stream().distinct().collect(Collectors.toList()));
        tempResult.setWeight(subResult.getValue() + tempResult.getWeight());
        if (!hasSameRoute(calcResult, tempResult)) {
            curCalcResults.add(tempResult);
        }
    }

    private Pair<List<String>, Integer> getSubOffsetPath(List<String> lastTimePath, int m) {
        List<String> subOffsetPath = new ArrayList<>();
        Integer subOffsetWeight = 0;
        if (m != 0) {
            List<String> temp = new ArrayList<>(lastTimePath);
            subOffsetPath = temp.subList(0, m + 1);
            subOffsetWeight = getWeight(subOffsetPath);
        }
        return new Pair<>(subOffsetPath,subOffsetWeight);
    }

    private Constraint createCycleConstraint(Pair<String, String> excludeLink, Pair<List<String>,Integer> subOffsetResult) {
        Constraint cycleConstraint = new Constraint();
        cycleConstraint.setExcludeLink(Lists.newArrayList(excludeLink));
        if (constraint != null) {
            cycleConstraint.getExcludeLink().addAll(constraint.getExcludeLink());
            cycleConstraint.setExcludeNode(constraint.getExcludeNode());
            for (String node : constraint.getIncludeNode()) {
                if (subOffsetResult.getKey().contains(node)) {
                    continue;
                }
                cycleConstraint.getIncludeNode().add(node);
            }
        }
        if(!subOffsetResult.getKey().isEmpty()){
            cycleConstraint.getExcludeNode().addAll(subOffsetResult.getKey().subList(0, subOffsetResult.getKey().size() - 1));
        }
        return cycleConstraint;
    }

    private boolean hasSameRoute(CalcResult calcResult, CalcResult tempResult) {
        List<List<String>> allRoute = new ArrayList<>();
        allRoute.add(calcResult.getResultPath());
        calcResult.getOtherPaths().forEach(pair -> allRoute.add(pair.getKey()));
        List<String> tempRoute = tempResult.getResultPath();

        return allRoute.stream().anyMatch(route -> {
            if (route.size() != tempRoute.size()) {
                return false;
            }
            for (int i = 0; i < route.size(); i++) {
                if (!Objects.equals(route.get(i), tempRoute.get(i))) {
                    return false;
                }
            }
            return true;
        });
    }

    private Integer getWeight(List<String> subOffsetPath) {
        if (subOffsetPath == null || subOffsetPath.isEmpty()) {
            return 0;
        }
        Integer weight = 0;
        for (int i = 0; i < subOffsetPath.size() - 1; i++) {
            String firstNodeId = subOffsetPath.get(i);
            String secondNodeId = subOffsetPath.get(i + 1);
            GraphNode firstNode = this.graph.stream().filter(node ->
                    Objects.equals(node.getNodeId(), firstNodeId)).findFirst().orElse(null);
            if (firstNode != null) {
                Map<String, Integer> nearNode = firstNode.getNearNodeValueTable();
                weight += nearNode.get(secondNodeId);
            }
        }
        return weight;
    }
}
posted @ 2023-09-04 11:02  星流残阳  阅读(99)  评论(0编辑  收藏  举报