Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 10

SVKM’s NMIMS University

Mukesh Patel School of Technology Management & Engineering


COURSE: Principles of Artificial Intelligence

Experiment: 2
PART A
(PART A: TO BE REFFERED BY STUDENTS)

Aim: Uninformed Search: Write a program to implement BFS and DFS.


Theory:
Introduction to Graph Traversals:
Graph traversal means visiting every vertex and edge exactly once in a well-defined order.
While using certain graph algorithms, you must ensure that each vertex of the graph is
visited exactly once. The orders in which the vertices are visited are important and may
depend upon the algorithm or question that you are solving.
There are several graph traversal techniques such as Breadth-First Search, Depth-First
Search and so on. The challenge is to use a graph traversal technique that is most suitable
for solving a particular problem.
Breadth First Search:
BFS is a traversing algorithm where you should start traversing from a selected node
(source or starting node) and traverse the graph layer wise thus exploring the neighbour
nodes (nodes which are directly connected to source node). You must then move towards
the next-level neighbor nodes.
As the name BFS suggests, you are required to traverse the graph breadthwise as follows:
1. First move horizontally and visit all the nodes of the current layer
2. Move to the next layer
Understanding the Breadth-First Search Algorithm with an example:
Breadth-First Search algorithm follows a simple, level-based approach to solve a problem.
Consider the below binary tree (which is a graph). Our aim is to traverse the graph by using
the Breadth-First Search Algorithm.
A queue is an abstract data structure that follows the First-In-First-Out methodology (data
inserted first will be accessed first). It is open on both ends, where one end is always used
to insert data (enqueue) and the other is used to remove data (dequeue).
SVKM’s NMIMS University
Mukesh Patel School of Technology Management & Engineering
COURSE: Principles of Artificial Intelligence

Now let’s take a look at the steps involved in traversing a graph by using Breadth-First
Search:
Step 1: Take an Empty Queue.
Step 2: Select a starting node (visiting a node) and insert it into the Queue.
Step 3: Provided that the Queue is not empty, extract the node from the Queue and insert its
child nodes (exploring a node) into the Queue.
Step 4: Print the extracted node.
Example:
We will use the Breadth-First Search algorithm to traverse through the graph.

We’ll assign node ‘a’ as the root node and start traversing downward and follow the steps
mentioned above.
SVKM’s NMIMS University
Mukesh Patel School of Technology Management & Engineering
COURSE: Principles of Artificial Intelligence

The above image depicts the end-to-end process of Breadth-First Search Algorithm.
1. Assign ‘a’ as the root node and insert it into the Queue.
2. Extract node ‘a’ from the queue and insert the child nodes of ‘a’, i.e., ‘b’ and ‘c’.
3. Print node ‘a’.
4. The queue is not empty and has node ‘b’ and ‘c’. Since ‘b’ is the first node in the
queue, let’s extract it and insert the child nodes of ‘b’, i.e., node ‘d’ and ‘e’.
5. Repeat these steps until the queue gets empty. Note that the nodes that are already
visited should not be added to the queue again.

The pseudo-code of Breadth-First Search algorithm is as follows:


Input: s as the source node

BFS (G, s)
let Q be queue.
Q.enqueue( s )
mark s as visited
while ( Q is not empty)
v = Q.dequeue( )

for all neighbors w of v in Graph G


if w is not visited
Q.enqueue( w )
mark w as visited
In the above code, the following steps are executed:
1. (G, s) is input, here G is the graph and s is the root node
2. A queue ‘Q’ is created and initialized with the source node ‘s’
3. All child nodes of ‘s’ are marked
4. Extract ‘s’ from queue and visit the child nodes
SVKM’s NMIMS University
Mukesh Patel School of Technology Management & Engineering
COURSE: Principles of Artificial Intelligence

5. Process all the child nodes of v


6. Stores w (child nodes) in Q to further visit its child nodes
7. Continue till ‘Q’ is empty

Performance Measures of BFS:


Let s = the depth of the shallowest solution.
n = number of nodes in level i
i

Time complexity: Equivalent to the number of nodes traversed in BFS until the shallowest
solution.
2 3 s s
T ( n )=1+n +n + …+n =O(n )
Space complexity: Equivalent to how large can the fringe get.
S ( n )=O( ns )
Completeness: BFS is complete, meaning for a given search tree, BFS will come up with a
solution if it exists.
Optimality: BFS is optimal as long as the costs of all edges are equal.

Applications of BFS:
1. Crawlers in Search Engines
2. GPS Navigation systems
3. Find the Shortest Path & Minimum Spanning Tree for an un-weighted graph
4. Broadcasting
5. Peer to Peer Networking

Depth-first search:
Depth-first search (DFS) is an algorithm for traversing or searching tree or graph data
structures. The algorithm starts at the root node (selecting some arbitrary node as the root
node in the case of a graph) and explores as far as possible along each branch before
backtracking.
Example:
For the following graph:

A Depth-First Search starting at A, assuming that the left edges in the shown graph are
chosen before right edges, and assuming the search remembers previously visited nodes and
will not repeat them (since this is a small graph), will visit the nodes in the following order:
A, B, D, F, E, C, G. Performing the same search without remembering previously visited
SVKM’s NMIMS University
Mukesh Patel School of Technology Management & Engineering
COURSE: Principles of Artificial Intelligence

nodes results in visiting nodes in the order A, B, D, F, E, A, B, D, F, E, etc. forever, caught


in the A, B, D, F, E cycle and never reaching C or G.

