Lab 3
Lab 3
Student Name: Ibrahim, Hassaan, Huzaifa Reg. No: 220391, 222563, 222590
Objective: __________________________
LAB ASSESMENT:
Ability to
Conduct
Experiment
Ability to
assimilate the
results
Effective use of lab
equipment and
follows
the lab safety rules
Data presentation
Experimental results
Conclusion
Date: Signature:
Lab No. 2
Uniformed Vs Informed Search
Objective:
The objective of this lab is to explore and compare different search strategies in artificial intelligence,
particularly focusing on the differences between informed (heuristic-based) and uninformed (blind)
search algorithms, as well as the distinction between finding any path versus finding the optimal path.
1. Implementing and analyzing uninformed search algorithms such as best-first (BF) and uniform cost
search (UCS), iterative dependation.
Uniform Cost Search Algorithm (UCS):
UCS is often classified as an uninformed search (also known as blind search) because it does not use any
information about the goal state (like a heuristic) to guide its search. Instead, UCS explores nodes based
solely on the cumulative cost from the start node.
Initialization: The algorithm starts by initializing the priority queue with the start node, assigning it
a path cost of 0.
A priority queue (pq) is used to keep track of the nodes to be explored. The priority queue stores
tuples of the form (cost, node, path). Initially, the queue contains the start node with a cost of 0
and an empty path [].
A set visited is used to track nodes that have already been explored. This helps in preventing re-
exploration of the same node, ensuring efficiency
.
The algorithm enters a while loop that continues until the priority queue is empty.
heapq.heappop(pq) removes and returns the node with the lowest cost from the priority queue
(because UCS prioritizes the node with the least cumulative cost).
If the current node has already been visited (i.e., it’s in the visited set), the algorithm skips the
processing for that node and moves on to the next iteration of the loop.
If the node hasn't been visited yet, it's added to the visited set to mark it as explored.
The current node is added to the path by concatenating it to the existing path
The algorithm checks if the current node is the goal node. If it is, the search terminates, and the total
cost and the path leading to the goal are returned as the solution.
Best-First Algorithm:
Best-First Search (BFS) is a greedy search algorithm. It prioritizes exploration of nodes based on an
evaluation function, typically a heuristic, to determine the most promising node.
A priority queue (pq) is used to store tuples of the form (heuristic_value, node, path),
Initially, the priority queue contains the start node, with its heuristic value ( heuristic[start]), and
an empty path.
The algorithm enters a while loop that continues until the priority queue is empty.
heapq.heappop(pq) removes and returns the node with the lowest heuristic value from the priority
queue. This is because Best-First Search is a greedy search algorithm, and it always explores the
node with the lowest estimated cost (heuristic) first.
Output:
Lab Tasks:
Task: Implement the depth first search algorithm. Explain code step by step with screenshots of code.
Task 2: Implement the uniform cost search algorithm. Explain code step by step with screenshots of
code.
Task 3: Implement the best first search algorithm. Explain code step by step with screenshots of code.
Task 1: Breath First Search
Code:
graph = {
'A': ['B', 'C'],
'B': ['D', 'E'],
'C': ['F', 'G'],
'D': [],
'E': [],
'F': [],
'G': ['H', 'I'],
'H': [],
'I': []
}
Output:
['A', 'C', 'G', 'I']
Task 2: Depth First Search
Code:
graph = {
'A': ['B', 'C'],
'B': ['D', 'E'],
'C': ['F', 'G'],
'D': [],
'E': [],
'F': [],
'G': ['H', 'I'],
'H': [],
'I': []
}
Output:
['A', 'C', 'G', 'I']
Task 3: Uniform Cost Search
Code:
graph = {
'A': ['B', 'C'],
'B': ['D', 'E'],
'C': ['F', 'G'],
'D': [],
'E': [],
'F': [],
'G': ['H', 'I'],
'H': [],
'I': []
}
Output:
['A', 'C', 'G', 'I']
Task 4: Best First Search
Code:
graph = {
'A': ['B', 'C'],
'B': ['D', 'E'],
'C': ['F', 'G'],
'D': [],
'E': [],
'F': [],
'G': ['H', 'I'],
'H': [],
'I': []
}
def best_first_search(graph, start, goal, heuristic):
queue = deque([(start, [start], heuristic[start])])
visited = set()
while queue:
# Find the node with the lowest heuristic value
queue = deque(sorted(list(queue), key=lambda x: x[2]))
(node, path, cost) = queue.popleft()
if node not in visited:
visited.add(node)
if node == goal:
return path
for neighbor in graph[node]:
if neighbor not in visited:
queue.append((neighbor, path + [neighbor],
heuristic[neighbor]))
return None
# Example heuristic (replace with your own)
heuristic = {
'A': 10,'B': 8,'C': 5,
'D': 7,'E': 3,'F': 6,
'G': 5,'H': 3,'I': 0
}
path = best_first_search(graph, 'A', 'I', heuristic)
if path:
print(path)
else:
print("No path found")
Output:
['A', 'C', 'G', 'I']
Conclusions:
In this lab, we implemented and explored various search algorithms—Depth First Search (DFS), Breadth
First Search (BFS), Uniform Cost Search, and Best First Search—using Google Colab. Each algorithm
exhibited distinct characteristics and applications based on its design principles. DFS proved efficient in
memory usage and deep branch traversal but struggled to find the shortest path in unweighted graphs.
In contrast, BFS excelled at finding the shortest path in unweighted graphs through level-wise
exploration, though it required more memory for storing all nodes at the current depth. Uniform Cost
Search effectively handled weighted graphs by always exploring the least costly path first, while Best
First Search highlighted the significance of heuristic functions in guiding the search toward promising
paths, albeit potentially being less optimal without well-defined heuristics. Overall, the lab provided
valuable insights into the strengths and weaknesses of these algorithms, emphasizing their suitability for
different problem scenarios, and showcased the advantages of using Google Colab for interactive coding
and visualizing the search processes.