A* Algorithm

Teja
6 min readApr 24, 2023

--

Introduction to Pathfinding

Path Finding With Breadth-First Search

Pathfinding is finding the shortest path between two points in a graph or a grid. The goal of pathfinding is to find the optimal path which has the lowest cost or takes the least amount of time. Pathfinding is a fundamental problem in computer science and is used in various applications, such as navigation systems, robotics, video games, and more.

Many algorithms can be used for pathfinding, but the choice of algorithm depends on the problem's characteristics and the application's constraints. Some of the popular algorithms for pathfinding include:

Breadth-First Search (BFS): BFS is a simple algorithm that explores the graph in layers, starting from the source node and visiting all its neighbors before moving on to their neighbors. BFS guarantees it will find the shortest path, but it may not be efficient for large graphs.

Depth-First Search (DFS): DFS explores the graph by visiting as far as possible along each branch before backtracking. DFS is not guaranteed to find the shortest path and can get stuck in cycles, but it may be more efficient than BFS for certain types of graphs.

Dijkstra’s Algorithm: Dijkstra’s algorithm is popular for finding the shortest path in weighted graphs. It works by maintaining a priority queue of nodes to be explored and updating the distances as it visits each node. Dijkstra’s algorithm is guaranteed to find the shortest path, but it may not be efficient for graphs with negative weights.

A* Algorithm: The A* algorithm is an extension of Dijkstra’s algorithm that uses a heuristic function to guide the search. The heuristic function estimates the cost of reaching the goal node from each node, which allows the algorithm to explore the most promising paths first. A* is guaranteed to find the shortest path as long as the heuristic function is admissible.

For this article, let’s have a look at the A* algorithm

A* Algorithm

The A* algorithm is a pathfinding algorithm commonly used in robotics, video games, and other applications that require finding the shortest path between two points. The algorithm uses a heuristic function to guide its search, which makes it more efficient than other search algorithms like Dijkstra’s algorithm.

A* algorithm to find destination node from source

Theory

The A* algorithm works by exploring the neighboring nodes of the starting node and continuing to explore the neighbors of the nodes with the lowest estimated cost. The estimated cost is calculated using a heuristic function, which estimates the distance between the current node and the goal node.

The A* algorithm uses two cost functions to determine the estimated cost of each node: g(n) and h(n). The g(n) function represents the actual cost of reaching node n from the starting node, while the h(n) function is the heuristic function that estimates the cost of reaching the goal node from node n.

The A* algorithm combines these two cost functions to calculate each node's f(n) value, determining the order in which the nodes are explored. The f(n) value is simply the sum of g(n) and h(n): f(n) = g(n) + h(n).

The algorithm maintains a priority queue of nodes to explore, sorted by their f(n) values. The algorithm selects the node with the lowest f(n) value at each step and explores its neighboring nodes. If a neighboring node has not been explored before, it is added to the priority queue with its f(n) value calculated using the g(n) and h(n) functions.

The algorithm continues until it reaches the goal node, which has found the shortest path.

H score

The H score of the A* algorithm is the heuristic estimate of the distance from the current node to the goal node. The total cost of the path from the start node to the goal node is the sum of the path cost from the start node to the current node and the H score of the current node. The following formula represents this:

f(n) = g(n) + h(n)

where f(n) is the total cost of the path from the start node to the goal node passing through node n, g(n) is the cost of the path from the start node to node n, and h(n) is the H score of node n.

The A* algorithm maintains a priority queue of nodes to be explored. Each node in the queue is assigned a priority equal to its f score. The algorithm selects the node with the lowest f score from the queue and expands it. The expanded nodes are added to a closed set to prevent revisiting them. The algorithm stops when the goal node is reached, or the queue is empty.

Types of heuristics

Several types of heuristics can be used with the A* algorithm. Some of the most common heuristics are:

Manhattan distance

The Manhattan distance is the sum of the horizontal and vertical distances between two nodes. It is named after the grid-like street layout of Manhattan. This heuristic is often used in grid-based environments like video games and robotics.

def manhattan_distance(node, goal):
return abs(node.x - goal.x) + abs(node.y - goal.y)

Euclidean distance

The Euclidean distance is the straight-line distance between two nodes. It is the shortest possible distance between two points in a Euclidean space. This heuristic is often used in continuous environments like robotics and autonomous vehicles.

def euclidean_distance(node, goal):
return ((node.x - goal.x) ** 2 + (node.y - goal.y) ** 2) ** 0.5

Diagonal distance

def diagonal_distance(node, goal):
dx = abs(node.x - goal.x)
dy = abs(node.y - goal.y)
return max(dx, dy)

Why A* Algorithm is better than other algorithms for pathfinding

The A* algorithm is often a better choice than other algorithms for pathfinding for several reasons:

  1. Efficiency: The A* algorithm is generally more efficient than other search algorithms, like Dijkstra’s algorithm, because it uses a heuristic function to guide its search. This allows it to explore the most promising paths first, which can lead to significant performance improvements.
  2. Optimality: The A* algorithm is guaranteed to find the shortest path between the starting node and the goal node as long as the heuristic function is admissible (meaning that it never overestimates the cost of reaching the goal). Other algorithms like Breadth-First Search (BFS) and Depth-First Search (DFS) do not guarantee optimality.
  3. Flexibility: The A* algorithm can be used with different graphs, including weighted and unweighted graphs, directed and undirected graphs, and graphs with different edges. Other algorithms like BFS and DFS may only be suitable for some graphs.
  4. Adaptable: The A* algorithm can handle various constraints and objectives, such as avoiding obstacles, minimizing travel time, or minimizing fuel consumption in a robotic system.

Overall, the A* algorithm is a powerful tool for finding the shortest path between two points in a graph. Its efficiency and flexibility make it a popular choice for various applications.

Code

To implement the A* algorithm, we must define the heuristic function, the cost functions, and the priority queue data structure. Here is an example implementation in Python:

import heapq
import math

# Heuristic functions

# Manhattan Distance
def manhattan_distance(node, goal):
return abs(node[0] - goal[0]) + abs(node[1] - goal[1])

# Euclidean Distance
def euclidean_distance(node, goal):
return math.sqrt((node[0] - goal[0]) ** 2 + (node[1] - goal[1]) ** 2)

# Diagonal Distance
def diagonal_distance(node, goal):
dx = abs(node[0] - goal[0])
dy = abs(node[1] - goal[1])
return max(dx, dy)

# Define the A* algorithm
def astar(start, goal, graph, heuristic):
frontier = []
heapq.heappush(frontier, (0, start))
came_from = {}
cost_so_far = {}
came_from[start] = None
cost_so_far[start] = 0

while frontier:
current = heapq.heappop(frontier)[1]
if current == goal:
break

for neighbor in graph.neighbors(current):
new_cost = cost_so_far[current] + graph.cost(current, neighbor)
if neighbor not in cost_so_far or new_cost < cost_so_far[neighbor]:
cost_so_far[neighbor] = new_cost
priority = new_cost + heuristic(neighbor, goal)
heapq.heappush(frontier, (priority, neighbor))
came_from[neighbor] = current

path = [goal]
current = goal
while current != start:
current = came_from[current]
path.append(current)
path.reverse()

return path

References

  1. https://www.codingame.com/learn/pathfinding
  2. https://chat.openai.com/
  3. https://www.techwithtim.net/tutorials/breadth-first-search/

--

--

Teja
0 Followers

Tech Enthusiast | Pursuing Masters@Northeastern University