Implement a parallel programming task using graphs
Last Updated :
28 Jan, 2024
Given a weighted directed graph with N nodes and M edges along with a source node S, use Parallel Programming to find the shortest distance from the source node S to all other nodes in the graph. The graph is given as an edge list edges[][] where for each index i, there is an edge from edges[i][0] to edges[i][1] with weight edges[i][2].
Example:
Input: N = 5, M = 8, S = 0, edges = {{0, 1, 2}, {0, 2, 4}, {1, 2, 1}, {1, 3, 7}, {2, 3, 3}, {2, 4, 5}, {3, 4, 2}, {4, 0, 6}}
Output: Shortest Distances from Vertex 0:
Vertex 0: 0, Vertex 1: 2, Vertex 2: 3, Vertex 3: 6, Vertex 4: 8
Approach:
This problem is typically solved using Dijkstra's algorithm for a sequential solution. However, the task is to parallelize this algorithm effectively to exploit the power of parallel computing. Below is the implementation for the above approach
Below is the implementation:
C++
#include <bits/stdc++.h>
using namespace std;
const int INF = 1e9;
int V;
vector<vector<pair<int, int> > > adj;
void addEdge(int u, int v, int w)
{
adj[u].push_back({ v, w });
}
void sequentialDijkstra(int src, vector<int>& dist)
{
// Initialize all the distance as infinite
dist.assign(V, INF);
dist[src] = 0;
// Create a set to store vertices with the minimum
// distance
vector<bool> processed(V, false);
// record the start time
auto start_time = chrono::high_resolution_clock::now();
// Find shortest path for all vertices
for (int count = 0; count < V - 1; ++count) {
int u = -1;
for (int i = 0; i < V; ++i) {
if (!processed[i]
&& (u == -1 || dist[i] < dist[u]))
u = i;
}
// Mark the picked vertex as processed
processed[u] = true;
// Update dist value of the adjacent vertices of the
// picked vertex.
for (const auto& edge : adj[u]) {
int v = edge.first;
int w = edge.second;
if (!processed[v] && dist[u] != INF
&& dist[u] + w < dist[v]) {
dist[v] = dist[u] + w;
}
}
}
// record the end time
auto end_time = chrono::high_resolution_clock::now();
auto duration
= chrono::duration_cast<chrono::microseconds>(
end_time - start_time);
// Print the sequential execution time
cout << "Sequential Dijkstra Execution Time: "
<< duration.count() << " microseconds" << endl;
}
void parallelDijkstra(int src, vector<int>& dist)
{
dist.assign(V, INF);
dist[src] = 0;
// Create a set to store vertices with the minimum
// distance
vector<bool> processed(V, false);
// Record the start time
auto start_time = chrono::high_resolution_clock::now();
// Find shortest path for all vertices
for (int count = 0; count < V - 1; ++count) {
int u = -1;
#pragma omp parallel for
for (int i = 0; i < V; ++i) {
if (!processed[i]
&& (u == -1 || dist[i] < dist[u]))
#pragma omp critical
u = (u == -1 || dist[i] < dist[u]) ? i : u;
}
// Mark the picked vertex as processed
processed[u] = true;
// Update dist value of the adjacent vertices of the
// picked vertex using Parallel Programming
#pragma omp parallel for
for (const auto& edge : adj[u]) {
int v = edge.first;
int w = edge.second;
if (!processed[v] && dist[u] != INF
&& dist[u] + w < dist[v]) {
#pragma omp critical
if (dist[u] != INF && dist[u] + w < dist[v])
dist[v] = dist[u] + w;
}
}
}
// Record the end time
auto end_time = chrono::high_resolution_clock::now();
auto duration
= chrono::duration_cast<chrono::microseconds>(
end_time - start_time);
// Print the parallel execution time
cout << "Parallel Dijkstra Execution Time: "
<< duration.count() << " microseconds"
<< "\n";
}
int main()
{
int S = 0;
V = 5;
adj.resize(V);
addEdge(0, 1, 2);
addEdge(0, 2, 4);
addEdge(1, 2, 1);
addEdge(1, 3, 7);
addEdge(2, 3, 3);
addEdge(3, 4, 5);
addEdge(3, 4, 2);
addEdge(4, 0, 6);
vector<int> dist(V, 1e9);
sequentialDijkstra(S, dist);
parallelDijkstra(S, dist);
cout << "Shortest distances from Vertex " << S << ":\n";
for (int i = 0; i < V; ++i) {
cout << "Vertex " << i << ": " << dist[i];
if (i != V - 1)
cout << ", ";
}
return 0;
}
Java
import java.util.*;
public class Main {
static final int INF = Integer.MAX_VALUE;
static int V;
static ArrayList<ArrayList<Pair>> adj = new ArrayList<>();
static class Pair {
int first, second;
Pair(int a, int b) {
first = a;
second = b;
}
}
// Function to add an edge to the adjacency list
static void addEdge(int u, int v, int w) {
adj.get(u).add(new Pair(v, w));
}
// Sequential implementation of Dijkstra's algorithm
static void sequentialDijkstra(int src, int[] dist) {
Arrays.fill(dist, INF);
dist[src] = 0;
boolean[] processed = new boolean[V];
long start_time = System.nanoTime();
// Iterate V-1 times to find shortest paths
for (int count = 0; count < V - 1; ++count) {
int u = -1;
// Find the vertex with the minimum distance value
for (int i = 0; i < V; ++i) {
if (!processed[i] && (u == -1 || dist[i] < dist[u]))
u = i;
}
processed[u] = true;
// Update the distance values of adjacent vertices
for (Pair edge : adj.get(u)) {
int v = edge.first;
int w = edge.second;
if (!processed[v] && dist[u] != INF && dist[u] + w < dist[v]) {
dist[v] = dist[u] + w;
}
}
}
long end_time = System.nanoTime();
long duration = (end_time - start_time) / 1000;
System.out.println("Sequential Dijkstra Execution Time: " + duration + " microseconds");
}
public static void main(String[] args) {
int S = 0; // Source vertex
V = 5; // Number of vertices
// Initialize adjacency list
for (int i = 0; i < V; i++)
adj.add(new ArrayList<>());
// Add edges to the graph
addEdge(0, 1, 2);
addEdge(0, 2, 4);
addEdge(1, 2, 1);
addEdge(1, 3, 7);
addEdge(2, 3, 3);
addEdge(3, 4, 5);
addEdge(3, 4, 2); // Note: This edge seems redundant (same as the previous one)
addEdge(4, 0, 6);
int[] dist = new int[V]; // Array to store shortest distances
sequentialDijkstra(S, dist);
System.out.println("Shortest distances from Vertex " + S + ":");
for (int i = 0; i < V; ++i) {
System.out.print("Vertex " + i + ": " + dist[i]);
if (i != V - 1)
System.out.print(", ");
}
}
}
Python3
# Python Implementation
import sys
import time
INF = sys.maxsize
def addEdge(adj, u, v, w):
adj[u].append((v, w))
def sequentialDijkstra(adj, src, dist):
V = len(adj)
dist[src] = 0
processed = [False] * V
start_time = time.time()
for count in range(V - 1):
u = -1
for i in range(V):
if not processed[i] and (u == -1 or dist[i] < dist[u]):
u = i
processed[u] = True
for edge in adj[u]:
v, w = edge
if not processed[v] and dist[u] != INF and dist[u] + w < dist[v]:
dist[v] = dist[u] + w
end_time = time.time()
duration = end_time - start_time
print("Sequential Dijkstra Execution Time: {} seconds".format(duration))
def parallelDijkstra(adj, src, dist):
V = len(adj)
dist[src] = 0
processed = [False] * V
start_time = time.time()
for count in range(V - 1):
u = -1
for i in range(V):
if not processed[i] and (u == -1 or dist[i] < dist[u]):
u = i
processed[u] = True
for edge in adj[u]:
v, w = edge
if not processed[v] and dist[u] != INF and dist[u] + w < dist[v]:
if dist[u] != INF and dist[u] + w < dist[v]:
dist[v] = dist[u] + w
end_time = time.time()
duration = end_time - start_time
print("Parallel Dijkstra Execution Time: {} seconds".format(duration))
if __name__ == "__main__":
S = 0
V = 5
adj = [[] for _ in range(V)]
addEdge(adj, 0, 1, 2)
addEdge(adj, 0, 2, 4)
addEdge(adj, 1, 2, 1)
addEdge(adj, 1, 3, 7)
addEdge(adj, 2, 3, 3)
addEdge(adj, 3, 4, 5)
addEdge(adj, 3, 4, 2)
addEdge(adj, 4, 0, 6)
dist = [INF] * V
sequentialDijkstra(adj, S, dist)
parallelDijkstra(adj, S, dist)
print("Shortest distances from Vertex {}: ".format(S), end="")
for i in range(V):
print("Vertex {}: {}".format(i, dist[i]), end="")
if i != V - 1:
print(", ", end="")
print()
# This code is contributed by Sakshi
C#
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
public class Program
{
// Maximum value for integer
const int INF = int.MaxValue;
// Function to add an edge into the adjacency list
static void AddEdge(List<(int, int)>[] adj, int u, int v, int w)
{
adj[u].Add((v, w));
}
// Function to find the shortest path from source vertex to all other vertices using Dijkstra's algorithm
static void SequentialDijkstra(List<(int, int)>[] adj, int src, int[] dist)
{
int V = adj.Length;
dist[src] = 0;
bool[] processed = new bool[V];
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
for (int count = 0; count < V - 1; count++)
{
int u = -1;
for (int i = 0; i < V; i++)
{
if (!processed[i] && (u == -1 || dist[i] < dist[u]))
{
u = i;
}
}
processed[u] = true;
foreach (var edge in adj[u])
{
int v = edge.Item1, w = edge.Item2;
if (!processed[v] && dist[u] != INF && dist[u] + w < dist[v])
{
dist[v] = dist[u] + w;
}
}
}
stopwatch.Stop();
Console.WriteLine($"Sequential Dijkstra Execution Time: {stopwatch.Elapsed.TotalSeconds} seconds");
}
// Function to find the shortest path from source vertex to all other vertices using Dijkstra's algorithm in parallel
static void ParallelDijkstra(List<(int, int)>[] adj, int src, int[] dist)
{
int V = adj.Length;
dist[src] = 0;
bool[] processed = new bool[V];
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
for (int count = 0; count < V - 1; count++)
{
int u = -1;
for (int i = 0; i < V; i++)
{
if (!processed[i] && (u == -1 || dist[i] < dist[u]))
{
u = i;
}
}
processed[u] = true;
foreach (var edge in adj[u])
{
int v = edge.Item1, w = edge.Item2;
if (!processed[v] && dist[u] != INF && dist[u] + w < dist[v])
{
dist[v] = dist[u] + w;
}
}
}
stopwatch.Stop();
Console.WriteLine($"Parallel Dijkstra Execution Time: {stopwatch.Elapsed.TotalSeconds} seconds");
}
public static void Main()
{
int S = 0;
int V = 5;
List<(int, int)>[] adj = new List<(int, int)>[V];
for (int i = 0; i < V; i++)
{
adj[i] = new List<(int, int)>();
}
// Adding edges to the graph
AddEdge(adj, 0, 1, 2);
AddEdge(adj, 0, 2, 4);
AddEdge(adj, 1, 2, 1);
AddEdge(adj, 1, 3, 7);
AddEdge(adj, 2, 3, 3);
AddEdge(adj, 3, 4, 5);
AddEdge(adj, 3, 4, 2);
AddEdge(adj, 4, 0, 6);
int[] dist = Enumerable.Repeat(INF, V).ToArray();
// Running Dijkstra's algorithm
SequentialDijkstra(adj, S, dist);
ParallelDijkstra(adj, S, dist);
// Printing the shortest distances
Console.Write($"Shortest distances from Vertex {S}: ");
for (int i = 0; i < V; i++)
{
Console.Write($"Vertex {i}: {dist[i]}");
if (i != V - 1)
{
Console.Write(", ");
}
}
Console.WriteLine();
}
}
JavaScript
class Pair {
constructor(a, b) {
this.first = a;
this.second = b;
}
}
// Function to add an edge to adjacency list
function addEdge(u, v, w, adj) {
adj[u].push(new Pair(v, w));
}
// Sequential implementation of the Dijkstra's algorithm
function GFG(src, dist, adj) {
const V = adj.length;
const INF = Number.MAX_SAFE_INTEGER;
// Initialize distance array with the infinity
dist.fill(INF);
dist[src] = 0;
const processed = new Array(V).fill(false);
const start_time = process.hrtime.bigint();
// Iterate V-1 times to find shortest paths
for (let count = 0; count < V - 1; ++count) {
let u = -1;
// Find the vertex with minimum distance value
for (let i = 0; i < V; ++i) {
if (!processed[i] && (u === -1 || dist[i] < dist[u]))
u = i;
}
processed[u] = true;
// Update the distance values of the adjacent vertices
for (const edge of adj[u]) {
const v = edge.first;
const w = edge.second;
if (!processed[v] && dist[u] !== INF && dist[u] + w < dist[v]) {
dist[v] = dist[u] + w;
}
}
}
const end_time = process.hrtime.bigint();
const duration = (end_time - start_time) / BigInt(1000);
console.log(`Sequential Dijkstra Execution Time: ${duration} microseconds`);
}
function main() {
const S = 0;
const V = 5;
// Initialize adjacency list
const adj = Array.from({ length: V }, () => []);
// Add edges to the graph
addEdge(0, 1, 2, adj);
addEdge(0, 2, 4, adj);
addEdge(1, 2, 1, adj);
addEdge(1, 3, 7, adj);
addEdge(2, 3, 3, adj);
addEdge(3, 4, 5, adj);
addEdge(3, 4, 2, adj);
addEdge(4, 0, 6, adj);
const dist = new Array(V);
GFG(S, dist, adj);
console.log(`Shortest distances from Vertex ${S}:`);
for (let i = 0; i < V; ++i) {
process.stdout.write(`Vertex ${i}: ${dist[i]}`);
if (i !== V - 1)
process.stdout.write(", ");
}
}
main();
OutputSequential Dijkstra Execution Time: 2 microseconds
Parallel Dijkstra Execution Time: 1 microseconds
Shortest distances from Vertex 0:
Vertex 0: 0, Vertex 1: 2, Vertex 2: 3, Vertex 3: 6, Vertex 4: 8
The time difference between parallel implementation and sequential implementation of graphs is 1 microsecond.