Open In App

Uninformed Search Algorithms in AI

Last Updated : 07 Apr, 2025
Comments
Improve
Suggest changes
Like Article
Like
Report

Uninformed search algorithms is also known as blind search algorithms, are a class of search algorithms that do not use any domain-specific knowledge about the problem being solved.

  • Uninformed search algorithms rely on the information provided in the problem definition, such as the initial state, actions available in each state, and the goal state.
  • These are called "blind" because they do not have a heuristic function to guide the search towards the goal instead, they explore the search space systematically.
  • Uninformed search algorithms provide basic search strategies for exploring problem spaces where no additional knowledge is available beyond the problem definition.
  • These algorithms are important for solving a wide range of problems in AI, such as pathfinding, puzzle solving, and state-space search.

While these algorithms may not always be the most efficient, they provide a baseline for understanding and solving complex problems in AI.

Types of Uninformed Search Algorithms

1. Breadth-First Search (BFS)

Breadth-First Search explores all nodes at the current depth before moving to the next level. It uses a queue (FIFO) to keep track of nodes, ensuring that the shortest path is found in an unweighted graph.

Key Features:

  • Guarantees the shortest path if the cost is uniform.
  • Uses more memory as it stores all child nodes.\

Code Implementation:

Here,

  • The bfs() function finds the shortest path in a 2D maze using the Breadth-First Search (BFS) algorithm.
    • It starts from a given position and explores neighbors (up, down, left, right).
    • A queue stores positions along with the path taken.
    • A visited set prevents revisiting positions.
    • If the goal is reached, the function returns the complete path; otherwise, it returns None.
  • The visualize_maze() function displays the maze and the found path using Matplotlib.
    • Different colors represent open paths, obstacles, start, goal, and the solution path.
    • imshow() displays the maze, and scatter() marks key positions.
Python
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
from collections import deque

def bfs(maze, start, goal):
    queue = deque([(start, [])])
    visited = set()
    
    while queue:
        current, path = queue.popleft()
        x, y = current
        
        if current == goal:
            return path + [current]
        
        if current in visited:
            continue
        
        visited.add(current)
        
        for dx, dy in [(0, 1), (0, -1), (1, 0), (-1, 0)]:
            nx, ny = x + dx, y + dy
            if 0 <= nx < len(maze) and 0 <= ny < len(maze[0]) and maze[nx][ny] != 1:
                queue.append(((nx, ny), path + [current]))
    
    return None

def visualize_maze(maze, start, goal, path=None):
    cmap = ListedColormap(['white', 'black', 'red', 'blue', 'green'])
    bounds = [0, 0.5, 1.5, 2.5, 3.5, 4.5]
    norm = plt.Normalize(bounds[0], bounds[-1])
    
    fig, ax = plt.subplots()
    ax.imshow(maze, cmap=cmap, norm=norm)
    
    ax.scatter(start[1], start[0], color='yellow', marker='o', label='Start')
    ax.scatter(goal[1], goal[0], color='purple', marker='o', label='Goal')
    
    if path:
        for node in path[1:-1]:
            ax.scatter(node[1], node[0], color='green', marker='o')
    
    ax.legend()
    plt.show()

# Example maze
maze = np.array([
    [0, 0, 0, 0, 0],
    [1, 1, 0, 1, 1],
    [0, 0, 0, 0, 0],
    [0, 1, 1, 1, 0],
    [0, 0, 0, 0, 0]
])

start = (0, 0)
goal = (4, 4)

path = bfs(maze, start, goal)

visualize_maze(maze, start, goal, path)

Output:

download-(3)
Graphical Representation of the maze grid with start position represented by yellow circle and goal position represented by purple circle.

2. Depth-First Search (DFS)

Depth-First Search (DFS) explores as far as possible along each path before backtracking. It uses a stack (LIFO) and is more memory-efficient than Breadth-First Search (BFS) but does not guarantee the shortest path.

Key Features:

  • Efficient for deep exploration.
  • Can get stuck in infinite loops if cycles exist.

Code Implementation:

Here,

  • The dfs() function finds a path in a 2D maze using the Depth-First Search (DFS) algorithm.
    • It starts from a given position and explores neighbors (up, down, left, right).
    • A stack stores positions along with the path taken.
    • A visitedset prevents revisiting positions.
    • If the goal is reached, the function returns the complete path; otherwise, it returns None.
  • The visualize_maze() function displays the maze and the found path using Matplotlib.
    • Different colors represent open paths, obstacles, start, goal, and the solution path.
    • imshow() displays the maze, and scatter() marks key positions.
Python
def visualize_maze(maze, start, goal, path=None):
    cmap = ListedColormap(['white', 'black', 'red', 'blue', 'green'])
    bounds = [0, 0.5, 1.5, 2.5, 3.5, 4.5]
    norm = plt.Normalize(bounds[0], bounds[-1])
    
    fig, ax = plt.subplots()
    ax.imshow(maze, cmap=cmap, norm=norm)
    
    ax.scatter(start[1], start[0], color='yellow', marker='o', label='Start')
    ax.scatter(goal[1], goal[0], color='purple', marker='o', label='Goal')
    
    if path:
        for node in path[1:-1]:
            ax.scatter(node[1], node[0], color='green', marker='o')
    
    ax.legend()
    plt.show()

def dfs(maze, start, goal):
    stack = [(start, [])]
    visited = set()
    
    while stack:
        current, path = stack.pop()
        x, y = current
        
        if current == goal:
            return path + [current]
        
        if current in visited:
            continue
        
        visited.add(current)
        
        for dx, dy in [(0, 1), (0, -1), (1, 0), (-1, 0)]:
            nx, ny = x + dx, y + dy
            if 0 <= nx < len(maze) and 0 <= ny < len(maze[0]) and maze[nx][ny] != 1:
                stack.append(((nx, ny), path + [current]))
    
    return None

