Skip to content

Commit

Permalink
chore: feat: implement Bellman Ford algorithm (#423)
Browse files Browse the repository at this point in the history
Co-authored-by: Taj <[email protected]>
  • Loading branch information
Tahmeed156 and tjgurwara99 authored Nov 3, 2021
1 parent 44a0622 commit 153ee2c
Show file tree
Hide file tree
Showing 2 changed files with 185 additions and 0 deletions.
48 changes: 48 additions & 0 deletions graph/bellmanford.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// The Bellman–Ford algorithm is an algorithm that computes shortest paths from a
// single source vertex to all of the other vertices in a weighted durected graph.
// It is slower than Dijkstra but capable of handling negative edge weights.
// https://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm
// Implementation is based on the book 'Introduction to Algorithms' (CLRS)
package graph

import (
"errors"
"math"
)

func (g *Graph) BellmanFord(start, end int) (isReachable bool, distance int, err error) {
INF := math.Inf(1)
distances := make([]float64, g.vertices)

// Set all vertices to unreachable, initialize source
for i := 0; i < g.vertices; i++ {
distances[i] = INF
}
distances[start] = 0

// Making iterations equal to #vertices
for n := 0; n < g.vertices; n++ {

// Looping over all edges
for u, adjacents := range g.edges {
for v, weightUV := range adjacents {

// If new shorter distance is found, update distance value (relaxation step)
if newDistance := distances[u] + float64(weightUV); distances[v] > newDistance {
distances[v] = newDistance
}
}
}
}

// Check for negative weight cycle
for u, adjacents := range g.edges {
for v, weightUV := range adjacents {
if newDistance := distances[u] + float64(weightUV); distances[v] > newDistance {
return false, -1, errors.New("negative weight cycle present")
}
}
}

return distances[end] != INF, int(distances[end]), nil
}
137 changes: 137 additions & 0 deletions graph/bellmanford_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package graph

import (
"errors"
"fmt"
"math"
"testing"
)

func TestBellmanford(t *testing.T) {

var testCases = []struct {
name string
edges [][]int
vertices int
start int
end int
isReachable bool
distance int
err error
}{
{
"single edge",
[][]int{
{0, 1, 1},
},
2, 0, 1, true, 1, nil,
},
{
"negative weights",
[][]int{
{0, 1, 1},
{1, 2, -3},
{2, 1, 4},
{2, 3, 1},
},
4, 0, 1, true, 1, nil,
},
{
"negative cycle",
[][]int{
{0, 1, 1},
{1, 2, -3},
{2, 1, 1},
{2, 3, 1},
},
4, 0, 1, false, -1, errors.New("negative weight cycle present"),
},
{
"unreachable vertex",
[][]int{
{0, 6, 771},
{0, 9, 782},
{1, 2, 454},
{2, 8, 48},
{3, 8, 249},
{3, 9, 880},
{3, 5, 280},
{7, 1, 92},
{7, 2, 497},
{8, 1, 102},
{8, 4, 977},
},
10, 8, 3, false, int(math.Inf(1)), nil,
},
{
"disconnected graph",
[][]int{
{0, 1, 10},
{2, 3, 15},
{3, 5, 10},
},
6, 0, 3, false, int(math.Inf(1)), nil,
},
{
"multiple paths",
[][]int{
{0, 1, 5},
{1, 2, 10},
{1, 3, 30},
{2, 4, 10},
{4, 5, 15},
{3, 5, 10},
},
6, 0, 5, true, 40, nil,
},
{
"random 1",
[][]int{
{0, 1, 10},
{1, 2, 10},
{0, 2, 100},
{2, 0, -10},
{1, 2, 1},
},
3, 0, 1, true, 10, nil,
},
{
"random 2",
[][]int{
{0, 1, 5498},
{2, 0, 7679},
{0, 3, 4999},
{1, 2, 8629},
{1, 3, -948},
{2, 3, 6231},
},
4, 0, 3, true, 4550, nil,
},
}

for _, test := range testCases {
t.Run(fmt.Sprint(test.name), func(t *testing.T) {
// Initializing graph, adding edges
graph := New(test.vertices)
graph.Directed = true
for _, edge := range test.edges {
graph.AddWeightedEdge(edge[0], edge[1], edge[2])
}

resIsReachable, resDistance, resError := graph.BellmanFord(test.start, test.end)
if resDistance != test.distance {
t.Errorf("Distance, Expected: %d, Computed: %d", test.distance, resDistance)
}
if resIsReachable != test.isReachable {
t.Errorf("Reachable, Expected: %t, Computed: %t", test.isReachable, resIsReachable)
}
if resError != test.err {
if resError == nil || test.err == nil {
t.Errorf("Reachable, Expected: %s, Computed: %s", test.err, resError)
} else if resError.Error() != test.err.Error() {
t.Errorf("Reachable, Expected: %s, Computed: %s", test.err.Error(), resError.Error())
}
}
})
}
}

0 comments on commit 153ee2c

Please sign in to comment.