Skip to main content

Bellman Ford Graph Algorithm

The Shortest path algorithms so you go to google maps and you want to find the shortest path from one city to another.  Two algorithms can help you, they both calculate the shortest distance from a source node into all other nodes, one node can handle negative weights with cycles and another cannot, Dijkstra cannot and bellman ford can.

One is Dijkstra if you run the Dijkstra algorithm on this map its input would be a single source node and its output would be the path to all other vertices.  However, there is a caveat if Elon mask comes and with some magic creates a black hole loop which makes one of the edges negative weight then the Dijkstra algorithm would fail to give you the answer.

This is where bellman Ford algorithm comes into place, it's like the Dijkstra algorithm only it knows to handle well negative weight in edges.

Dijkstra has an issue handling negative weights and cycles

Bellman's ford algorithm target is to find the shortest path from a single node in a graph to all other nodes.

When we discussed Dijkstra's algorithm we saw that it's moving forward and it's quite efficient with a heap it's only O(E+V) * O(VlgV) however if the graph is having negative edge weights then Dijkstra's algorithm does not take care of it, it would simply fail Dijkstra algorithm is simply not intended for graphs with negative edge weights.  The reason for the above time complexity is that the Dijkstra's algorithm simply scan's the graph in a rather BFS way it's not a strict BFS there are rules to which vertices to visit next but it's like swooping the graph from your source node into all other nodes the reason for the O(VlgV) is that when it chooses which next node to choose then we use a heap sort to find the one currently having the minimal weight.

To recap here are the steps for Dijkstra algorithm are:
  1. Initialize distances to infinity.
  2. Pick first node and calculate distances to adjacent nodes.
  3. Pick next node with minimal distance; repeat adjacent node distance calculations.
  4. Final result of shortest-path tree.

So as Bellman's ford algorithm can handle negative edges it can also detect negative cycles.  Why would we need to detect negative cycle? in arbitrage trading we can find we could make money of thin air by selling on one stock exchange high and buying on another low, this is a negative cycle because we can continue this cycle take the money we made on the high stock exchange and with the negative edge buy on a lower price on the lower priced stock exchange.

Of course if we find out that our graph contains a negative cycle to a target node this means there would be no shortest path to this target node or it would be minus infinity.

So while the bellman ford algorithm can handle negative weighted graphs while the Dijkstra's cannot nothing in life comes for free and bellman ford is slower than Dijkstra's algorithm after all it's making fewer assumptions about the weights they should not all be non-negative.

When we have a negative weights its possible for a negative to exist and then Dijkstra algorithm would find better and better paths.

What would a negative infinity mean to us, it depends on our problem it can be a good thing to have negative infinity path but it could also mean to have a bad thing to have a negative infinity path.

Before we get to the actual Bellman ford algorithm let's reap the terms we have for graphs

E is the edges more specifically we are interested in the number of edges

A directed edge is an edge going from a source node to a target node with some weight.

V is the vertices more specifically we are interested in the number of vertices
S is the starting node
D is an array of size V that tracks the best distance from S to each node.

The distance D array first is initialized to positive infinity.

In our first step we go over array D which stores the distance the best distance from S to any node as infinity, we don't know yet of any distance, so we start with infinity.

However, we have a starting point, so we already know the distance to the starting node which means that D[S] = 0.  The path of the starting node to itself is zero.

Now we start reducing the weights of these edges, we are going to do this stay tuned:

For each edge (order does not matter) check if the edge makes the target node weight lower if so replace it with lower number, repeat this whole process for all edges V-1 Times
When we start with this process we are going to update all weights because any weight would be smaller than infinity which we initialized all weights in the shortest distance to.
  
 How do we detect negative cycles?

We run the algorithm a second time, if any node value changed then we update that node to having cost of negative infinity and that's it. When we say that we run the algorithm a second time we run again the V-1 times of edges relaxing.

At the end you return the distance array D which contains all the weights. Then if you want to know the shortest path from a node to another one you just find that target node in the distance array.

And that was bellman ford algorithm.

Comments

  1. Amazing tutorial. Graph algorithms are very useful in modelling and implementing networks. Learning and practicing Data structure and algorithm is necessary to build solid problem-solving skills. I must say this problem enhanced my conceptual understanding. Thank you for sharing this.

    ReplyDelete

Post a Comment

Popular posts from this blog

Functional Programming in Scala for Working Class OOP Java Programmers - Part 1

Introduction Have you ever been to a scala conf and told yourself "I have no idea what this guy talks about?" did you look nervously around and see all people smiling saying "yeah that's obvious " only to get you even more nervous? . If so this post is for you, otherwise just skip it, you already know fp in scala ;) This post is optimistic, although I'm going to say functional programming in scala is not easy, our target is to understand it, so bare with me. Let's face the truth functional programmin in scala is difficult if is difficult if you are just another working class programmer coming mainly from java background. If you came from haskell background then hell it's easy. If you come from heavy math background then hell yes it's easy. But if you are a standard working class java backend engineer with previous OOP design background then hell yeah it's difficult. Scala and Design Patterns An interesting point of view on scala, is

Alternatives to Using UUIDs

  Alternatives to Using UUIDs UUIDs are valuable for several reasons: Global Uniqueness : UUIDs are designed to be globally unique across systems, ensuring that no two identifiers collide unintentionally. This property is crucial for distributed systems, databases, and scenarios where data needs to be uniquely identified regardless of location or time. Standardization : UUIDs adhere to well-defined formats (such as UUIDv4) and are widely supported by various programming languages and platforms. This consistency simplifies interoperability and data exchange. High Collision Resistance : The probability of generating duplicate UUIDs is extremely low due to the combination of timestamp, random bits, and other factors. This collision resistance is essential for avoiding data corruption. However, there are situations where UUIDs may not be the optimal choice: Length and Readability : UUIDs are lengthy (typically 36 characters in their canonical form) and may not be human-readable. In URLs,