68 lines
1.9 KiB
Go
68 lines
1.9 KiB
Go
|
// Copyright (c) 2016 Bitfury Group Limited
|
||
|
// Distributed under the MIT software license, see the accompanying
|
||
|
// file LICENSE or http://www.opensource.org/licenses/mit-license.php
|
||
|
|
||
|
package graph
|
||
|
|
||
|
import "math"
|
||
|
|
||
|
// KShortestPaths finds k shortest paths
|
||
|
// Note: this implementation finds k path not necessary shortest
|
||
|
// It tries to make that distinct and shortest at the same time
|
||
|
func KShortestPaths(g *Graph, source, target Vertex, k int) ([][]Vertex, error) {
|
||
|
ksp := make([][]Vertex, 0, k)
|
||
|
DRY := make(map[string]bool)
|
||
|
actualNodeWeight := make(map[Vertex]float64, g.GetVertexCount())
|
||
|
for _, id := range g.GetVertexes() {
|
||
|
actualNodeWeight[id] = 1
|
||
|
}
|
||
|
const UselessIterations = 200
|
||
|
for cnt := 0; len(ksp) < k && cnt < UselessIterations; cnt++ {
|
||
|
if err := modifyEdgeWeight(g, actualNodeWeight); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
path, err := DijkstraPath(g, source, target)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
for _, v := range path {
|
||
|
actualNodeWeight[v]++
|
||
|
}
|
||
|
key := ""
|
||
|
for _, v := range path {
|
||
|
key += v.String()
|
||
|
}
|
||
|
if !DRY[key] {
|
||
|
DRY[key] = true
|
||
|
ksp = append(ksp, path)
|
||
|
cnt = 0
|
||
|
}
|
||
|
}
|
||
|
return ksp, nil
|
||
|
}
|
||
|
|
||
|
func modifyEdgeWeight(g *Graph, actualNodeWeight map[Vertex]float64) error {
|
||
|
for _, v1 := range g.GetVertexes() {
|
||
|
targets, err := g.GetNeighbors(v1)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
for v2, multiedges := range targets {
|
||
|
for ID := range multiedges {
|
||
|
wgt := calcEdgeWeight(actualNodeWeight, v1, v2)
|
||
|
g.ReplaceUndirectedEdge(v1, v2, ID, &ChannelInfo{Wgt: wgt})
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Calculate new edge weight based on vertex weights.
|
||
|
// It uses empirical formulae
|
||
|
// weight(i, j) = (weight(i) + weight(j)) ^ 6
|
||
|
// Number 6 was choosen because it gives best results in several simulations
|
||
|
func calcEdgeWeight(actualNodeWeight map[Vertex]float64, v1, v2 Vertex) float64 {
|
||
|
const ExperiementalNumber = 6.0
|
||
|
wgt := math.Pow(actualNodeWeight[v1]+actualNodeWeight[v2], ExperiementalNumber)
|
||
|
return wgt
|
||
|
}
|