Ads Lab Record
Ads Lab Record
Ads Lab Record
NO 1A
DATE:
AIM:
The aim of Dijkstra's algorithm is to find the shortest path in a given graph.
ALGORITHM
1. Initialize the source node to take value 0 and all other nodes to ∞. Start with node 0 as
the “current node.”
2. Find all neighboring nodes and update their values to either the minimum of their value
or the value of the current node plus its distance. Mark the node as finished.
3. Assign the minimum-valued unfinished node as the current node.
4. Repeat steps 2 and 3 until all nodes (or a specific node of interest) are finished.
PROGRAM
import heapq
while pq:
dist, node = heapq.heappop(pq)
if dist > distances[node]:
continue
for neighbor, weight in enumerate(graph[node]):
if weight > 0:
new_dist = dist + weight
if new_dist < distances[neighbor]:
distances[neighbor] = new_dist
heapq.heappush(pq, (new_dist, neighbor))
return distances
# User input
num_vertices = int(input("Enter the number of vertices: "))
graph = [list(map(int, input(f"Enter weights for vertex {i}: ").split())) for i in
range(num_vertices)]
start_vertex = int(input("Enter the starting vertex: "))
RESULT:
Thus, the above program of Dijkstra's algorithm was successfully implemented, and output was
executed.
EX .NO 1B
DATE:
AIM:
The aim of Breadth-First Search (BFS) is to find the shortest path in a given graph
ALGORITHM:
1. Initialization:
2. Traversal Loop:
▪ Dequeue a node.
▪ Enqueue unvisited neighbors, marking them as visited.
while queue:
node = queue.popleft()
print(node, end=' ')
RESULT:
Thus, the above program of Breadth-First Search (BFS) was successfully implemented, and
output was executed.
EX.NO: 2A
DATE:
AIM:
The aim of the Ford-Fulkerson algorithm is to find the maximum flow in a flow network. The
goal is to find the maximum amount of flow that can be sent from a specified source node to
a specified sink node.
ALGORITHM:
1.Initialize: Set flow on all edges to 0.
2.While there exists an augmenting path from source to sink:
• Find an augmenting path in the residual graph.
• Determine the bottleneck capacity (minimum capacity along the path).
• Update flow and residual graph.
3.Output: The maximum flow is the sum of all flows reaching the sink.
PROGRAM:
import networkx as nx
def ford_fulkerson(graph, source, sink):
flow_value, flow_dict = nx.maximum_flow(graph, source, sink)
return flow_value, flow_dict
OUTPUT:
Enter the number of nodes: 4
Enter the number of edges: 5
RESULT:
Thus the output for network flow has been achieved successfully.
EX.NO: 2B
DATE:
AIM:
The aim of the "Transportation Problem" program is to solve a transportation problem using
linear programming. The goal is to determine the optimal allocation of goods from suppliers
to consumers while minimizing the transportation costs.
ALGORITHM:
Initialization:
Initialize suppliers, consumers, supply, demand, and transportation costs.
Create an empty dictionary for allocations.
Solution Method (solve function):
Iterate over each supplier and consumer.
For each combination, determine the quantity to be allocated based on supplier's supply and
consumer's demand.
Update the allocations, supply, and demand accordingly.
Display Result (display_result function):
Display the optimal allocations after solving the transportation problem.
PROGRAM:
class TransportationProblem:
def __init__(self):
self.suppliers = ['S1', 'S2']
self.consumers = ['C1', 'C2', 'C3']
self.supply = {'S1': 20, 'S2': 30}
self.demand = {'C1': 10, 'C2': 25, 'C3': 15}
self.costs = {'S1': {'C1': 2, 'C2': 3, 'C3': 4}, 'S2': {'C1': 5, 'C2': 2, 'C3': 1}}
def solve(self):
allocations = {}
return allocations
if __name__ == "__main__":
tp = TransportationProblem()
result = tp.solve()
tp.display_result(result)
OUTPUT:
Optimal Allocations:
S1 -> C1: 10
S1 -> C2: 10
S1 -> C3: 0
S2 -> C1: 0
S2 -> C2: 15
S2 -> C3: 15
RESULT:
The output indicates the optimal allocations from suppliers to consumers, showing the
quantity of goods transported from each supplier to each consumer.
EX.NO: 3
DATE:
AIM:
The aim of the Floyd-Warshall algorithm is to find the shortest paths between all pairs of
vertices in a weighted graph. It handles both positive and negative edge weights and is
particularly useful for dense graphs.
ALGORITHM: Floyd-Warshall
1)Initialize: Create a matrix dist where dist[i][j] represents the shortest distance from vertex i
to vertex j. Initialize the matrix based on the graph's edge weights.
2)Iterative Update:
• For each intermediate vertex k (from 1 to the number of vertices):
• For each pair of vertices i and j:
• If the path from i to k and then from k to j is shorter than the current best-known path
from i to j, update dist[i][j].
3)Result: After the iterations, the dist matrix contains the shortest distances between all pairs
of vertices.
PROGRAM :
V = int(input("Enter the number of vertices: "))
# A function to print the solution matrix
def print_solution(dist):
print("The following matrix shows the shortest distances between every pair of vertices")
for i in range(V):
for j in range(V):
if dist[i][j] == float('inf'):
print("{:4s}".format("INF"), end="")
else:
print("{:4d}".format(dist[i][j]), end="")
print()
# Solves the all-pairs shortest path problem using Floyd Warshall algorithm
def floyd_warshall(graph):
# Initialize the solution matrix same as the input graph matrix
dist = [[0]*V for _ in range(V)]
for i in range(V):
for j in range(V):
dist[i][j] = graph[i][j]
# Add all vertices one by one to the set of intermediate vertices
for k in range(V):
for i in range(V):
for j in range(V):
# If vertex k is on the shortest path from i to j, update the value of dist[i][j]
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])
# Print the shortest distance matrix
print_solution(dist)
# Driver program to test above function
if __name__ == "__main__":
# Get input graph from the user
graph = []
print("Enter the adjacency matrix of the graph (Enter 'inf' for infinity)")
for _ in range(V):
row = input().split()
row = [float('inf') if x.lower() == 'inf' else int(x) for x in row]
graph.append(row)
# Print the solution
floyd_warshall(graph)
OUTPUT :
Enter the number of vertices: 3
Enter the adjacency matrix of the graph (Enter 'inf' for infinity)
0 2 inf
inf 0 3
1 inf 0
The following matrix shows the shortest distances between every pair of vertices
0 2 5
INF 0 3
1 INF 0
RESULT :
Thus the output for implementation of Dynamic programming technique using Floyd
Warshall algorithm has been achieved successfully.
EX .NO 4
DATE:
AIM:
The aim is to implement the depth first search using recursive back tracking algorithm.
ALGORITHM:
Initialize: Start from a given vertex (or node) in the graph. Mark the starting vertex as visited.
Explore neighbors: Visit an unvisited neighbor of the current vertex. If there are multiple
neighbors, choose one and move to that vertex.
Recursion: Recursively apply the same process to the chosen vertex. If the chosen vertex has
unvisited neighbors, repeat step 2.
Backtrack: If there are no unvisited neighbors for the current vertex, backtrack to the
previous vertex (if possible) and explore its remaining unvisited neighbors.
Repeat: Continue this process until all vertices are visited, or until a specific condition is met.
PROGRAM:
class Graph:
def __init__(self):
self.graph = {}
def main():
graph = Graph()
OUTPUT:
Enter the number of edges: 4
Enter an edge (format: vertex1 vertex2): a b
Enter an edge (format: vertex1 vertex2): b c
Enter an edge (format: vertex1 vertex2): c d
Enter an edge (format: vertex1 vertex2): d e
Enter the starting vertex for DFS: a
DFS traversal starting from a:
abcde
RESULT:
Thus, the above program of depth first search was successfully completed and output was
executed.
EX.NO 5
DATE:
AIM:
The aim of Las vegas Algorithm is to guarantee correct result while introduction
controlled randomness to improve average case performance ,allowing for vary running
times.
ALGORITHM:
1) Initialize: Set up the initial state of the algorithm.
2) Randomize: Introduce randomness into the algorithm, making random choices or using
random values.
3) Execute: Perform the main steps of the algorithm based on both input data and random
choices.
4) Check Correctness: Verify whether the result is correct. Las Vegas algorithms always
produce a correct result.
5) Repeat if Necessary: If the correctness depends on random choices, repeat with new
random choices until a correct result is obtained.
6) Analysis: Evaluate the expected runtime or complexity, considering the random choices
made during execution.
PROGRAM:
import random
def randomized_quicksort(arr):
if len(arr) <= 1:
return arr
pivot = random.choice(arr)
less = [x for x in arr if x < pivot]
equal = [x for x in arr if x == pivot]
greater = [x for x in arr if x > pivot]
return randomized_quicksort(less) + equal + randomized_quicksort(greater)
# User input for the array
user_input = input("Enter elements of the array separated by spaces: ")
input_array = list(map(int, user_input.split()))
# Print the original array
print("Original array:", input_array)
# Apply the randomized QuickSort algorithm
sorted_array = randomized_quicksort(input_array)
# Print the sorted array
print("Sorted array:", sorted_array)
OUTPUT:
Enter elements of the array separated by spaces: 3 1 4 1 5 9 2
Original array: [3, 1, 4, 1, 5, 9, 2]
Sorted array: [1, 1, 2, 3, 4, 5, 9]
RESULT:
Thus the above program for las vegas algorithm was successfully executed.
EX.NO: 6A
DATE:
AIM:
Implement a concurrent list that allows multiple threads to safely insert, delete, and search for
elements in the list concurrently.
ALGORITHM:
Class Definition:
Define a class for a concurrent list with a threading lock.
Insert Operation:
Acquire the lock before inserting an element into the list.
Perform the insertion operation.
Release the lock after the operation is complete.
Delete Operation:
Acquire the lock before deleting an element from the list.
Perform the deletion operation.
Release the lock after the operation is complete.
Search Operation:
Acquire the lock before searching for an element in the list.
Perform the search operation.
Release the lock after the operation is complete.
PROGRAM:
import threading
import time
class Node:
def __init__(self, data):
self.data = data
self.next = None
class ConcurrentLL:
def __init__(self):
self.head = None
self.lock = threading.Lock()
def insert(self, data):
with self.lock:
new_node = Node(data)
new_node.next = None
if(self.head==None):
self.head = new_node
else:
tptr = self.head
while(tptr.next!=None):
tptr = tptr.next
tptr.next = new_node
time.sleep(0.1) # Simulating some processing time between inserts
def display(self):
current = self.head
while current:
print(current.data, end=" -> ")
current = current.next
print("None")
if __name__ == "__main__":
Linked_List = ConcurrentLL()
while(1):
i = int(input()
if(i==-1):
break
# Create two threads to concurrently insert data
thread1 = threading.Thread(Linked_List.insert(i))
thread2 = threading.Thread(Linked_List.insert(i))
# Start the threads
thread1.start()
thread2.start()
# Display the final state of the linked list
Linked_List.display()
INPUT :
1
2
3
4
5
-1
OUTPUT :
1 -> 1 -> 2 -> 2 -> 3 -> 3 -> 4 -> 4 -> 5 -> 5 -> None
RESULT:
The implementation of the concurrent list has been achieved successfully.
EX.NO: 6B
DATE:
AIM:
Implement a concurrent stack that allows multiple threads to safely push, pop, and peek at
elements in the stack concurrently.
ALGORITHM:
Class Definition:
Define a class for a concurrent stack with a threading lock.
Push Operation:
Acquire the lock before pushing an element onto the stack.
Perform the push operation.
Release the lock after the operation is complete.
Pop Operation:
Acquire the lock before popping an element from the stack.
Perform the pop operation.
Release the lock after the operation is complete.
Peek Operation:
Acquire the lock before peeking at the top element of the stack.
Perform the peek operation.
Release the lock after the operation is complete.
PROGRAM :
import threading
import time
class Node
def __init__(self, data):
self.data = data
self.next = None
class ConcurrentStack:
def __init__(self):
self.head = None
self.lock = threading.Lock()
def display(self):
current = self.head
while current:
print(current.data, end=" -> ")
current = current.next
print("None")
if __name__ == "__main__":
stack = ConcurrentStack()
while(1):
i = int(input()
if(i==-1):
break
INPUT :
1
2
3
4
5
-1
OUTPUT :
5 -> 5 -> 4 -> 4 -> 3 -> 3 -> 2 -> 2 -> 1 -> 1 -> None
RESULT:
The implementation of the concurrent stack has been achieved successfully.
EX.NO: 6C
DATE:
AIM:
Implement a concurrent queue that allows multiple threads to safely enqueue, dequeue, and
peek at elements in the queue concurrently.
ALGORITHM:
Class Definition:
Define a class for a concurrent queue with a threading lock.
Enqueue Operation:
Acquire the lock before enqueuing an element into the queue.
Perform the enqueue operation.
Release the lock after the operation is complete.
Dequeue Operation:
Acquire the lock before dequeuing an element from the queue.
Perform the dequeue operation.
Release the lock after the operation is complete.
Peek Operation:
Acquire the lock before peeking at the front element of the queue.
Perform the peek operation.
Release the lock after the operation is complete.
PROGRAM :
import threading
import time
class Node:
def __init__(self, data):
self.data = data
self.next = None
class ConcurrentQueue:
def __init__(self):
self.head = None
self.lock = threading.Lock()
def dequeue(self):
with self.lock:
if self.head is not None:
data = self.head.data
self.head = self.head.next
return data
else:
return None
def display(self):
current = self.head
while current:
print(current.data, end=" -> ")
current = current.next
print("None")
if __name__ == "__main__":
queue = ConcurrentQueue()
while True:
i = int(input())
if i == -1:
break
INPUT :
1
2
3
4
5
-1
OUTPUT :
1 -> 1 -> 2 -> 2 -> 3 -> 3 -> 4 -> 4 -> 5 -> 5 -> None
1 -> 2 -> 2 -> 3 -> 3 -> 4 -> 4 -> 5 -> 5 -> None
RESULT:
The implementation of the concurrent queue has been achieved successfully.
EX.NO: 7
DATE:
AIM:
The aim of this code is to demonstrate concurrent insertion of elements into a linked list using
multithreading in Python. Two threads are created to insert the same set of data into the
linked list concurrently. The objective is to showcase how threads can operate
simultaneously, accessing and modifying shared data structures.
ALGORITHM:
Node Class:
_init_(self, data): Initialize a Node with given data and set next to None.
Concurrent Class:
_init_(self): Initialize an empty linked list with a head pointer.
insert(self, data): Insert a new node with the given data at the beginning of the linked list.
Print a message indicating the thread that performed the insertion.
display(self): Display the elements of the linked list.
insert_data Function:
insert_data(con, data): Insert each item in the provided data list into the linked list con.
Main Execution:
Create an instance of the Concurrent class.
Define a list data_to_insert containing elements to be inserted into the linked list.
Create two threads (thread1 and thread2) targeting the insert_data function to insert data
concurrently into the linked list.
Start both threads.
Wait for both threads to finish using thread1.join() and thread2.join().
Display the final state of the linked list.
PROGRAM:
import threading
import time
class Node:
def __init__(self, data):
self.data = data
self.next = None
class Concurrent:
def __init__(self):
self.head = None
def display(self):
current = self.head
while current:
print(current.data, end=" -> ")
current = current.next
print("None")
RESULT:
Thus the output for the multi-threading involving concurrency has been achieved
successfully.
ADDITIONAL PROGRAM – N Queens Problem
Aim :
The aim of the problem is to place N chess queens on an N×N chessboard in such a way that
no two queens threaten each other. This means that no two queens should be in the same row,
column, or diagonal.
Algorithm :
• Initialize the chessboard: Create an empty chessboard of size N×N.
• Place queens recursively: Start with the first row and try placing a queen in each
column of that row. Move to the next row and repeat the process. If at any point
placing a queen in a particular position violates the constraints, backtrack to the
previous row and try a different column for the queen.
• Base case: If all queens are successfully placed on the board, a solution is found. If
you reach a point where it's not possible to place a queen in any column of the current
row without violating constraints, backtrack to the previous row.
• Repeat until all solutions are found: Continue the process until you find all possible
solutions or exhaust all possibilities.
PROGRAM
def print_solution(board):
for row in board:
print(' '.join(row))
print()
return True
if res :
return True
return res
def solve_n_queens(n):
board = [['_' for _ in range(n)] for _ in range(n)]
RESULT:
Thus the output for the N Queens problem has been achieved successfully.