# Example maze
maze = np.array([
    [0, 0, 0, 0, 0],
    [1, 1, 0, 1, 1],
    [0, 0, 0, 0, 0],
    [0, 1, 1, 1, 0],
    [0, 0, 0, 0, 0]
])

start = (0, 0)
goal = (4, 4)

path = dfs(maze, start, goal)

visualize_maze(maze, start, goal, path)

Output:

download-(4)
The output represents he maze grid with start position represented by yellow circle and goal position represented by purple circle.

3. Depth-Limited Search (DLS)

Depth Limited Search (DLS) is a variation of Depth First Search (DFS) that limits the depth of exploration to prevent infinite loops in large or infinite search spaces.

Key Features:

  • Useful when the goal depth is known.
  • Cannot find solutions beyond the depth limit.

4. Iterative Deepening Depth-First Search (IDDFS)

Iterative Deepening Depth-First Search (IDDFS) combines Breadth-First Search (BFS) and Depth First Search (DFS) by running Depth First Search (DFS) with increasing depth limits until a solution is found.

Key Features:

  • Ensures completeness and optimality like Breadth-First Search (BFS).
  • Uses less memory than Breadth-First Search (BFS).

5. Uniform-Cost Search (UCS)

Uniform-Cost Search (UCS) extends Breadth-First Search (BFS) by considering path costs, always expanding the least-cost node first. It guarantees finding the optimal path when all costs are non-negative.

Key Features:

  • Finds the least-cost path.
  • Slower than Breadth-First Search (BFS) in uniform cost cases.

Code Implementation:

Here,

  • The uniform_cost_search() function finds the least-cost path in a weighted graph.
    • It uses a priority queue (heapq) to explore nodes with the lowest cost first.
    • A frontier stores nodes to be explored, and an exploredset tracks visited nodes.
    • If the goal is reached, the function reconstructs and returns the optimal path.
  • The reconstruct_path() function traces back from the goal to reconstruct the found path.
  • The visualize_graph() function displays the graph using NetworkX and Matplotlib.
    • Nodes and edges are drawn, with weights labeled.
    • If a path is found, its edges are highlighted in red.
Python
import networkx as nx
import matplotlib.pyplot as plt
import heapq

class Node:
    def __init__(self, state, parent=None, action=None, path_cost=0):
        self.state = state
        self.parent = parent
        self.action = action
        self.path_cost = path_cost

    def __lt__(self, other):
        return self.path_cost < other.path_cost

def uniform_cost_search(graph, start, goal):
    frontier = []
    heapq.heappush(frontier, Node(start))
    explored = set()
    path = []
    
    while frontier:
        node = heapq.heappop(frontier)
        
        if node.state == goal:
            path = reconstruct_path(node)
            break
        
        explored.add(node.state)
        
        for (cost, result_state) in graph[node.state]:
            if result_state not in explored:
                child_cost = node.path_cost + cost
                child_node = Node(result_state, node, None, child_cost)
                if not any(frontier_node.state == result_state and 
                           frontier_node.path_cost <= child_cost for frontier_node in frontier):
                    heapq.heappush(frontier, child_node)
                    
    return path

def reconstruct_path(node):
    path = []
    while node:
        path.append(node.state)
        node = node.parent
    return path[::-1]

def visualize_graph(graph, path=None):
    G = nx.DiGraph()
    labels = {}
    for node, edges in graph.items():
        for cost, child_node in edges:
            G.add_edge(node, child_node, weight=cost)
            labels[(node, child_node)] = cost
    
    pos = nx.spring_layout(G)
    nx.draw(G, pos, with_labels=True, node_color='lightblue', node_size=2000)
    nx.draw_networkx_edge_labels(G, pos, edge_labels=labels)
    
    if path:
        path_edges = list(zip(path[:-1], path[1:]))
        nx.draw_networkx_edges(G, pos, edgelist=path_edges, edge_color='r', width=2)
    
    plt.show()

# Define the graph
graph = {
    'A': [(1, 'B'), (3, 'C')],
    'B': [(2, 'D')],
    'C': [(5, 'D'), (2, 'B')],
    'D': []
}

start = 'A'
goal = 'D'
path = uniform_cost_search(graph, start, goal)

visualize_graph(graph, path)
print("Path from", start, "to", goal, ":", path)

Output:

Path from A to D : ['A', 'B', 'D']

download-(5)
The graph represent the optimal path determined by the UCS algorithm

Applications of Uninformed Search Algorithm

  • Pathfinding: Used in navigation systems and robotics to find the shortest route between locations. BFS and DFS explore the map to determine the optimal path.
  • Puzzle Solving: Applied in problems like the Eight Puzzle or Fifteen Puzzle to determine a sequence of moves leading to a solution.
  • Game AI: Helps in exploring game trees to find winning strategies. BFS and DFS are often combined with minimax or alpha-beta pruning for efficiency.
  • Robot Navigation: Assists robots in path planning by exploring environments and identifying the best route while avoiding obstacles.
  • Web Crawling: BFS is widely used in web crawlers to systematically visit and index web pages by following links.

Uninformed search algorithms has its own strengths and weaknesses, and the choice of algorithm depends on the specific problem at hand. By understanding these algorithms, AI practitioners can apply them effectively to a wide range of problems, from pathfinding in games to route planning in logistics.


Next Article

Similar Reads