github.com/LdDl/ch@v1.7.8/dijkstra_bidirectional.go (about) 1 package ch 2 3 import ( 4 "container/heap" 5 "container/list" 6 ) 7 8 type direction int 9 10 const ( 11 forward direction = iota 12 backward 13 directionsCount 14 ) 15 16 // ShortestPath Computes and returns shortest path and it's cost (extended Dijkstra's algorithm) 17 // 18 // If there are some errors then function returns '-1.0' as cost and nil as shortest path 19 // 20 // source - user's definied ID of source vertex 21 // target - user's definied ID of target vertex 22 func (graph *Graph) ShortestPath(source, target int64) (float64, []int64) { 23 if source == target { 24 return 0, []int64{source} 25 } 26 endpoints := [directionsCount]int64{source, target} 27 for d, endpoint := range endpoints { 28 var ok bool 29 if endpoints[d], ok = graph.mapping[endpoint]; !ok { 30 return -1.0, nil 31 } 32 } 33 return graph.shortestPath(endpoints) 34 } 35 36 func (graph *Graph) initShortestPath() (queryDist [directionsCount][]float64, processed [directionsCount][]bool, queues [directionsCount]*vertexDistHeap) { 37 for d := forward; d < directionsCount; d++ { 38 queryDist[d] = make([]float64, len(graph.Vertices)) 39 for i := range queryDist[d] { 40 queryDist[d][i] = Infinity 41 } 42 processed[d] = make([]bool, len(graph.Vertices)) 43 queues[d] = &vertexDistHeap{} 44 heap.Init(queues[d]) 45 } 46 return 47 } 48 49 func (graph *Graph) shortestPath(endpoints [directionsCount]int64) (float64, []int64) { 50 queryDist, processed, queues := graph.initShortestPath() 51 for d := forward; d < directionsCount; d++ { 52 processed[d][endpoints[d]] = true 53 queryDist[d][endpoints[d]] = 0 54 heapEndpoint := &vertexDist{ 55 id: endpoints[d], 56 dist: 0, 57 } 58 heap.Push(queues[d], heapEndpoint) 59 } 60 return graph.shortestPathCore(queryDist, processed, queues) 61 } 62 63 func (graph *Graph) shortestPathCore(queryDist [directionsCount][]float64, processed [directionsCount][]bool, queues [directionsCount]*vertexDistHeap) (float64, []int64) { 64 var prev [directionsCount]map[int64]int64 65 for d := forward; d < directionsCount; d++ { 66 prev[d] = make(map[int64]int64) 67 } 68 estimate := Infinity 69 middleID := int64(-1) 70 for { 71 queuesProcessed := false 72 for d := forward; d < directionsCount; d++ { 73 if queues[d].Len() == 0 { 74 continue 75 } 76 queuesProcessed = true 77 reverseDirection := (d + 1) % directionsCount 78 graph.directionalSearch(d, queues[d], processed[d], processed[reverseDirection], queryDist[d], queryDist[reverseDirection], prev[d], &estimate, &middleID) 79 } 80 if !queuesProcessed { 81 break 82 } 83 } 84 if estimate == Infinity { 85 return -1.0, nil 86 } 87 return estimate, graph.ComputePath(middleID, prev[forward], prev[backward]) 88 } 89 90 func (graph *Graph) directionalSearch(d direction, q *vertexDistHeap, localProcessed, reverseProcessed []bool, localQueryDist, reverseQueryDist []float64, prev map[int64]int64, estimate *float64, middleID *int64) { 91 vertex := heap.Pop(q).(*vertexDist) 92 if vertex.dist <= *estimate { 93 localProcessed[vertex.id] = true 94 // Edge relaxation in a forward propagation 95 var vertexList []incidentEdge 96 if d == forward { 97 vertexList = graph.Vertices[vertex.id].outIncidentEdges 98 } else { 99 vertexList = graph.Vertices[vertex.id].inIncidentEdges 100 } 101 for i := range vertexList { 102 temp := vertexList[i].vertexID 103 cost := vertexList[i].weight 104 if graph.Vertices[vertex.id].orderPos < graph.Vertices[temp].orderPos { 105 alt := localQueryDist[vertex.id] + cost 106 if localQueryDist[temp] > alt { 107 localQueryDist[temp] = alt 108 prev[temp] = vertex.id 109 node := &vertexDist{ 110 id: temp, 111 dist: alt, 112 } 113 heap.Push(q, node) 114 } 115 } 116 } 117 } 118 if reverseProcessed[vertex.id] { 119 if vertex.dist+reverseQueryDist[vertex.id] < *estimate { 120 *middleID = vertex.id 121 *estimate = vertex.dist + reverseQueryDist[vertex.id] 122 } 123 } 124 } 125 126 // ShortestPathWithAlternatives Computes and returns shortest path and it's cost (extended Dijkstra's algorithm), 127 // with multiple alternatives for source and target vertices with additional distances to reach the vertices 128 // (useful if source and target are outside of the graph) 129 // 130 // If there are some errors then function returns '-1.0' as cost and nil as shortest path 131 // 132 // sources - user's definied ID of source vertex with additional penalty 133 // targets - user's definied ID of target vertex with additional penalty 134 func (graph *Graph) ShortestPathWithAlternatives(sources, targets []VertexAlternative) (float64, []int64) { 135 endpoints := [directionsCount][]VertexAlternative{sources, targets} 136 var endpointsInternal [directionsCount][]vertexAlternativeInternal 137 for d, alternatives := range endpoints { 138 endpointsInternal[d] = graph.vertexAlternativesToInternal(alternatives) 139 } 140 return graph.shortestPathWithAlternatives(endpointsInternal) 141 } 142 143 func (graph *Graph) shortestPathWithAlternatives(endpoints [directionsCount][]vertexAlternativeInternal) (float64, []int64) { 144 queryDist, processed, queues := graph.initShortestPath() 145 for d := forward; d < directionsCount; d++ { 146 for _, endpoint := range endpoints[d] { 147 if endpoint.vertexNum == vertexNotFound { 148 continue 149 } 150 processed[d][endpoint.vertexNum] = true 151 queryDist[d][endpoint.vertexNum] = endpoint.additionalDistance 152 heapEndpoint := &vertexDist{ 153 id: endpoint.vertexNum, 154 dist: endpoint.additionalDistance, 155 } 156 heap.Push(queues[d], heapEndpoint) 157 } 158 } 159 return graph.shortestPathCore(queryDist, processed, queues) 160 } 161 162 // ComputePath Returns slice of IDs (user defined) of computed path 163 func (graph *Graph) ComputePath(middleID int64, forwardPrev, backwardPrev map[int64]int64) []int64 { 164 l := list.New() 165 l.PushBack(middleID) 166 u := middleID 167 var ok bool 168 for { 169 if u, ok = forwardPrev[u]; ok { 170 l.PushFront(u) 171 } else { 172 break 173 } 174 } 175 u = middleID 176 for { 177 if u, ok = backwardPrev[u]; ok { 178 l.PushBack(u) 179 } else { 180 break 181 } 182 } 183 ok = true 184 for ok { 185 ok = false 186 for e := l.Front(); e.Next() != nil; e = e.Next() { 187 if contractedNode, ok2 := graph.shortcuts[e.Value.(int64)][e.Next().Value.(int64)]; ok2 { 188 ok = true 189 l.InsertAfter(contractedNode.Via, e) 190 } 191 } 192 } 193 194 var path = make([]int64, 0, l.Len()) 195 for e := l.Front(); e != nil; e = e.Next() { 196 path = append(path, graph.Vertices[e.Value.(int64)].Label) 197 } 198 199 return path 200 }