0% found this document useful (0 votes)
24 views19 pages

A* and Best First Search Algorithms

The document discusses the A* Search and Best First Search algorithms, highlighting their mechanisms, optimality, and performance. A* guarantees the shortest path using both actual and heuristic costs, while Best First Search focuses solely on heuristic values, making it faster but less reliable for optimal paths. The implementation of both algorithms is demonstrated through code examples and a comparison of their results on a graph.

Uploaded by

ligey70482
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
24 views19 pages

A* and Best First Search Algorithms

The document discusses the A* Search and Best First Search algorithms, highlighting their mechanisms, optimality, and performance. A* guarantees the shortest path using both actual and heuristic costs, while Best First Search focuses solely on heuristic values, making it faster but less reliable for optimal paths. The implementation of both algorithms is demonstrated through code examples and a comparison of their results on a graph.

Uploaded by

ligey70482
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd

AILAB

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.
AILAB
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.
AILAB
▪ 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).
AILAB
▪ 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).

Best First Search is a heuristic search algorithm that explores a


graph by expanding the most promising node first, according to
a specified evaluation function. We use a priority queue or heap
to store the costs of nodes that have the lowest evaluation
function value. So the implementation is a variation of BFS, we
just need to change Queue to PriorityQueue. Heuristic function
is nothing but a shortcut to solve a given problem when either
the exact solution is non-existing or it takes huge time to find
the same.

Key Features:

Uses a heuristic function to evaluate and prioritize nodes. This


heuristic is used to estimate the "cost" or "distance" from a
given node to the goal.

Follows a greedy strategy, focusing on expanding the most


promising node based on the heuristic
AILAB
Since BFS only uses the heuristic and doesn’t account for the
total path cost, it doesn’t guarantee finding the optimal solution
unless the heuristic is specifically designed to be admissible
and consistent

It prevents the system from becoming entangled in loops of


previously tested paths or nodes and helps avoid errors.

Branching Factor and Depth:

In BFS, the branching factor b refers to the average number of


child nodes

that each node has in a tree. The depth d refers to how many
levels deep the

traversal goes, starting from the root.

Nodes Traversed in a Tree with Branching Factor b and Depth d:

1. At depth 0 (the root), there is 1 node.

2. At depth 1, there are b nodes.

3. At depth 2, there are b^2 nodes.

4. And so on, up to depth d, where there are b^d nodes.

Time Complexity:

The time complexity of BFS depends on the total number of


nodes and

edges it must explore:

● Node Exploration:
AILAB
○ Each node is visited exactly once, contributing O(V), where V
is the number of nodes.

● Edge Exploration:

○ Each edge is traversed exactly once while exploring the


nodes, contributing O(E), where E is the number of edges. In
a tree, the number of edges is E = V - 1, meaning there are
fewer edges compared to general graphs.

● Worst Case for a Tree:

The number of nodes explored at each level grows


exponentially. For a tree with branching factor b and depth d,
the total number of nodes explored up to depth d is
approximately O(b^d).

Therefore, the time complexity of BFS in a tree-like structure is


O(b^d) where:

■ b is the branching factor (average number of children per


node)

■ d is the depth of the tree.

Space Complexity:

The space complexity of BFS is dominated by the space


required to store

the queue and the visited nodes. This results in the following:

● The number of nodes stored in the queue is at mostO(b^d)


where b is the branching factor and d is the depth of the tree or
graph.

● The number of nodes stored in the visited list is also O(b^d),


in the worst case.

Therefore, the space complexity of BFS is: O(b^d)


AILAB
Algorithm:

Best-First-Search(Tree root)

PriorityQueue pq;

[Link](root)

Until PriorityQueue is empty

u = [Link]

If u is the goal

Exit

Else

[Link](u->right)

[Link](u->left)

Code:

#include <iostream>

#include <queue>

#include <sstream>

using namespace std;

// TreeNode structure

struct TreeNode {
AILAB
int val;

TreeNode* left;

TreeNode* right;

TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}

};

// Comparator to assign priority

struct Compare {

bool operator()(TreeNode* a, TreeNode* b) {

return a->val > b->val;

};

void bestFirstSearch(TreeNode* root) {

if (!root) return;

// Create a priority queue

priority_queue<TreeNode*, vector<TreeNode*>, Compare>


pq;

[Link](root);

while (![Link]()) {

TreeNode* current = [Link]();

[Link]();
AILAB
cout << current->val << " ";

// Push children of node into queue

if (current->left) [Link](current->left);

if (current->right) [Link](current->right);

TreeNode* createTree() {

cout << "Enter the tree nodes in level order (use -1 for null
nodes): ";

string input;

getline(cin, input);

stringstream ss(input);

int val;

ss >> val;

if (val == -1) return nullptr;

TreeNode* root = new TreeNode(val);

queue<TreeNode*> q;

[Link](root);

while (![Link]()) {

TreeNode* node = [Link]();


AILAB
[Link]();

if (ss >> val) {

if (val != -1) {

node->left = new TreeNode(val);

[Link](node->left);

if (ss >> val) {

if (val != -1) {

node->right = new TreeNode(val);

[Link](node->right);

return root;

int main() {

TreeNode* root = createTree();

cout << "Best First Search: ";

bestFirstSearch(root);

cout << endl;


AILAB
return 0;

2]A* Search vs Best First Search

Feature A* Algorithm Best-First Search


Finds a path (not
Objective Finds the shortest
necessarily the
path
shortest)
Cost Function Uses f(n) = g(n) + Uses only h(n)
h(n)
Optimality Guarantees shortest Not optimal
path
Completeness Complete Not complete
Heuristic Requires
Works with any
Requirement admissible
heuristic
heuristic
Combines Dijkstra
Algorithm Greedy search
and heuristic search
Type
Time O(b^d) O(b^m)
Complexity
Space O(b^d) O(b^m)
Complexity
Ideal for shortest Suitable for quick
Use Case
path applications solutions
Guarantees Guarantees optimal No guarantees
path

Code:
import heapq
AILAB
class Graph:
def init (self):
[Link] =
{} self.h =
{}

def add_edge(self, node, neighbour,


cost): if node not in [Link]:
[Link][node] = []
[Link][node].append((neighbour,
cost))
AILAB
def set_heuristic(self,
heuristics): self.h =
heuristics

# A* Search Algorithm
def a_star_search(graph, start, goal):
open_list = []
[Link](open_list, (0 + graph.h[start], 0,
start, [])) closed_list = set()

while open_list:
_, cost, node, path =
[Link](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
[Link](node, []): if neighbour not in
closed_list:
[Link](open_list, (cost + move_cost +
graph.h[neighbour], cost + move_cost, neighbour, path))
return None

# Best First Search Algorithm


AILAB
def best_first_search(graph, start, goal):
open_list = []
AILAB
[Link](open_list, (graph.h[start], start, []))
closed_list = set()

while open_list:
_, node, path =
[Link](open_list) if node in
closed_list:
continue
path = path +
[node] if node ==
goal:
return path
closed_list.add(node)
for neighbour, _ in
[Link](node, []): if
neighbour not in closed_list:
[Link](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)
AILAB
graph.add_edge('E', 'H', 3)
graph.add_edge('F', 'H', 4)
AILAB
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'))
AILAB
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.
AILAB
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.

You might also like