Open In App

Implement a parallel programming task using graphs

Last Updated : 28 Jan, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

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();


Output

Sequential 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.



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads