You are on page 1of 29

2316043

Practical -1

Aim:

Sort a given set of elements using Insertion sort method and determine the time required to sort
the elements. Plot the graph of the time taken versus n. The elements can be generated using
random number generator.

Pseudo Code for Insertion Sort:

Insertion-Sort (A)

for j = 2 to A.length()
key = A[ j ]
// Insert A[ j ] into the sorted sequence A[ 1….j-1 ]
i = j -1
while i > 0 and A[ i ] > key
A [ i +1 ] = A [ i ]
i=i–1
A[ i + 1 ] = key

Code:
import time

import numpy.random as np

import matplotlib.pyplot as plt

list1=[]

def insertion_sort(A):

for j in range(2,len(A)):

key=A[j]

i=j-1

while i>-1 and A[i]>key:

A[i+1]=A[i]

1
2316043
i=i-1

A[i+1]=key

list2=[]

for i in range(5,0,-1):

A=np.randint(i, 900,i*1000)

list1.append(len(A))

Starttime=time.clock()

insertion_sort(A)

endtime=time.clock()

list2.append(endtime - Starttime)

plt.plot(list1,list2)

print(list1, "\n", list2)

Output:
[5000, 4000, 3000, 2000, 1000]
[4.181740838952919, 2.6366416284978413, 1.5231062973732605,
0.6836392882361224, 0.16351879782666856]

2
2316043

Practical – 2

Aim:

Sort a given set of elements using Merge sort and determine the time required to sort the
elements. Plot a graph of time taken versus n. The elements can be generated random number
generator.

Pseudo Code for Merge Sort:

Merge ( A , p , q , r )

n1  q – p +1

n2  r – q
create arrays L [ 1..…n1+1] & R [ 1……n2+1]
for i  1 to n1
do L[ i ]  A[ p + i -1 ]
for j  1 to n2
do R[ j ]  A[ q + p]
L[ n1 + 1]  ∞

R[ n2 + 1]  ∞

I1
J1
for k  p to r
do if L[ i ] <= R[ j ]
A[ k ]  L [ i ]
i  i+1
else
A[k] R[j]
Jj+1

Merge_Sort ( A , p , r )

