| | |
| | | |
| | | private static final long REDIS_MAP_CACHE_TTL_SECONDS = 60 * 60 * 24L; |
| | | private static final long MAP_CACHE_TTL_MS = 5000L; |
| | | private static final long SIMPLE_PATH_SEARCH_TIMEOUT_MS = 3000L; |
| | | private static final Map<String, CachedNavigateMap> NAVIGATE_MAP_CACHE = new ConcurrentHashMap<>(); |
| | | private static final Map<String, CachedNavigateMapIndex> NAVIGATE_MAP_INDEX_CACHE = new ConcurrentHashMap<>(); |
| | | |
| | | // Open表用优先队列 |
| | | public PriorityQueue<NavigateNode> Open = new PriorityQueue<NavigateNode>(); |
| | | //Close表用普通的数组 |
| | | public ArrayList<NavigateNode> Close = new ArrayList<NavigateNode>(); |
| | | //Close表用坐标key集合,O(1)查找 |
| | | public Set<String> Close = new HashSet<>(); |
| | | //用来存放已经出现过的结点。 |
| | | Map<String, Integer> bestGMap = new HashMap<>(); |
| | | |
| | |
| | | } |
| | | |
| | | //将这个结点加入到Close表中 |
| | | Close.add(current_node); |
| | | Close.add(keyOf(current_node)); |
| | | //对当前结点进行扩展,得到一个四周结点的数组 |
| | | ArrayList<NavigateNode> neighbour_node = extend_current_node(map, current_node); |
| | | //对这个结点遍历,看是否有目标结点出现 |
| | | for (NavigateNode node : neighbour_node) { |
| | | // 已在关闭列表中的节点不再处理,避免父链反复改写形成环 |
| | | if (Close.contains(node)) { |
| | | if (Close.contains(keyOf(node))) { |
| | | continue; |
| | | } |
| | | // G + H + E (对启发函数增加去拐点方案calcNodeExtraCost) |
| | |
| | | node.setH(calcNodeCost(node, end)); |
| | | node.setF(node.getG() + node.getH()); |
| | | |
| | | String key = node.getX() + "_" + node.getY(); |
| | | String key = keyOf(node); |
| | | Integer recordedG = bestGMap.get(key); |
| | | // 仅当找到更小的代价时才更新与入Open,避免等价代价反复入队导致父链抖动 |
| | | if (recordedG == null || node.getG() < recordedG) { |
| | |
| | | |
| | | AtomicInteger pathCount = new AtomicInteger(0); |
| | | Map<Integer, NavigateNode> guideNodeMap = buildGuideNodeMap(map, guideStationIds); |
| | | SimplePathSearchContext searchContext = new SimplePathSearchContext( |
| | | start, |
| | | end, |
| | | maxDepth, |
| | | maxPaths, |
| | | maxCost, |
| | | System.currentTimeMillis() + SIMPLE_PATH_SEARCH_TIMEOUT_MS |
| | | ); |
| | | |
| | | dfsAllSimplePaths(map, start, end, |
| | | visited, path, results, |
| | |
| | | maxDepth, maxPaths, maxCost, |
| | | pathCount, |
| | | guideStationIds, |
| | | guideNodeMap |
| | | guideNodeMap, |
| | | searchContext |
| | | ); |
| | | |
| | | return results; |
| | |
| | | int maxCost, |
| | | AtomicInteger pathCount, |
| | | List<Integer> guideStationIds, |
| | | Map<Integer, NavigateNode> guideNodeMap |
| | | Map<Integer, NavigateNode> guideNodeMap, |
| | | SimplePathSearchContext searchContext |
| | | ) { |
| | | if (isSimplePathSearchTimedOut(searchContext, results, pathCount, depth)) { |
| | | return; |
| | | } |
| | | |
| | | // 防爆:条数限制 |
| | | if (maxPaths > 0 && pathCount.get() >= maxPaths) return; |
| | | |
| | |
| | | neighbors = sortNeighborsByGuide(current, path, neighbors, guideStationIds, guideNodeMap); |
| | | |
| | | for (NavigateNode next : neighbors) { |
| | | if (isSimplePathSearchTimedOut(searchContext, results, pathCount, depth)) { |
| | | return; |
| | | } |
| | | |
| | | // 防爆:条数限制 |
| | | if (maxPaths > 0 && pathCount.get() >= maxPaths) return; |
| | | |
| | |
| | | maxDepth, maxPaths, maxCost, |
| | | pathCount, |
| | | guideStationIds, |
| | | guideNodeMap |
| | | guideNodeMap, |
| | | searchContext |
| | | ); |
| | | |
| | | // 回溯 |
| | | path.removeLast(); |
| | | visited.remove(nk); |
| | | } |
| | | } |
| | | |
| | | private boolean isSimplePathSearchTimedOut(SimplePathSearchContext searchContext, |
| | | List<List<NavigateNode>> results, |
| | | AtomicInteger pathCount, |
| | | int depth) { |
| | | if (searchContext == null || searchContext.timeoutLogged) { |
| | | return searchContext != null && searchContext.timeoutLogged; |
| | | } |
| | | if (System.currentTimeMillis() < searchContext.deadlineMs) { |
| | | return false; |
| | | } |
| | | |
| | | searchContext.timeoutLogged = true; |
| | | Integer startStationId = extractStationId(searchContext.startNode); |
| | | Integer endStationId = extractStationId(searchContext.endNode); |
| | | com.zy.core.News.warn("站点路径候选枚举超时返回,startStationId={},endStationId={},timeoutMs={},foundPathCount={},resultSize={},depth={},maxDepth={},maxPaths={},maxCost={}", |
| | | startStationId, |
| | | endStationId, |
| | | SIMPLE_PATH_SEARCH_TIMEOUT_MS, |
| | | pathCount == null ? 0 : pathCount.get(), |
| | | results == null ? 0 : results.size(), |
| | | depth, |
| | | searchContext.maxDepth, |
| | | searchContext.maxPaths, |
| | | searchContext.maxCost); |
| | | return true; |
| | | } |
| | | |
| | | private String keyOf(NavigateNode n) { |
| | |
| | | } |
| | | } |
| | | |
| | | private static class SimplePathSearchContext { |
| | | private final NavigateNode startNode; |
| | | private final NavigateNode endNode; |
| | | private final int maxDepth; |
| | | private final int maxPaths; |
| | | private final int maxCost; |
| | | private final long deadlineMs; |
| | | private boolean timeoutLogged; |
| | | |
| | | private SimplePathSearchContext(NavigateNode startNode, |
| | | NavigateNode endNode, |
| | | int maxDepth, |
| | | int maxPaths, |
| | | int maxCost, |
| | | long deadlineMs) { |
| | | this.startNode = startNode; |
| | | this.endNode = endNode; |
| | | this.maxDepth = maxDepth; |
| | | this.maxPaths = maxPaths; |
| | | this.maxCost = maxCost; |
| | | this.deadlineMs = deadlineMs; |
| | | } |
| | | } |
| | | |
| | | private static class CachedNavigateMap { |
| | | private final long cacheAtMs; |
| | | private final List<List<NavigateNode>> navigateMap; |