Output of a depth-first search:

The four types of edges defined by a spanning tree


A convenient description of a depth-first search of a graph is in terms of a spanning tree of
the vertices reached during the search. Based on this spanning tree, the edges of the original
graph can be divided into three classes: forward edges, which point from a node of the tree
to one of its descendants, back edges, which point from a node to one of its ancestors,
and cross edges, which do neither. Sometimes tree edges, edges which belong to the
spanning tree itself, are classified separately from forward edges. If the original graph is
undirected then all of its edges are tree edges or back edges.

Pseudocode
Input: A graph G and a vertex v of G
Output: All vertices reachable from v labeled as discovered
A recursive implementation of DFS:
procedure DFS(G,v):
label v as discovered
for all directed edges from v to w that are in G.adjacentEdges(v) do
if vertex w is not labeled as discovered then
recursively call DFS(G,w)
The order in which the vertices are discovered by this algorithm is called the lexicographic
order.
A non-recursive implementation of DFS with worst-case space complexity 
procedure DFS-iterative(G,v):
let S be a stack
S.push(v)
while S is not empty
v = S.pop()
if v is not labeled as discovered:
label v as discovered
SVKM’s NMIMS University
Mukesh Patel School of Technology Management & Engineering
COURSE: Principles of Artificial Intelligence

for all edges from v to w in G.adjacentEdges(v) do


S.push(w)
These two variations of DFS visit the neighbors of each vertex in the opposite order from
each other: the first neighbor of v visited by the recursive variation is the first one in the list
of adjacent edges, while in the iterative variation the first visited neighbor is the last one in
the list of adjacent edges. The recursive implementation will visit the nodes from the
example graph in the following order: A, B, D, F, E, C, G. The non-recursive
implementation will visit the nodes as: A, E, F, B, D, C, G.
The non-recursive implementation is similar to breadth-first search but differs from it in two
ways:

1. It uses a stack instead of a queue, and


2. It delays checking whether a vertex has been discovered until the vertex is popped
from the stack rather than making this check before adding the vertex.

Applications of DFS:
Algorithms that use depth-first search as a building block include:

1. Finding connected components.
2. Topological sorting.
3. Finding 2-(edge or vertex)-connected components.
4. Finding 3-(edge or vertex)-connected components.
5. Finding the bridges of a graph.
6. Generating words in order to plot the limit set of a group.
7. Finding strongly connected components.
8. Planarity testing.[7][8]
9. Solving puzzles with only one solution, such as mazes. (DFS can be adapted to find
all solutions to a maze by only including nodes on the current path in the visited set.)
10. Maze generation may use a randomized depth-first search.
11. Finding bi-connectivity in graphs.
SVKM’s NMIMS University
Mukesh Patel School of Technology Management & Engineering
COURSE: Principles of Artificial Intelligence

PART B
(PART B: TO BE COMPLETED BY STUDENTS)
Students must submit the soft copy as per following segments within two hours of the
practical. The soft copy must be uploaded on the portal at the end of the practical. The
filename should be PAI_batch_rollno_Labno Example: PAI_A2_A001_Lab2
Roll No.: Name:
Prog/Yr/Sem: Batch:
Date of Experiment: Date of Submission:

# Python3 Program to print BFS traversal


# from a given source vertex. BFS(int s)
# traverses vertices reachable from s.
from collections import defaultdict

# This class represents a directed graph


# using adjacency list representation
class Graph:

# Constructor
def __init__(self):

# default dictionary to store graph


self.graph = defaultdict(list)

# function to add an edge to graph


def addEdge(self,u,v):
self.graph[u].append(v)

# Function to print a BFS of graph


def BFS(self, s):
SVKM’s NMIMS University
Mukesh Patel School of Technology Management & Engineering
COURSE: Principles of Artificial Intelligence

# Mark all the vertices as not visited


visited = [False] * (len(self.graph))

# Create a queue for BFS


queue = []

# Mark the source node as


# visited and enqueue it
queue.append(s)
visited[s] = True

while queue:

# Dequeue a vertex from


# queue and print it
s = queue.pop(0)
print (s, end = " ")

# Get all adjacent vertices of the


# dequeued vertex s. If a adjacent
# has not been visited, then mark it
# visited and enqueue it
for i in self.graph[s]:
if visited[i] == False:
queue.append(i)
visited[i] = True

# Driver code
SVKM’s NMIMS University
Mukesh Patel School of Technology Management & Engineering
COURSE: Principles of Artificial Intelligence

# Create a graph given in


# the above diagram
g = Graph()
g.addEdge(0, 1)
g.addEdge(0, 2)
g.addEdge(1, 2)
g.addEdge(2, 0)
g.addEdge(2, 3)
g.addEdge(3, 3)

print ("Following is Breadth First Traversal"


" (starting from vertex 2)")
g.BFS(2)

graph = {
'5' : ['3','7'],
'3' : ['2', '4'],
'7' : ['8'],
'2' : [],
'4' : ['8'],
'8' : []
}

visited = set() # Set to keep track of visited nodes of graph.

def dfs(visited, graph, node): #function for dfs


if node not in visited:
print (node)
visited.add(node)
SVKM’s NMIMS University
Mukesh Patel School of Technology Management & Engineering
COURSE: Principles of Artificial Intelligence

for neighbour in graph[node]:


dfs(visited, graph, neighbour)

# Driver Code
print("Following is the Depth-First Search")
dfs(visited, graph, '5')

Conclusion (Learning Outcomes):

You might also like