mirror of
https://github.com/gonum/gonum.git
synced 2025-10-05 23:26:52 +08:00
192 lines
5.3 KiB
Go
192 lines
5.3 KiB
Go
// Copyright ©2014 The Gonum Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package path
|
|
|
|
import (
|
|
"container/heap"
|
|
"math"
|
|
"sort"
|
|
|
|
"gonum.org/v1/gonum/graph"
|
|
"gonum.org/v1/gonum/graph/simple"
|
|
)
|
|
|
|
// WeightedBuilder is a type that can add nodes and weighted edges.
|
|
type WeightedBuilder interface {
|
|
AddNode(graph.Node)
|
|
SetWeightedEdge(graph.WeightedEdge)
|
|
}
|
|
|
|
// Prim generates a minimum spanning tree of g by greedy tree extension, placing
|
|
// the result in the destination, dst. If the edge weights of g are distinct
|
|
// it will be the unique minimum spanning tree of g. The destination is not cleared
|
|
// first. The weight of the minimum spanning tree is returned. If g is not connected,
|
|
// a minimum spanning forest will be constructed in dst and the sum of minimum
|
|
// spanning tree weights will be returned.
|
|
//
|
|
// Nodes and Edges from g are used to construct dst, so if the Node and Edge
|
|
// types used in g are pointer or reference-like, then the values will be shared
|
|
// between the graphs.
|
|
//
|
|
// If dst has nodes that exist in g, Prim will panic.
|
|
func Prim(dst WeightedBuilder, g graph.WeightedUndirected) float64 {
|
|
nodes := graph.NodesOf(g.Nodes())
|
|
if len(nodes) == 0 {
|
|
return 0
|
|
}
|
|
|
|
q := &primQueue{
|
|
indexOf: make(map[int64]int, len(nodes)-1),
|
|
nodes: make([]simple.WeightedEdge, 0, len(nodes)-1),
|
|
}
|
|
dst.AddNode(nodes[0])
|
|
for _, u := range nodes[1:] {
|
|
dst.AddNode(u)
|
|
heap.Push(q, simple.WeightedEdge{F: u, W: math.Inf(1)})
|
|
}
|
|
|
|
u := nodes[0]
|
|
uid := u.ID()
|
|
for _, v := range graph.NodesOf(g.From(uid)) {
|
|
w, ok := g.Weight(uid, v.ID())
|
|
if !ok {
|
|
panic("prim: unexpected invalid weight")
|
|
}
|
|
q.update(v, u, w)
|
|
}
|
|
|
|
var w float64
|
|
for q.Len() > 0 {
|
|
e := heap.Pop(q).(simple.WeightedEdge)
|
|
if e.To() != nil && g.HasEdgeBetween(e.From().ID(), e.To().ID()) {
|
|
dst.SetWeightedEdge(g.WeightedEdge(e.From().ID(), e.To().ID()))
|
|
w += e.Weight()
|
|
}
|
|
|
|
u = e.From()
|
|
uid := u.ID()
|
|
for _, n := range graph.NodesOf(g.From(uid)) {
|
|
if key, ok := q.key(n); ok {
|
|
w, ok := g.Weight(uid, n.ID())
|
|
if !ok {
|
|
panic("prim: unexpected invalid weight")
|
|
}
|
|
if w < key {
|
|
q.update(n, u, w)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return w
|
|
}
|
|
|
|
// primQueue is a Prim's priority queue. The priority queue is a
|
|
// queue of edge From nodes keyed on the minimum edge weight to
|
|
// a node in the set of nodes already connected to the minimum
|
|
// spanning forest.
|
|
type primQueue struct {
|
|
indexOf map[int64]int
|
|
nodes []simple.WeightedEdge
|
|
}
|
|
|
|
func (q *primQueue) Less(i, j int) bool {
|
|
return q.nodes[i].Weight() < q.nodes[j].Weight()
|
|
}
|
|
|
|
func (q *primQueue) Swap(i, j int) {
|
|
q.indexOf[q.nodes[i].From().ID()] = j
|
|
q.indexOf[q.nodes[j].From().ID()] = i
|
|
q.nodes[i], q.nodes[j] = q.nodes[j], q.nodes[i]
|
|
}
|
|
|
|
func (q *primQueue) Len() int {
|
|
return len(q.nodes)
|
|
}
|
|
|
|
func (q *primQueue) Push(x interface{}) {
|
|
n := x.(simple.WeightedEdge)
|
|
q.indexOf[n.From().ID()] = len(q.nodes)
|
|
q.nodes = append(q.nodes, n)
|
|
}
|
|
|
|
func (q *primQueue) Pop() interface{} {
|
|
n := q.nodes[len(q.nodes)-1]
|
|
q.nodes = q.nodes[:len(q.nodes)-1]
|
|
delete(q.indexOf, n.From().ID())
|
|
return n
|
|
}
|
|
|
|
// key returns the key for the node u and whether the node is
|
|
// in the queue. If the node is not in the queue, key is returned
|
|
// as +Inf.
|
|
func (q *primQueue) key(u graph.Node) (key float64, ok bool) {
|
|
i, ok := q.indexOf[u.ID()]
|
|
if !ok {
|
|
return math.Inf(1), false
|
|
}
|
|
return q.nodes[i].Weight(), ok
|
|
}
|
|
|
|
// update updates u's position in the queue with the new closest
|
|
// MST-connected neighbour, v, and the key weight between u and v.
|
|
func (q *primQueue) update(u, v graph.Node, key float64) {
|
|
id := u.ID()
|
|
i, ok := q.indexOf[id]
|
|
if !ok {
|
|
return
|
|
}
|
|
q.nodes[i].T = v
|
|
q.nodes[i].W = key
|
|
heap.Fix(q, i)
|
|
}
|
|
|
|
// UndirectedWeightLister is an undirected graph that returns edge weights and
|
|
// the set of edges in the graph.
|
|
type UndirectedWeightLister interface {
|
|
graph.WeightedUndirected
|
|
WeightedEdges() graph.WeightedEdges
|
|
}
|
|
|
|
// Kruskal generates a minimum spanning tree of g by greedy tree coalescence, placing
|
|
// the result in the destination, dst. If the edge weights of g are distinct
|
|
// it will be the unique minimum spanning tree of g. The destination is not cleared
|
|
// first. The weight of the minimum spanning tree is returned. If g is not connected,
|
|
// a minimum spanning forest will be constructed in dst and the sum of minimum
|
|
// spanning tree weights will be returned.
|
|
//
|
|
// Nodes and Edges from g are used to construct dst, so if the Node and Edge
|
|
// types used in g are pointer or reference-like, then the values will be shared
|
|
// between the graphs.
|
|
//
|
|
// If dst has nodes that exist in g, Kruskal will panic.
|
|
func Kruskal(dst WeightedBuilder, g UndirectedWeightLister) float64 {
|
|
edges := graph.WeightedEdgesOf(g.WeightedEdges())
|
|
sort.Sort(byWeight(edges))
|
|
|
|
ds := make(djSet)
|
|
it := g.Nodes()
|
|
for it.Next() {
|
|
n := it.Node()
|
|
dst.AddNode(n)
|
|
ds.add(n.ID())
|
|
}
|
|
|
|
var w float64
|
|
for _, e := range edges {
|
|
if s1, s2 := ds.find(e.From().ID()), ds.find(e.To().ID()); s1 != s2 {
|
|
ds.union(s1, s2)
|
|
dst.SetWeightedEdge(g.WeightedEdge(e.From().ID(), e.To().ID()))
|
|
w += e.Weight()
|
|
}
|
|
}
|
|
return w
|
|
}
|
|
|
|
type byWeight []graph.WeightedEdge
|
|
|
|
func (e byWeight) Len() int { return len(e) }
|
|
func (e byWeight) Less(i, j int) bool { return e[i].Weight() < e[j].Weight() }
|
|
func (e byWeight) Swap(i, j int) { e[i], e[j] = e[j], e[i] }
|