AI LAB
Krish Shah
SY CS
231070061
Batch D
Aim: To perform A* search and Best First Search
Theory:
1] A* Search Algorithm
1.1. Description
The A* (A-star) algorithm is a widely used path finding and graph
traversal algorithm. It is designed to find the shortest path from a starting
node to a goal node in a weighted graph. A* combines the advantages
of Dijkstra's algorithm (which guarantees the shortest path) and Greedy
Best-First Search (which is efficient but does not guarantee the shortest
path). It uses a heuristic function to estimate the cost from the current
node to the goal, making it more efficient than Dijkstra's in many cases.
1.2. How does it work?
A* finds the optimal path by evaluating nodes using the function:
f(n)=g(n)+h(n)
where:
f(n) → Estimated total cost from start to goal passing through node
n.
g(n) → Cost from the start node to node n (known cost).
h(n) → Heuristic function that estimates the cost from node n to
the goal.
AI LAB
Heuristic Function h(n): The choice of heuristic function is critical to A*'s
performance. A heuristic is:
Admissible → it never overestimates the cost to reach the goal
(ensures optimality).
Consistent (Monotonic) → It satisfies the condition:
h(n) ≤ c(n,m)+h(m)
where c(n,m) is the actual cost from n to m.
Common Heuristics Used
Manhattan Distance (for grid-based paths): h(n)=|x1−x2|+|y1−y2|
Euclidean Distance (for continuous space paths):
h(n)=(x1−x2)^2+(y1−y2)^2
Chebyshev Distance (for diagonal moves in grids):
h(n)=max(|x1−x2|,|y1−y2|)
1.3. Algorithm
1. Initialization:
o Create an open list (priority queue) and a closed list (visited
nodes).
o Add the start node to the open list with f(n) = g(n) + h(n).
o Set g(n) for the start node to 0.
2. Main Loop:
o While the open list is not empty:
Select the node with the lowest f(n) from the open list.
If this node is the goal, reconstruct the path and return
it.
Otherwise, move the node to the closed list.
3. Expand Neighbours:
o For each neighbour of the current node:
Calculate g(n) for the neighbour as g(current) +
cost(current, neighbour).
If the neighbour is already in the closed list and the
new g(n) is higher, skip it.
If the neighbour is not in the open list or the new g(n) is
lower:
Update the neighbour's g(n) and f(n).
AI LAB
Add the neighbour to the open list if it's not
already there.
4. Termination:
o If the open list is empty and the goal has not been reached,
no path exists.
1.4. Time Complexity
The time complexity of A* depends on the heuristic function and
the structure of the graph.
In the worst case, A* explores all nodes, leading to a time
complexity of O(b^d), where:
o b is the branching factor (average number of neighbours per
node).
o d is the depth of the goal node (number of steps to reach the
goal).
With a good heuristic, the number of explored nodes can be
significantly reduced.
1.5. Space Complexity
The space complexity is O(b^d) because A* stores all explored
nodes in memory (open and closed lists).
2] Best First Search Algorithm
2.1. Description
Best-First Search is a graph traversal algorithm that explores a graph by
prioritizing nodes that appear to be the closest to the goal. Unlike A*, it
does not consider the cost to reach the current node (g(n)) and relies
solely on a heuristic function (h(n)) to estimate the cost from the current
node to the goal. This makes it a greedy algorithm, as it always chooses
the path that seems best at the moment.
2.2. How does it work?
1. Best-First Search uses a priority queue to explore nodes based on
their heuristic value (h(n)).
2. It starts at the initial node and expands the node with the lowest
heuristic value.
3. The algorithm continues until the goal node is reached or all
possible paths have been explored.
AI LAB
4. Unlike A*, it does not guarantee the shortest path because it
ignores the actual cost to reach the current node (g(n)).
2.3. Algorithm
1. Initialization:
o Create an open list (priority queue) and a closed list (visited
nodes).
o Add the start node to the open list with its heuristic value
h(n).
2. Main Loop:
o While the open list is not empty:
Select the node with the lowest h(n) from the open list.
If this node is the goal, reconstruct the path and return
it.
Otherwise, move the node to the closed list.
3. Expand Neighbours:
o For each neighbour of the current node:
Calculate the heuristic value h(n) for the neighbour.
If the neighbour is already in the closed list, skip it.
If the neighbour is not in the open list, add it to the
open list.
4. Termination:
o If the open list is empty and the goal has not been reached,
no path exists.
2.4. Time Complexity
The time complexity of Best-First Search depends on the heuristic
function and the structure of the graph.
In the worst case, it explores all nodes, leading to a time
complexity of O(b^m), where:
o b is the branching factor (average number of neighbours per
node).
o m is the maximum depth of the search tree.
The efficiency of the algorithm depends on the quality of the
heuristic function.
2.5. Space Complexity
The space complexity is O(b^m) because the algorithm stores all
explored nodes in memory (open and closed lists).
AI LAB
3] A* Search vs Best First Search
Feature A* Algorithm Best-First Search
Finds a path (not
Objective Finds the shortest path
necessarily the shortest)
Cost Function Uses f(n) = g(n) + h(n) Uses only h(n)
Optimality Guarantees shortest path Not optimal
Completeness Complete Not complete
Heuristic Requires admissible
Works with any heuristic
Requirement heuristic
Combines Dijkstra and
Algorithm Type Greedy search
heuristic search
Time Complexity O(b^d) O(b^m)
Space Complexity O(b^d) O(b^m)
Ideal for shortest path Suitable for quick
Use Case
applications solutions
Guarantees Guarantees optimal path No guarantees
Code:
import heapq
class Graph:
def __init__(self):
self.edges = {}
self.h = {}
def add_edge(self, node, neighbour, cost):
if node not in self.edges:
self.edges[node] = []
self.edges[node].append((neighbour, cost))
AI LAB
def set_heuristic(self, heuristics):
self.h = heuristics
# A* Search Algorithm
def a_star_search(graph, start, goal):
open_list = []
heapq.heappush(open_list, (0 + graph.h[start], 0, start, []))
closed_list = set()
while open_list:
_, cost, node, path = heapq.heappop(open_list)
if node in closed_list:
continue
path = path + [node]
if node == goal:
return path
closed_list.add(node)
for neighbour, move_cost in graph.edges.get(node, []):
if neighbour not in closed_list:
heapq.heappush(open_list, (cost + move_cost +
graph.h[neighbour], cost + move_cost, neighbour, path))
return None
# Best First Search Algorithm
def best_first_search(graph, start, goal):
open_list = []
AI LAB
heapq.heappush(open_list, (graph.h[start], start, []))
closed_list = set()
while open_list:
_, node, path = heapq.heappop(open_list)
if node in closed_list:
continue
path = path + [node]
if node == goal:
return path
closed_list.add(node)
for neighbour, _ in graph.edges.get(node, []):
if neighbour not in closed_list:
heapq.heappush(open_list, (graph.h[neighbour], neighbour,
path))
return None
graph = Graph()
graph.add_edge('A', 'B', 1)
graph.add_edge('A', 'C', 4)
graph.add_edge('B', 'D', 2)
graph.add_edge('C', 'D', 1)
graph.add_edge('D', 'E', 5)
graph.add_edge('B', 'F', 3)
graph.add_edge('D', 'G', 2)
graph.add_edge('E', 'H', 3)
graph.add_edge('F', 'H', 4)
AI LAB
graph.add_edge('G', 'H', 1)
graph.set_heuristic({'A': 10, 'B': 8, 'C': 7, 'D': 6, 'E': 4, 'F': 5, 'G': 3, 'H': 0})
print("A* Search Path:", a_star_search(graph, 'A', 'H'))
print("Best First Search Path:", best_first_search(graph, 'A', 'H'))
AI LAB
Output:
Conclusion:
The implementation of A* Search and Best-First Search highlights their
differences in finding paths in a graph. Both algorithms were tested on a
graph with heuristic values, and the paths from node 'A' to node 'H' were
derived.
1. A Search:*
o Path: ['A', 'B', 'D', 'G', 'H']
o A* Search considers both the actual cost (g(n)) and the
heuristic (h(n)), which ensures that the shortest path is
found. In this case, the algorithm traversed through the
optimal nodes while maintaining a balance between cost and
heuristic evaluation. A* guarantees the shortest path due to
its combination of Dijkstra's algorithm and heuristic-based
search.
2. Best-First Search:
o Path: ['A', 'C', 'D', 'G', 'H']
o Best-First Search relies solely on the heuristic value (h(n)),
which leads it to choose nodes that appear closest to the
goal. Although it reached the goal, it does not guarantee the
shortest path since the actual cost (g(n)) is not considered.
This makes Best-First Search faster but less reliable for
optimal paths.
Comparison:
A* Search is optimal and guarantees the shortest path by using the
cost function f(n)=g(n)+h(n)
Best-First Search is a greedy algorithm that prioritizes nodes
based on h(n), making it efficient for quick solutions but not
necessarily accurate in finding the shortest path.
AI LAB
In this example, the A* algorithm provided a shorter path compared to
the Best-First Search, demonstrating its advantage when optimality is
required. Best-First Search, however, may still be suitable for scenarios
where speed is prioritized over precision.