Satsuma
a delicious .NET graph library
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Properties Pages
BellmanFord.cs
Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Threading.Tasks;
6 
7 namespace Satsuma
8 {
28  public sealed class BellmanFord
29  {
31  public IGraph Graph { get; private set; }
34  public Func<Arc, double> Cost { get; private set; }
35 
37  public IPath NegativeCycle { get; private set; }
38 
39  private const string NegativeCycleMessage = "A negative cycle was found.";
40  private readonly Dictionary<Node, double> distance;
41  private readonly Dictionary<Node, Arc> parentArc;
42 
47  public BellmanFord(IGraph graph, Func<Arc, double> cost, IEnumerable<Node> sources)
48  {
49  Graph = graph;
50  Cost = cost;
51 
52  distance = new Dictionary<Node, double>();
53  parentArc = new Dictionary<Node, Arc>();
54 
55  foreach (var n in sources)
56  {
57  distance[n] = 0;
58  parentArc[n] = Arc.Invalid;
59  }
60 
61  Run();
62  }
63 
64  private void Run()
65  {
66  for (int i = Graph.NodeCount(); i > 0; i--)
67  {
68  foreach (var arc in Graph.Arcs())
69  {
70  Node u = Graph.U(arc);
71  Node v = Graph.V(arc);
72  double du = GetDistance(u);
73  double dv = GetDistance(v);
74  double c = Cost(arc);
75 
76  if (Graph.IsEdge(arc))
77  {
78  if (du > dv)
79  {
80  var t = u; u = v; v = t;
81  var dt = du; du = dv; dv = dt;
82  }
83 
84  if (!double.IsPositiveInfinity(du) && c < 0)
85  {
86  var cycle = new Path(Graph);
87  cycle.Begin(u);
88  cycle.AddLast(arc);
89  cycle.AddLast(arc);
90  NegativeCycle = cycle;
91  return;
92  }
93  }
94 
95  if (du + c < dv)
96  {
97  distance[v] = du + c;
98  parentArc[v] = arc;
99 
100  if (i == 0)
101  {
102  Node p = u;
103  for (int j = Graph.NodeCount() - 1; j > 0; j--)
104  p = Graph.Other(parentArc[p], p);
105 
106  var cycle = new Path(Graph);
107  cycle.Begin(p);
108  Node x = p;
109  while (true)
110  {
111  Arc a = parentArc[x];
112  cycle.AddFirst(a);
113  x = Graph.Other(a, x);
114  if (x == p) break;
115  }
116  NegativeCycle = cycle;
117  return;
118  }
119  }
120  } // for all arcs
121  } // for i
122  }
123 
125  public bool Reached(Node node)
126  {
127  return parentArc.ContainsKey(node);
128  }
129 
132  public IEnumerable<Node> ReachedNodes { get { return parentArc.Keys; } }
133 
137  public double GetDistance(Node node)
138  {
139  if (NegativeCycle != null) throw new InvalidOperationException(NegativeCycleMessage);
140  double result;
141  return distance.TryGetValue(node, out result) ? result : double.PositiveInfinity;
142  }
143 
147  public Arc GetParentArc(Node node)
148  {
149  if (NegativeCycle != null) throw new InvalidOperationException(NegativeCycleMessage);
150  Arc result;
151  return parentArc.TryGetValue(node, out result) ? result : Arc.Invalid;
152  }
153 
157  public IPath GetPath(Node node)
158  {
159  if (NegativeCycle != null) throw new InvalidOperationException(NegativeCycleMessage);
160  if (!Reached(node)) return null;
161 
162  var result = new Path(Graph);
163  result.Begin(node);
164  while (true)
165  {
166  Arc arc = GetParentArc(node);
167  if (arc == Arc.Invalid) break;
168  result.AddFirst(arc);
169  node = Graph.Other(arc, node);
170  }
171  return result;
172  }
173  }
174 }