Daa Lab Manual 24
Daa Lab Manual 24
Name : …………………………………….
Semester : ……………………………………
CERTIFICATE
This is to certify that the bonafide record of the practical work done by
………….……………………..……. Register Number …………….……………… of II year
B.Tech(Artificial Intelligence and Data Science) submitted for the B.Tech Degree
Practical examination (III Semester) in AD3351 – DESIGN AND ANALYSIS OF
ALGORITHMS LABORATORY during the academic year 2023 -2024.
TABLE OF CONTENTS
AIM:
To Implement Recursive and Non-Recursive Algorithms and Study the Order of
Growth from Log2 N To N!
PROGRAM:
Program to compute Log2 n
# Python 3 program to find log(n) using Recursion
def Log2n(n):
if n > 1:
return 1 + Log2n(n / 2)
else:
return 0
# Driver code
n = 32
print(Log2n(n))
OUTPUT:
5
OUTPUT:
5
n = 256
r=3
print(Logn(n, r))
OUTPUT:
5
import math
def Logn(n, r):
return math.log(n) / math.log(r)
n = 256
r=3
print(int(Logn(n, r)))
OUTPUT:
5
def factorial(n):
return_value = 1
for i in range(2, n + 1):
return_value *= i
return return_value
result = factorial(4)
print(result)
OUTPUT:
24
import math
def fact(n):
if n == 1:
return 0
else:
return fact(n - 1) + math.log(n)
N=3
print(fact(N))
OUTPUT:
1.791759
RESULT:
Thus the program have been executed using Recursive and Non-Recursive Algorithm.
AIM:
To multiply two square matrices using Divide and Conquer - Strassen’s Matrix
Multiplication method.
Step 1: Base case If the size of the matrices is 1x1, then the product is simply the product
of the two elements.
Step 2: Divide and conquer
Divide each of the matrices A and B into four submatrices of equal size:
a | b
c | d
e | f
g | h
Step 3: Recursively compute seven submatrices
Recursively compute the following seven submatrices:
p1 = strassen(a, (f - h))
p2 = strassen((a + b), h)
p3 = strassen((c + d), e)
p4 = strassen(d, (g - e))
p5 = strassen((a + d), (e + h))
p6 = strassen((b - d), (g + h))
p7 = strassen((a - c), (e + f))
Step 4: Combine submatrices
Compute the four submatrices of the result matrix C:
c11 = p5 + p4 - p2 + p6
c12 = p1 + p2
c21 = p3 + p4
c22 = p1 + p5 - p3 - p7
Step 5: Combine submatrices into final result matrix
Combine the four submatrices into the final result matrix C:
C = [[c11, c12],
[c21, c22]]
Here are some additional notes about the Strassen algorithm:
• The Strassen algorithm is a more efficient way to multiply matrices than the traditional
method, especially for large matrices.
• However, the Strassen algorithm is also more complex than the traditional method, and it
can be more prone to errors.
• For small matrices, the traditional method is usually faster and more accurate.
• The Strassen algorithm is best used for large matrices where speed is more important
than accuracy.
PROGRAM:
OUTPUT:
def split(matrix):
"""
Splits a given matrix into quarters.
Input: nxn matrix
Output: tuple containing 4 n/2 x n/2 matrices corresponding to a, b, c, d
"""
"""
Computes matrix product by the divide and conquer approach, recursively.
Input: nxn matrices x and y
Output: nxn matrix, product of x and y
"""
c21 = p3 + p4
c22 = p1 + p5 - p3 - p7
# Combining the 4 quadrants into a single matrix by stacking horizontally and vertically.
c = np.vstack((np.hstack((c11, c12)), np.hstack((c21, c22))))
return c
# Example usage:
B = np.array([[1, 1, 1, 1],
[2, 2, 2, 2],
[3, 3, 3, 3],
[2, 2, 2, 2]])
OUTPUT:
Array A =>
[ [1 1 1 1]
[2 2 2 2]
[3 3 3 3]
[2 2 2 2] ]
Array B =>
[ [1 1 1 1]
[2 2 2 2]
[3 3 3 3]
[2 2 2 2] ]
RESULT:
Thus the programs have been executed using Divide and Conquer - Strassen’s
Matrix Multiplication.
EX. NO. : 03
DIVIDE AND CONQUER –TOPOLOGICAL SORT
DATE :
AIM:
To implement Decrease and Conquer Recursive and Non-Recursive
Topological Sort for the given numbers.
➢ If, at any point, you can't find a node with in-degree 0 and the graph is not
empty, it means there is a cycle in the graph, and topological sort is not
possible.
➢ This algorithm runs in O(V + E) time, where V is the number of vertices and E
is the number of edges in the graph.
PROGRAM:
i) RECURSIVE TOPOLOGICAL SORT:
#Python program to print topological sorting of a DAG
10
g = Graph(6)
g.addEdge(5, 2)
g.addEdge(5, 0)
g.addEdge(4, 0)
g.addEdge(4, 1)
g.addEdge(2, 3)
g.addEdge(3, 1)
OUTPUT:
Following is a Topological Sort of
the given graph[5, 4, 2, 3, 1, 0]
11
for i in range(self.V):
if not visited[i]:
working_stack = [(i, self.neighbor_gen(i))]
continue_flag = True
while working_stack:
v, gen = working_stack[-1]
visited[v] = True
working_stack.pop()
continue_flag = True
while continue_flag:
next_neighbor = next(gen, None)
if next_neighbor is None:
continue_flag = False
stack.append(v)
elif not visited[next_neighbor]:
working_stack.append((v, gen))
working_stack.append((next_neighbor,
self.neighbor_gen(next_neighbor)))
continue_flag = False
# Print contents of the stack in reverse
print("The following is a Topological Sort of the given graph:")
print(stack[::-1])
g = Graph(6)
g.addEdge(5, 2)
g.addEdge(5, 0)
g.addEdge(4, 0)
g.addEdge(4, 1)
g.addEdge(2, 3)
g.addEdge(3, 1)
g.nonRecursiveTopologicalSort()
12
OUTPUT:
RESULT:
Thus the programs have been executed using Decrease and Conquer
Recursive and Non-Recursive Topological Sort.
13
EX. NO. : 04
TRANSFORM AND CONQUER- HEAP SORT
DATE :
AIM:
To sort given numbers in to ascending order using Transform and Conquer – Heap
sort method.
Step 1: Build Max Heap: Convert the array into a max heap (a complete
binary tree where each node is greater than or equal to its children).
Step 2: Heapify: Starting from the last non-leaf node and moving up to
the root, heapify the array by ensuring that each subtree is a max heap.
Step 3: Swap and Heapify Down: Swap the root (maximum element) with
the last element, reduce the heap size by 1, and heapify the root.
Step 4: Repeat: Repeat steps 3 until the heap size is greater than 1.
PROGRAM:
#(i) Transform and Conquer-Heapsort use to sort numbers get from user.
def heapsort(alist):
build_max_heap(alist)
for i in range(len(alist) - 1, 0, -1):
alist[0], alist[i] = alist[i], alist[0]
max_heapify(alist, index=0, size=i)
def parent(i):
return (i - 1) // 2
def left(i):
return 2 * i + 1
def right(i):
return 2 * i + 2
def build_max_heap(alist):
length = len(alist)
start = parent(length - 1)
while start >= 0:
14
if largest != index:
alist[largest], alist[index] = alist[index], alist[largest]
max_heapify(alist, largest, size)
OUTPUT:
Case 1:
Enter the list of numbers: 3 2 2 1 0 -2 5 7
Sorted list: [-2, 0, 1, 2, 2, 3, 5, 7]
Case 2:
Enter the list of numbers: 1 4 3 2 5
Sorted list: [1, 2, 3, 4, 5]
15
largest = r
# Build a max-heap.
# Since the last parent will be at ((n//2)-1), we can start at that location.
for i in range(n // 2 - 1, -1, -1):
heapify(arr, n, i)
OUTPUT:
Sorted array is
5
6
7
11
12
13
RESULT:
Thus the programs have been executed using Transform and Conquer- Heap Sort.
16
AIM:
To implement Coin Change Problem, Warshall’s and Floyd’s Algorithm &
Knapsack Problem in Dynamic Programming.
Step 1: Define the problem: Clearly define the problem and the objective you want to optimize.
Step 2: Identify subproblems: Break down the main problem into smaller overlapping
subproblems. Subproblems should have optimal substructure, meaning the optimal
solution to the main problem can be constructed from optimal solutions to its
subproblems.
Step 3: Formulate a recursive relation: Express the solution to a subproblem in terms of
solutions to smaller subproblems. This recursive relation will guide the construction of the
dynamic programming algorithm.
Step 4: Memorization or bottom-up approach: Choose between a top-down approach with
memorization (storing solutions to subproblems in a data structure like a table or cache)
or a bottom-up approach where you solve the subproblems in a specific order, starting
from the smallest ones.
Step 5: Define base cases: Identify the base cases, which are the smallest subproblems that can
be solved directly without further decomposition.
Step 6: Build the solution: Either recursively build the solution using memorization or
iteratively solve subproblems in a bottom-up manner, using the recursive relation.
Step 7: Optimize: If possible, optimize the algorithm further by eliminating redundant
computations or unnecessary storage.
PROGRAM:
i) DYNAMIC PROGRAMMING:
def kProfit(W, N, wt, pr, dp):
# Base Condition
if N == 0 or W == 0:
return 0
if wt[N - 1] <= W:
dp[N][W] = max(pr[N - 1] + kProfit(W - wt[N - 1], N - 1, wt, pr, dp), kProfit(W, N - 1, wt, pr, dp))
else:
dp[N][W] = kProfit(W, N - 1, wt, pr, dp)
17
return dp[N][W]
if __name__ == '__main__':
W = 11
wt = [1, 2, 5, 6, 7]
pr = [1, 6, 18, 22, 28]
N = len(pr)
# Define DP array
dp = [[None] * (W + 1) for _ in range(N + 1)]
OUTPUT:
Maximum Profit is : 40
# Initialize table[0][i] to 1 for all i, as there's one way to make the sum 0 (no coins used).
for i in range(m):
table[0][i] = 1
# Total count
table[i][j] = x + y
return table[n][m - 1]
18
OUTPUT:
TOTAL NO. OF COINS = 4
# Algorithm
def floyd(G):
dist = list(map(lambda p: list(map(lambda q: q, p)), G))
sol(dist)
floyd(G)
OUTPUT:
0 5 15 10
20 0 10 5
30 35 0 15
15 20 5 0
19
return K[n][W]
OUTPUT:
220
RESULT:
Thus the programs have been executed using Dynamic Programming – Coin
Change Problem, Warshall’s and Floyd’s Algorithm & Knapsack Problem.
20
AIM:
To implement Dijkstra’s Algorithm, Huffman Trees and Codes using Greedy
Technique.
Greedy algorithms are often used for optimization problems like minimum spanning trees,
Huffman coding, and interval scheduling. However, they may not always guarantee the globally
optimal solution. It's crucial to demonstrate that the greedy choice is safe and leads to an optimal
solution.
PROGRAM:
i) GREEDY TECHNIQUES:
activities = [
["A1", 0, 6],
["A2", 3, 4],
["A3", 1, 2],
["A4", 5, 8],
["A5", 5, 7],
["A6", 8, 9]
]
for j in range(len(activities)):
if activities[j][1] >= activities[i][2]:
print(activities[j][0])
i=j
21
printMaxActivities(activities)
OUTPUT:
A3
A2
A5
A6
class Graph:
def __init__(self, vertices):
self.V = vertices
self.graph = [[0 for column in range(vertices)] for row in range(vertices)]
for v in range(self.V):
if dist[v] < min_val and not sptSet[v]:
min_val = dist[v]
min_index = v
return min_index
for _ in range(self.V):
u = self.minDistance(dist, sptSet)
sptSet[u] = True
for v in range(self.V):
if (
not sptSet[v]
22
self.printSolution(dist)
# Driver program
g = Graph(9)
g.graph = [
[0, 4, 0, 0, 0, 0, 0, 8, 0],
[4, 0, 8, 0, 0, 0, 0, 11, 0],
[0, 8, 0, 7, 0, 4, 0, 0, 2],
[0, 0, 7, 0, 9, 14, 0, 0, 0],
[0, 0, 0, 9, 0, 10, 0, 0, 0],
[0, 0, 4, 14, 10, 0, 2, 0, 0],
[0, 0, 0, 0, 0, 2, 0, 1, 6],
[8, 11, 0, 0, 0, 0, 1, 0, 7],
[0, 0, 2, 0, 0, 0, 6, 7, 0],
]
g.dijkstra(0)
OUTPUT:
def children(self):
return self.left, self.right
23
def __str__(self):
return f'{self.left}, {self.right}'
def huffman_code_tree(node, binString=''):
if type(node) is str:
return {node: binString}
(l, r) = node.children()
d = dict()
d.update(huffman_code_tree(l, binString + '0'))
d.update(huffman_code_tree(r, binString + '1'))
return d
def make_tree(nodes):
if __name__ == '__main__':
string = 'BCAADDDCCACACAC'
freq = dict(Counter(string))
freq = sorted(freq.items(), key=lambda x: x[1], reverse=True)
node = make_tree(freq)
encoding = huffman_code_tree(node)
24
OUTPUT:
C:0
B : 100
D : 101
A : 11
RESULT:
Thus the programs have been executed using Greedy Techniques- Dijkstra’s
Algorithm, Huffman Trees and Codes.
25
EX. NO. : 07
ITERATIVE IMPROVEMENT - SIMPLEX METHOD
DATE :
AIM:
To implement Iterative Improvement - Simplex Method to find optimal solution.
This process aims to incrementally enhance the solution by iteratively making adjustments
and evaluating their impact. It's commonly used in optimization problems or situations where
finding the best solution involves refining an initial guess.
PROGRAM:
import numpy as np
def simplex_algorithm(table):
m, n = table.shape
iteration = 0
basis = []
while True:
iteration += 1
26
basis.append(f'x{pivot_col}')
pivot_element = table[pivot_row, pivot_col]
return basis
basis = simplex_algorithm(tableau)
27
OUTPUT:
Iteration: 1
B CB XB y1 y2 y3 y4
0.0 12.0 -1.0 1.0 1.0 0.0 0.0
0.0 30.0 1.0 1.0 0.0 1.0 0.0
0.0 90.0 2.0 5.0 0.0 0.0 1.0
0.0 0.0 0.0 0.0 0.0 0.0 0.0
0.0 0.0 -4.0 -6.0 0.0 0.0 0.0
RESULT:
Thus the program has been executed using Iterative Improvement - Simplex
Method.
28
AIM:
To implement N-Queen Problem and Subset Sum Problem using Backtracking
method.
Step 1: Define the Problem: Clearly define the problem and establish the constraints.
Step 2: Create a Solution Space: Identify a representation for potential solutions
and create a solution space.
Step 3: Initialize: Set up an initial solution or starting point.
Step 4: Explore: Systematically explore the solution space. At each step, make a choice
and move forward.
Step 5: Constraint Check: At each choice, check if it satisfies the problem constraints.
If not, backtrack and explore other options.
Step 6: Terminate or Output: If a solution is found, output it. Otherwise, backtrack
until all possibilities are exhausted.
Step 7: Recursive Structure: Implement the backtracking algorithm using recursion.
The recursive calls represent different choices in the solution space.
❖ Generic example in pseudocode:
function backtrack(candidate):
if candidate is a solution:
output(candidate)
return
❖ This pseudocode can be adapted and extended based on the specific problem you're
solving.
❖ Remember to implement the functions is_valid, generate_candidates, make_choice,
and undo_choice according to the requirements of your problem.
❖ For a concrete example, you might want to explore the N-Queens problem using
backtracking.
29
PROGRAM:
i) N-QUEEN PROBLEM
30
if __name__ == '__main__':
N = 4 # Change N as needed
board = [[0] * (N + 1) for _ in range(N + 1)]
if n_queen(1, N, N, board):
for i in range(1, N + 1):
print(board[i][1:])
else:
print("No solution exists.")
OUTPUT:
[0, 1, 0, 0]
[0, 0, 0, 1]
[1, 0, 0, 0]
[0, 0, 1, 0]
# Check if the sum can be obtained by either including or excluding the last element.
return isSubsetSum(arr, n - 1, target_sum) or isSubsetSum(arr, n - 1, target_sum - arr[n - 1])
if isSubsetSum(arr, n, target_sum):
print("Found a subset with the given sum")
else:
print("No subset with the given sum")
31
OUTPUT:
Found a subset with given sum
RESULT:
Thus the programs have been executed using Backtracking – N-Queen Problem
and Subset Sum Problem.
32
AIM:
To implement Traveling Salesman Problem using Branch and Bound to find
shortest path in minimum cost.
Step 6: Stop when the priority queue is empty or the problem space has been fully explored.
➢ The key idea is to systematically explore the solution space, pruning branches that cannot
lead to an optimal solution based on calculated bounds.
➢ This helps reduce the search space, making the algorithm more efficient.
➢ It's important to note that the specific implementation details can vary based on the
nature of the optimization problem being solved.
➢ The success of the branch and bound algorithm often relies on the efficiency of the
bounding function used to estimate the quality of partial solutions.
PROGRAM:
import math
maxsize = float('inf')
def copyToFinal(curr_path):
final_path[:N + 1] = curr_path[:]
final_path[N] = curr_path[0]
33
if level == N:
if adj[curr_path[level - 1]][curr_path[0]] != 0:
curr_res = curr_weight + adj[curr_path[level - 1]][curr_path[0]]
if curr_res < final_res:
copyToFinal(curr_path)
final_res = curr_res
return
for i in range(N):
if adj[curr_path[level - 1]][i] != 0 and not visited[i]:
temp = curr_bound
curr_weight += adj[curr_path[level - 1]][i]
if level == 1:
curr_bound -= (firstMin(adj, curr_path[level - 1]) + firstMin(adj, i)) / 2
else:
curr_bound -= (secondMin(adj, curr_path[level - 1]) + firstMin(adj, i)) / 2
global final_path
curr_bound = 0
curr_path = [-1] * (N + 1)
visited = [False] * N
for i in range(N):
curr_bound += firstMin(adj, i) + secondMin(adj, i)
curr_bound = math.ceil(curr_bound / 2)
visited[0] = True
curr_path[0] = 0
TSPRec(adj, curr_bound, 0, 1, curr_path, visited)
# Driver code
adj = [[0, 10, 15, 20],
[10, 0, 35, 25],
[15, 35, 0, 30],
[20, 25, 30, 0]]
N=4
final_path = [None] * (N + 1)
final_res = maxsize
TSP(adj)
print("Minimum cost:", final_res)
print("Path Taken:", end=' ')
for i in range(N + 1):
print(final_path[i], end=' ')
OUTPUT:
Minimum cost : 80
Path Taken : 0 1 3 2 0
RESULT:
Thus the programs have been executed using Branch and Bound - Traveling
Salesman Problem.
35