github.com/jingcheng-WU/gonum@v0.9.1-0.20210323123734-f1a2a11a8f7b/graph/path/yen_ksp.go (about) 1 // Copyright ©2018 The Gonum Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package path 6 7 import ( 8 "sort" 9 10 "github.com/jingcheng-WU/gonum/graph" 11 "github.com/jingcheng-WU/gonum/graph/iterator" 12 ) 13 14 // YenKShortestPaths returns the k-shortest loopless paths from s to t in g. 15 // YenKShortestPaths will panic if g contains a negative edge weight. 16 func YenKShortestPaths(g graph.Graph, k int, s, t graph.Node) [][]graph.Node { 17 _, isDirected := g.(graph.Directed) 18 yk := yenKSPAdjuster{ 19 Graph: g, 20 isDirected: isDirected, 21 } 22 23 if wg, ok := g.(Weighted); ok { 24 yk.weight = wg.Weight 25 } else { 26 yk.weight = UniformCost(g) 27 } 28 29 shortest, _ := DijkstraFrom(s, yk).To(t.ID()) 30 switch len(shortest) { 31 case 0: 32 return nil 33 case 1: 34 return [][]graph.Node{shortest} 35 } 36 paths := [][]graph.Node{shortest} 37 38 var pot []yenShortest 39 var root []graph.Node 40 for i := int64(1); i < int64(k); i++ { 41 for n := 0; n < len(paths[i-1])-1; n++ { 42 yk.reset() 43 44 spur := paths[i-1][n] 45 root := append(root[:0], paths[i-1][:n+1]...) 46 47 for _, path := range paths { 48 if len(path) <= n { 49 continue 50 } 51 ok := true 52 for x := 0; x < len(root); x++ { 53 if path[x].ID() != root[x].ID() { 54 ok = false 55 break 56 } 57 } 58 if ok { 59 yk.removeEdge(path[n].ID(), path[n+1].ID()) 60 } 61 } 62 63 spath, weight := DijkstraFrom(spur, yk).To(t.ID()) 64 if len(root) > 1 { 65 var rootWeight float64 66 for x := 1; x < len(root); x++ { 67 w, _ := yk.weight(root[x-1].ID(), root[x].ID()) 68 rootWeight += w 69 } 70 root = append(root[:len(root)-1], spath...) 71 pot = append(pot, yenShortest{root, weight + rootWeight}) 72 } else { 73 pot = append(pot, yenShortest{spath, weight}) 74 } 75 } 76 77 if len(pot) == 0 { 78 break 79 } 80 81 sort.Sort(byPathWeight(pot)) 82 best := pot[0].path 83 if len(best) <= 1 { 84 break 85 } 86 paths = append(paths, best) 87 pot = pot[1:] 88 } 89 90 return paths 91 } 92 93 // yenShortest holds a path and its weight for sorting. 94 type yenShortest struct { 95 path []graph.Node 96 weight float64 97 } 98 99 type byPathWeight []yenShortest 100 101 func (s byPathWeight) Len() int { return len(s) } 102 func (s byPathWeight) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 103 func (s byPathWeight) Less(i, j int) bool { return s[i].weight < s[j].weight } 104 105 // yenKSPAdjuster allows walked edges to be omitted from a graph 106 // without altering the embedded graph. 107 type yenKSPAdjuster struct { 108 graph.Graph 109 isDirected bool 110 111 // weight is the edge weight function 112 // used for shortest path calculation. 113 weight Weighting 114 115 // visitedEdges holds the edges that have 116 // been removed by Yen's algorithm. 117 visitedEdges map[[2]int64]struct{} 118 } 119 120 func (g yenKSPAdjuster) From(id int64) graph.Nodes { 121 nodes := graph.NodesOf(g.Graph.From(id)) 122 for i := 0; i < len(nodes); { 123 if g.canWalk(id, nodes[i].ID()) { 124 i++ 125 continue 126 } 127 nodes[i] = nodes[len(nodes)-1] 128 nodes = nodes[:len(nodes)-1] 129 } 130 return iterator.NewOrderedNodes(nodes) 131 } 132 133 func (g yenKSPAdjuster) canWalk(u, v int64) bool { 134 _, ok := g.visitedEdges[[2]int64{u, v}] 135 return !ok 136 } 137 138 func (g yenKSPAdjuster) removeEdge(u, v int64) { 139 g.visitedEdges[[2]int64{u, v}] = struct{}{} 140 if g.isDirected { 141 g.visitedEdges[[2]int64{v, u}] = struct{}{} 142 } 143 } 144 145 func (g *yenKSPAdjuster) reset() { 146 g.visitedEdges = make(map[[2]int64]struct{}) 147 } 148 149 func (g yenKSPAdjuster) Weight(xid, yid int64) (w float64, ok bool) { 150 return g.weight(xid, yid) 151 }