github.com/LdDl/ch@v1.7.8/bidirectional_ch_one_to_n.go (about) 1 package ch 2 3 import ( 4 "container/heap" 5 ) 6 7 func (graph *Graph) initShortestPathOneToMany() (estimateAll []float64, pathAll [][]int64, prev map[int64]int64, prevReverse map[int64]int64, queryDist, revQueryDist []float64, forwProcessed, revProcessed []int64) { 8 estimateAll = []float64{} 9 pathAll = [][]int64{} 10 11 prev = make(map[int64]int64) 12 prevReverse = make(map[int64]int64) 13 14 queryDist = make([]float64, len(graph.Vertices)) 15 revQueryDist = make([]float64, len(graph.Vertices)) 16 17 forwProcessed = make([]int64, len(graph.Vertices)) 18 revProcessed = make([]int64, len(graph.Vertices)) 19 20 return 21 } 22 23 // ShortestPathOneToMany computes and returns shortest paths and theirs's costs (extended Dijkstra's algorithm) between single source and multiple targets 24 // 25 // If there are some errors then function returns '-1.0' as cost and nil as shortest path 26 // 27 // source - user's definied ID of source vertex 28 // targets - set of user's definied IDs of target vertices 29 func (graph *Graph) ShortestPathOneToMany(source int64, targets []int64) ([]float64, [][]int64) { 30 estimateAll, pathAll, prev, prevReverse, queryDist, revQueryDist, forwProcessed, revProcessed := graph.initShortestPathOneToMany() 31 32 var ok bool 33 if source, ok = graph.mapping[source]; !ok { 34 estimateAll = append(estimateAll, -1.0) 35 pathAll = append(pathAll, nil) 36 return estimateAll, pathAll 37 } 38 39 for idx, target := range targets { 40 nextQueue := int64(idx) + 1 41 if source == target { 42 estimateAll = append(estimateAll, 0) 43 pathAll = append(pathAll, []int64{source}) 44 continue 45 } 46 var ok bool 47 if target, ok = graph.mapping[target]; !ok { 48 estimateAll = append(estimateAll, -1.0) 49 pathAll = append(pathAll, nil) 50 continue 51 } 52 53 forwProcessed[source] = nextQueue 54 revProcessed[target] = nextQueue 55 56 queryDist[source] = 0 57 revQueryDist[target] = 0 58 59 forwQ := &vertexDistHeap{} 60 backwQ := &vertexDistHeap{} 61 62 heap.Init(forwQ) 63 heap.Init(backwQ) 64 65 heapSource := &vertexDist{ 66 id: source, 67 dist: 0, 68 } 69 heapTarget := &vertexDist{ 70 id: target, 71 dist: 0, 72 } 73 74 heap.Push(forwQ, heapSource) 75 heap.Push(backwQ, heapTarget) 76 77 estimate, path := graph.shortestPathOneToManyCore(nextQueue, prev, prevReverse, queryDist, revQueryDist, forwProcessed, revProcessed, forwQ, backwQ) 78 79 estimateAll = append(estimateAll, estimate) 80 pathAll = append(pathAll, path) 81 } 82 83 return estimateAll, pathAll 84 } 85 86 func (graph *Graph) shortestPathOneToManyCore(nextQueue int64, prev map[int64]int64, prevReverse map[int64]int64, queryDist, revQueryDist []float64, forwProcessed, revProcessed []int64, forwQ *vertexDistHeap, backwQ *vertexDistHeap) (float64, []int64) { 87 estimate := Infinity 88 89 var middleID int64 90 91 for forwQ.Len() != 0 || backwQ.Len() != 0 { 92 if forwQ.Len() != 0 { 93 vertex1 := heap.Pop(forwQ).(*vertexDist) 94 if vertex1.dist <= estimate { 95 forwProcessed[vertex1.id] = nextQueue 96 graph.relaxEdgesBiForwardOneToMany(vertex1, forwQ, prev, queryDist, nextQueue, forwProcessed) 97 } 98 if revProcessed[vertex1.id] == nextQueue { 99 if vertex1.dist+revQueryDist[vertex1.id] < estimate { 100 middleID = vertex1.id 101 estimate = vertex1.dist + revQueryDist[vertex1.id] 102 } 103 } 104 } 105 106 if backwQ.Len() != 0 { 107 vertex2 := heap.Pop(backwQ).(*vertexDist) 108 if vertex2.dist <= estimate { 109 revProcessed[vertex2.id] = nextQueue 110 graph.relaxEdgesBiBackwardOneToMany(vertex2, backwQ, prevReverse, revQueryDist, nextQueue, revProcessed) 111 } 112 113 if forwProcessed[vertex2.id] == nextQueue { 114 if vertex2.dist+queryDist[vertex2.id] < estimate { 115 middleID = vertex2.id 116 estimate = vertex2.dist + queryDist[vertex2.id] 117 } 118 } 119 } 120 121 } 122 if estimate == Infinity { 123 return -1, nil 124 } 125 126 return estimate, graph.ComputePath(middleID, prev, prevReverse) 127 } 128 129 // ShortestPathOneToManyWithAlternatives Computes and returns shortest path and it's cost (extended Dijkstra's algorithm) between single source and multiple targets 130 // with multiple alternatives for source and target vertices with additional distances to reach the vertices 131 // (useful if source and target are outside of the graph) 132 // 133 // If there are some errors then function returns '-1.0' as cost and nil as shortest path 134 // 135 // sourceAlternatives - user's definied ID of source vertex with additional penalty 136 // targetsAlternatives - set of user's definied IDs of target vertices with additional penalty 137 func (graph *Graph) ShortestPathOneToManyWithAlternatives(sourceAlternatives []VertexAlternative, targetsAlternatives [][]VertexAlternative) ([]float64, [][]int64) { 138 estimateAll, pathAll, prev, prevReverse, queryDist, revQueryDist, forwProcessed, revProcessed := graph.initShortestPathOneToMany() 139 140 sourceAlternativesInternal := graph.vertexAlternativesToInternal(sourceAlternatives) 141 142 for idx, targetAlternatives := range targetsAlternatives { 143 nextQueue := int64(idx) + 1 144 145 targetAlternativesInternal := graph.vertexAlternativesToInternal(targetAlternatives) 146 147 forwQ := &vertexDistHeap{} 148 backwQ := &vertexDistHeap{} 149 150 heap.Init(forwQ) 151 heap.Init(backwQ) 152 153 for _, sourceAlternative := range sourceAlternativesInternal { 154 if sourceAlternative.vertexNum == vertexNotFound { 155 continue 156 } 157 forwProcessed[sourceAlternative.vertexNum] = nextQueue 158 queryDist[sourceAlternative.vertexNum] = sourceAlternative.additionalDistance 159 160 heapSource := &vertexDist{ 161 id: sourceAlternative.vertexNum, 162 dist: sourceAlternative.additionalDistance, 163 } 164 heap.Push(forwQ, heapSource) 165 } 166 for _, targetAlternative := range targetAlternativesInternal { 167 if targetAlternative.vertexNum == vertexNotFound { 168 continue 169 } 170 revProcessed[targetAlternative.vertexNum] = nextQueue 171 revQueryDist[targetAlternative.vertexNum] = targetAlternative.additionalDistance 172 173 heapTarget := &vertexDist{ 174 id: targetAlternative.vertexNum, 175 dist: targetAlternative.additionalDistance, 176 } 177 heap.Push(backwQ, heapTarget) 178 } 179 180 estimate, path := graph.shortestPathOneToManyCore(nextQueue, prev, prevReverse, queryDist, revQueryDist, forwProcessed, revProcessed, forwQ, backwQ) 181 182 estimateAll = append(estimateAll, estimate) 183 pathAll = append(pathAll, path) 184 } 185 186 return estimateAll, pathAll 187 } 188 189 func (graph *Graph) relaxEdgesBiForwardOneToMany(vertex *vertexDist, forwQ *vertexDistHeap, prev map[int64]int64, queryDist []float64, cid int64, forwProcessed []int64) { 190 vertexList := graph.Vertices[vertex.id].outIncidentEdges 191 for i := range vertexList { 192 temp := vertexList[i].vertexID 193 cost := vertexList[i].weight 194 if graph.Vertices[vertex.id].orderPos < graph.Vertices[temp].orderPos { 195 alt := queryDist[vertex.id] + cost 196 if forwProcessed[temp] != cid || queryDist[temp] > alt { 197 queryDist[temp] = alt 198 prev[temp] = vertex.id 199 forwProcessed[temp] = cid 200 node := &vertexDist{ 201 id: temp, 202 dist: alt, 203 } 204 heap.Push(forwQ, node) 205 } 206 } 207 } 208 } 209 210 func (graph *Graph) relaxEdgesBiBackwardOneToMany(vertex *vertexDist, backwQ *vertexDistHeap, prev map[int64]int64, revQueryDist []float64, cid int64, revProcessed []int64) { 211 vertexList := graph.Vertices[vertex.id].inIncidentEdges 212 for i := range vertexList { 213 temp := vertexList[i].vertexID 214 cost := vertexList[i].weight 215 if graph.Vertices[vertex.id].orderPos < graph.Vertices[temp].orderPos { 216 alt := revQueryDist[vertex.id] + cost 217 if revProcessed[temp] != cid || revQueryDist[temp] > alt { 218 revQueryDist[temp] = alt 219 prev[temp] = vertex.id 220 revProcessed[temp] = cid 221 node := &vertexDist{ 222 id: temp, 223 dist: alt, 224 } 225 heap.Push(backwQ, node) 226 } 227 } 228 } 229 }