if p < r then
q  L [ ( p + (r -1))//2 ]
Merge_Sort ( A , p, q )

3
2316043

Merge_Sort ( A , q+1 , r )
Merge ( A , p , q , r )

Code:
import time

import numpy.random as np

import matplotlib.pyplot as plt

list1=[]

def merge(arr,p,q,r):

n1=q-p+1

n2=r-q

L=[0]*(n1)

R=[0]*(n2)

for i in range(0,n1):

L[i]=arr[p+i]

for j in range(0,n2):

R[j]=arr[q+1+j]

i=0

j=0

k=1

while(i<n1 and j<n2):

if(L[i]<=R[j]):

arr[k]=L[i]

i+=1

else:

arr[k]=R[j]

j+=1

4
2316043
k+=1

while i<n1:

arr[k]=L[i]

i+=1

k+=1

while j<n2:

arr[k]=R[j]

j+=1

k+=1

def mergeSort(arr,p,r):

if p<r:

q=(p+(r-1))//2

mergeSort(arr,p,q)

mergeSort(arr,q+1,r)

merge(arr,p,q,r)

list2=[]

for i in range(0,5,1):

arr=np.randint(i, 900,i*1000)

list1.append(len(arr))

Starttime=time.clock()

mergeSort(arr,0,len(arr)-1)

endtime=time.clock()

list2.append(endtime - Starttime)

plt.plot(list1,list2)

print(list1, "\n", list2)

5
2316043

Output:
[0, 1000, 2000, 3000, 4000]
[2.133328784736932e-06, 0.00887720772862366, 0.01926822556112029,
0.03390627433328319, 0.057405744201076914]

--

6
2316043

Practical – 3

Aim:

Sort a given set of elements using Quick sort and determine the time required to sort the
elements. Plot a graph of time taken versus n. The elements can be generated random number
generator.

Algorithm:

Quick_Sort ( A, p , r )

if p < r then

q  Partition ( A , p , r )

Quick_Sort ( A , p , q-1 )
Quick_Sort ( A , q+1 , r )

Partition ( A , p , r )

XA[r]

i  p -1
for j  p to r-1
do if A[ j ] <= x then
i  i +1
Exchange A[ i ]  A[ j ]
Exchange A[ i+1 ]  A[ r ]
return i+1

Code:
import time

import numpy.random as np

import matplotlib.pyplot as plt

list1=[]

def partition(A,p,r):

7
2316043
pivot=A[r]

i=p-1

for j in range(p,r):

if A[j]<=pivot:

i=i+1

A[i],A[j]=A[j],A[i]

A[i+1],A[r]=A[r],A[i+1]

return (i+1)

def quicksort(A,p,r):

if p<r:

q=partition(A,p,r)

quicksort(A,p,q-1)

quicksort(A,q+1,r)

list2=[]

A = None

for i in range(5,0,-1):

A=np.randint(i, 900,i*1000)

list1.append(len(A))

Starttime=time.clock()

quicksort(A,0,len(A)-1)

endtime=time.clock()

list2.append(endtime - Starttime)

plt.plot(list1,list2)

print(list1, "\n", list2)

8
2316043

Output:
[5000, 4000, 3000, 2000, 1000]
[0.04519831225363902, 0.03296019772623282, 0.024484453939617623,
0.013818320519646932, 0.006184599314458694]

9
2316043

Practical – 4

Aim:

Sort a given set of elements using Heap sort and determine the time required to sort the elements.
Plot a graph of time taken versus n. The elements can be generated random number generator

Algorithm:
MaxHeapify(A, i)

    l  left(i) # left(i) = 2*i + 1

    r  right(i) #right(i)=2*i +2

    if l <= heap-size[A] and A[l] > A[i]

        then largest  l

        else largest  i

    if r <= heap-size[A] and A[r] > A[largest]

        then largest  r

    if largest != i

        then swap A[i] with A[largest]

        MaxHeapify(A, largest)

end function

BuildMaxHeap(A)

    heap-size[A]  length[A]

    for i  length[A] to -1

        do MaxHeapify(A, i)

end function

HeapSort(A)

    BuildMaxHeap(A)

10
2316043

    for i = length[A]-1 to 0

        do swap A[1] with A[i]

            heap-size[A] = heap-size[A] – 1

            MaxHeapify(A, 1)

end function

Code:
import numpy.random as np

import matplotlib.pyplot as plt

import time

class Heap:

def __init__(self):

self.arr = None

self.size = None

def left(i):

return 2*i+1

def right(i):

return 2*i+2

def max_heapify(heap,i):

L=left(i)

R=right(i)

#print(i)

if L < heap.size and heap.arr[L]>heap.arr[i]:

largest=L

else:

largest=i

11
2316043
if R < heap.size and heap.arr[R]>heap.arr[largest]:

largest=R

if largest != i:
heap.arr[i],heap.arr[largest]=heap.arr[largest],heap.arr[i]

max_heapify(heap,largest)

def build_max_heap(heap):

for i in range(heap.size//2,-1,-1):

max_heapify(heap,i)

def heap_sort(heap):

build_max_heap(heap)

for i in range(heap.size-1,0,-1):

heap.arr[i],heap.arr[0] = heap.arr[0],heap.arr[i]

heap.size -= 1

max_heapify(heap,0)

list2=[]

list1 = []

for i in range(5,0,-1):

heap = Heap()

heap.arr = np.randint(i, 800,50*i)

heap.size = len(heap.arr)

list1.append(heap.size)

Starttime=time.clock()

heap_sort(heap)

endtime=time.clock()

list2.append(endtime - Starttime)
12
2316043
print(list1)

print(list2)

plt.plot(list1,list2)

plt.show()

Output:

13
2316043

Practical-5

Aim:
a. Print all the nodes reachable from a given starting node in a digraph using BFS method.
b. Check whether a given graph is connected or not using DFS method.

Algorithm:

bfs(graph, root)

visited, queue  list(), deque([root])

visited.append(root)

while queue

vertex  queue.popleft()

for neighbour in nx.neighbors(graph, vertex):

if neighbor is not visited

visited.append(neighbour)

queue.append(neighbour)

return visited

dfs(graph,root,visited = None)

if visited == None

visited  list()

visited.append(root)

for neighbor in nx.neighbors(graph, root)

if neighbor is not visited

dfs(graph,neighbor,visited)

return visited

14
2316043

Code:
import networkx as nx

from matplotlib import pyplot as plt

from _collections import deque

def bfs(graph, root):

visited, queue = list(), deque([root])

visited.append(root)

while queue:

vertex = queue.popleft()

for neighbour in nx.neighbors(graph, vertex):

if neighbour not in visited:

visited.append(neighbour)

queue.append(neighbour)

return visited

def dfs(graph,root,visited = None):

if visited == None:

visited = list()

visited.append(root)

for neighbor in nx.neighbors(graph, root):

if neighbor not in visited:

dfs(graph,neighbor,visited)

return visited

G = nx.DiGraph()

G.add_edge(5,1)
15
2316043
G.add_edge(1,2)

G.add_edge(2,3)

G.add_edge(2,5)

G.add_edge(5,3)

G.add_edge(2,1)

G.add_edge(3,4)

nx.draw_networkx(G)

plt.show()

print("BFS : " ,bfs(G, 1))

print("DFS : " ,dfs(G, 1))

Output:

16
2316043

Practical – 6

Aim:

To implement Longest Common Subsequence.

Algorithm:
lcs(X,Y,m,n)

if(m==0 or n==0)

return (0)

elif(X[m-1]==Y[n-1])

return (1+lcs(X,Y,m-1,n-1))

else:

return max(lcs(X,Y,m,n-1),lcs(X,Y,m-1,n))

Code:
def lcs(X,Y,m,n):

if(m==0 or n==0):

return (0)

elif(X[m-1]==Y[n-1]):

return (1+lcs(X,Y,m-1,n-1))

else:

return max(lcs(X,Y,m,n-1),lcs(X,Y,m-1,n))

X="knowledge"

Y="pledge"

print("length of lcs is ",lcs(X,Y,len(X),len(Y)))

Output:

17
2316043

Practical – 7

Aim:
Compute the transitive closure of a given directed graph using Warshall's algorithm.

Algorithm:

for k  1 to n do

for I  1 to n do

for j 1 to n do

R(k-1)[i,j]  R(k-1) [i,j] or (R(k-1) [i,k] and R(k-1) [k,j])

return R(n)

Code:
import networkx as nx

from matplotlib import pyplot as plt

def printSolution(reach):

ll=[]

print ("Following matrix transitive closure of the given graph ")

for i in range(4):

l=[]

for j in range(4):

l.append(reach[i][j])

ll.append(l)

for row in ll:

for elem in row:

print(elem, end=' ')

print()

def transitiveClosure(graph):

reach =[i[:] for i in graph]

18
2316043
for k in range(4):

for i in range(4):

for j in range(4):

reach[i][j] = reach[i][j] or (reach[i][k] and reach[k]


[j])

printSolution(reach)

G = nx.DiGraph()

G.add_edge(1,1)

G.add_edge(1,2)

G.add_edge(1,3)

G.add_edge(2,2)

G.add_edge(2,3)

G.add_edge(3,3)

G.add_edge(3,4)

G.add_edge(4,4)

G.add_edge(4,3)

nx.draw_networkx(G)

plt.show()

graph = [[1, 1, 0, 1],

[0, 1, 1, 0],

[0, 0, 1, 1],

[0, 0, 1, 1]]

transitiveClosure(graph)

19
2316043

Output:

20
2316043

Practical – 8

Aim:

Implement N Queen's problem using Back Tracking.


Algorithm:

1. Create a class QueenChessBoard.


2. The board configuration is stored in a list called columns where if c = columns[r], then a queen is at
column c and row r.
3. The class provides methods to place a queen in the next row given the column, remove the queen from
the last row where a queen is present, check whether a given column is safe to place a queen in the next
free row and to display the board.
4. Define the function solve_queen that takes the size of the board as argument.
5. It creates a QueenChessBoard object with that size.
6. A while loop is created with condition True.
7. In each iteration, it is checked to see in which column a queen can be placed in the first available row
of the board. This is done using another loop inside the while loop.
8. If such a column is there, the queen is placed there and the inner loop is stopped.
9. After the inner loop, if a column was not found or the board became full, then we need to backtrack.
10. If the board is full, then before backtracking, we need to display the board.
11. Backtracking is done by removing the queen from the last filled row and making the next iteration try
column values above the column values of the queen just removed.
12. If it is found that no more queens can be removed, then we are finished.
13. The number of solutions is kept track of and printed at the end.

Code:

class QueenChessBoard:

def __init__(self, size):

self.size = size

self.columns = []

def place_in_next_row(self, column):

self.columns.append(column)

def remove_in_current_row(self):

21
2316043
return self.columns.pop()

def is_this_column_safe_in_next_row(self, column):

row = len(self.columns)

for queen_column in self.columns:

if column == queen_column:

return False

for queen_row, queen_column in enumerate(self.columns):

if queen_column - queen_row == column - row:

return False

for queen_row, queen_column in enumerate(self.columns):

if ((self.size - queen_column) - queen_row

== (self.size - column) - row):

return False

return True

def display(self):

for row in range(self.size):

for column in range(self.size):

if column == self.columns[row]:

print('Q', end=' ')

else:

print('.', end=' ')

print()

def solve_queen(size):

board = QueenChessBoard(size)

number_of_solutions = 0

row = 0

22
2316043
column = 0

while True:

while column < size:

if board.is_this_column_safe_in_next_row(column):

board.place_in_next_row(column)

row += 1

column = 0

break

else:

column += 1

if (column == size or row == size):

if row == size:

board.display()

print()

number_of_solutions += 1

board.remove_in_current_row()

row -= 1

try:

prev_column = board.remove_in_current_row()

except IndexError:

break

row -= 1

column = 1 + prev_column

print('Number of solutions:', number_of_solutions)

n = int(input('Enter n: '))

solve_queen(n)

23
2316043

Output:

24
2316043

Practical – 9

Aim:

Obtain the Topological ordering of vertices in a given digraph


Algorithm:

Steps involved in finding the topological ordering of a DAG:


Step-1: Compute in-degree (number of incoming edges) for each of the vertex present in the DAG
and initialize the count of visited nodes as 0.
Step-2: Pick all the vertices with in-degree as 0 and add them into a queue (Enqueue operation)
Step-3: Remove a vertex from the queue (Dequeue operation) and then.
1. Increment count of visited nodes by 1.
2. Decrease in-degree by 1 for all its neighboring nodes.
3. If in-degree of a neighboring nodes is reduced to zero, then add it to the queue.
Step 5: Repeat Step 3 until the queue is empty.
Step 5: If count of visited nodes is not equal to the number of nodes in the graph then the topological
sort is not possible for the given graph.

Code:

import networkx as nx

from matplotlib import pyplot as plt

def TopologicalSort(graph):

TopologicalSortedList = []

ZeroInDegreeVertexList = []

inDegree = { u : 0 for u in graph }

for u in graph:

for v in graph[u]:

inDegree[v] += 1

for k in inDegree:

if (inDegree[k] == 0):

ZeroInDegreeVertexList.append(k)

while ZeroInDegreeVertexList:

v = ZeroInDegreeVertexList.pop(0)

TopologicalSortedList.append(v)

25
2316043
for neighbour in graph[v]:

inDegree[neighbour] -= 1

if (inDegree[neighbour] == 0):

ZeroInDegreeVertexList.append(neighbour)

return TopologicalSortedList

graph = {

'1': set(['2','3']),

'2': set(['4']),

'3': set(['2','4']),

'4': set([])

G = nx.DiGraph()

G.add_edge(1,2)

G.add_edge(2,4)

G.add_edge(1,3)

G.add_edge(3,2)

G.add_edge(3,4)

nx.draw_networkx(G)

plt.show()

result = TopologicalSort(graph)

print("Topological sort >>> ", result)

if (len(result) == len(graph)):

print("Directed Acyclic Graph!")

else:

print("Graph has cycles!")

26
2316043

Output

27
2316043

Practical – 9

Aim:

To implement Huffman coding using Greedy algo.


Algorithm:

HUFFMAN(C)

n ← |C|

Q←C

for i 1 to n - 1

do allocate a new node z

left[z] ← x ← EXTRACT-MIN (Q)

right[z] ← y ← EXTRACT-MIN (Q)

f [z] ← f [x] + f [y]

INSERT(Q, z)

return EXTRACT-MIN(Q)

Code:
import heapq

from collections import defaultdict

def encode(frequency):

heap = [[weight, [symbol, '']] for symbol, weight in


frequency.items()]

heapq.heapify(heap)

while len(heap) > 1:

lo = heapq.heappop(heap)

hi = heapq.heappop(heap)

for pair in lo[1:]:

pair[1] = '0' + pair[1]

for pair in hi[1:]:

28
2316043
pair[1] = '1' + pair[1]

heapq.heappush(heap, [lo[0] + hi[0]] + lo[1:] + hi[1:])

return sorted(heapq.heappop(heap)[1:], key=lambda p: (len(p[-1]),


p))

data = "SHIVANI"

frequency = defaultdict(int)

for symbol in data:

frequency[symbol] += 1

huff = encode(frequency)

print ("Symbol".ljust(10) + "Weight".ljust(10) + "Huffman Code")

for p in huff:

print (p[0].ljust(10) + str(frequency[p[0]]).ljust(10) + p[1])

Output:

29

You might also like