-
Notifications
You must be signed in to change notification settings - Fork 1.5k
/
PrimMatrix.cs
149 lines (126 loc) · 5.09 KB
/
PrimMatrix.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
using System;
namespace Algorithms.Graph.MinimumSpanningTree;
/// <summary>
/// Class that uses Prim's (Jarnik's algorithm) to determine the minimum
/// spanning tree (MST) of a given graph. Prim's algorithm is a greedy
/// algorithm that can determine the MST of a weighted undirected graph
/// in O(V^2) time where V is the number of nodes/vertices when using an
/// adjacency matrix representation.
/// More information: https://en.wikipedia.org/wiki/Prim%27s_algorithm
/// Pseudocode and runtime analysis: https://www.personal.kent.edu/~rmuhamma/Algorithms/MyAlgorithms/GraphAlgor/primAlgor.htm .
/// </summary>
public static class PrimMatrix
{
/// <summary>
/// Determine the minimum spanning tree for a given weighted undirected graph.
/// </summary>
/// <param name="adjacencyMatrix">Adjacency matrix for graph to find MST of.</param>
/// <param name="start">Node to start search from.</param>
/// <returns>Adjacency matrix of the found MST.</returns>
public static float[,] Solve(float[,] adjacencyMatrix, int start)
{
ValidateMatrix(adjacencyMatrix);
var numNodes = adjacencyMatrix.GetLength(0);
// Create array to represent minimum spanning tree
var mst = new float[numNodes, numNodes];
// Create array to keep track of which nodes are in the MST already
var added = new bool[numNodes];
// Create array to keep track of smallest edge weight for node
var key = new float[numNodes];
// Create array to store parent of node
var parent = new int[numNodes];
for (var i = 0; i < numNodes; i++)
{
mst[i, i] = float.PositiveInfinity;
key[i] = float.PositiveInfinity;
for (var j = i + 1; j < numNodes; j++)
{
mst[i, j] = float.PositiveInfinity;
mst[j, i] = float.PositiveInfinity;
}
}
// Ensures that the starting node is added first
key[start] = 0;
// Keep looping until all nodes are in tree
for (var i = 0; i < numNodes - 1; i++)
{
GetNextNode(adjacencyMatrix, key, added, parent);
}
// Build adjacency matrix for tree
for (var i = 0; i < numNodes; i++)
{
if (i == start)
{
continue;
}
mst[i, parent[i]] = adjacencyMatrix[i, parent[i]];
mst[parent[i], i] = adjacencyMatrix[i, parent[i]];
}
return mst;
}
/// <summary>
/// Ensure that the given adjacency matrix represents a weighted undirected graph.
/// </summary>
/// <param name="adjacencyMatrix">Adjacency matric to check.</param>
private static void ValidateMatrix(float[,] adjacencyMatrix)
{
// Matrix should be square
if (adjacencyMatrix.GetLength(0) != adjacencyMatrix.GetLength(1))
{
throw new ArgumentException("Adjacency matrix must be square!");
}
// Graph needs to be undirected and connected
for (var i = 0; i < adjacencyMatrix.GetLength(0); i++)
{
var connection = false;
for (var j = 0; j < adjacencyMatrix.GetLength(0); j++)
{
if (Math.Abs(adjacencyMatrix[i, j] - adjacencyMatrix[j, i]) > 1e-6)
{
throw new ArgumentException("Adjacency matrix must be symmetric!");
}
if (!connection && float.IsFinite(adjacencyMatrix[i, j]))
{
connection = true;
}
}
if (!connection)
{
throw new ArgumentException("Graph must be connected!");
}
}
}
/// <summary>
/// Determine which node should be added next to the MST.
/// </summary>
/// <param name="adjacencyMatrix">Adjacency matrix of graph.</param>
/// <param name="key">Currently known minimum edge weight connected to each node.</param>
/// <param name="added">Whether or not a node has been added to the MST.</param>
/// <param name="parent">The node that added the node to the MST. Used for building MST adjacency matrix.</param>
private static void GetNextNode(float[,] adjacencyMatrix, float[] key, bool[] added, int[] parent)
{
var numNodes = adjacencyMatrix.GetLength(0);
var minWeight = float.PositiveInfinity;
var node = -1;
// Find node with smallest node with known edge weight not in tree. Will always start with starting node
for (var i = 0; i < numNodes; i++)
{
if (!added[i] && key[i] < minWeight)
{
minWeight = key[i];
node = i;
}
}
// Add node to mst
added[node] = true;
// Update smallest found edge weights and parent for adjacent nodes
for (var i = 0; i < numNodes; i++)
{
if (!added[i] && adjacencyMatrix[node, i] < key[i])
{
key[i] = adjacencyMatrix[node, i];
parent[i] = node;
}
}
}
}