A
Avijit Mohan 08617704423(MCA 3B)
AI/ML LAB PRACTICALS
Q1. Write a program to implement Breadth first search and Depth first search.
Sol.
CODE
from collections import deque
class Graph:
def init (self):
# Dictionary to store the graph as an adjacency list
self.graph = {}
def add_edge(self, node, neighbor):
# Add edge from node to neighbor
if node not in self.graph:
self.graph[node] = []
self.graph[node].append(neighbor)
def bfs(self, start_node):
# Breadth-First Search (BFS) algorithm
visited = set()
queue = deque([start_node])
bfs_order = []
while queue:
node = queue.popleft() # Dequeue a node from the front
if node not in visited:
visited.add(node)
bfs_order.append(node)
# Add all unvisited neighbors to the queue
for neighbor in self.graph.get(node, []):
A
Avijit Mohan 08617704423(MCA 3B)
if neighbor not in visited:
queue.append(neighbor)
return bfs_order
def dfs(self, start_node):
# Depth-First Search (DFS) algorithm (iterative)
visited = set()
stack = [start_node]
dfs_order = []
while stack:
node = stack.pop() # Pop a node from the stack
if node not in visited:
visited.add(node)
dfs_order.append(node)
# Add all unvisited neighbors to the stack
for neighbor in reversed(self.graph.get(node, [])): # Reverse for correct DFS order
if neighbor not in visited:
stack.append(neighbor)
return dfs_order
# Example usage
if name == " main ":
g = Graph()
# Add edges to the graph
g.add_edge(0, 1)
A
Avijit Mohan 08617704423(MCA 3B)
g.add_edge(0, 2)
g.add_edge(1, 2)
g.add_edge(2, 0)
g.add_edge(2, 3)
g.add_edge(3, 3)
# Perform BFS and DFS
print("BFS starting from node 2:", g.bfs(2))
print("DFS starting from node 2:", g.dfs(2))
OUTPUT
A
Avijit Mohan 08617704423(MCA 3B)
Q2. Write a program to implement Best first search and A* algorithm.
Sol.
Best first search
CODE
import heapq
class Graph:
def init (self):
# Dictionary to store the graph as an adjacency list
self.graph = {}
def add_edge(self, node, neighbor, cost):
# Add edge with a cost from node to neighbor
if node not in self.graph:
self.graph[node] = []
self.graph[node].append((neighbor, cost))
def best_first_search(self, start_node, target_node):
# Best First Search algorithm using a priority queue (min-heap)
visited = set()
priority_queue = []
heapq.heappush(priority_queue, (0, start_node)) # (heuristic cost, node)
bfs_order = []
while priority_queue:
cost, node = heapq.heappop(priority_queue)
if node not in visited:
visited.add(node)
A
Avijit Mohan 08617704423(MCA 3B)
bfs_order.append(node)
# If target node is found, return the path
if node == target_node:
return bfs_order
# Explore all neighbors
for neighbor, edge_cost in self.graph.get(node, []):
if neighbor not in visited:
heapq.heappush(priority_queue, (edge_cost, neighbor))
return bfs_order
# Example usage
if name == " main ":
g = Graph()
# Add edges to the graph (node, neighbor, cost)
g.add_edge(0, 1, 1)
g.add_edge(0, 2, 4)
g.add_edge(1, 2, 2)
g.add_edge(1, 3, 5)
g.add_edge(2, 3, 1)
# Perform Best First Search from node 0 to node 3
print("Best First Search Path:", g.best_first_search(0, 3))
OUTPUT
A
Avijit Mohan 08617704423(MCA 3B)
A* Algorithm
CODE
import heapq
class Graph:
def init (self):
# Dictionary to store the graph as an adjacency list
self.graph = {}
def add_edge(self, node, neighbor, cost):
# Add edge with a cost from node to neighbor
if node not in self.graph:
self.graph[node] = []
self.graph[node].append((neighbor, cost))
def heuristic(self, node, target_node):
# Example heuristic: using a dummy heuristic (0 for all nodes)
# You can modify this based on the specific problem, e.g., Euclidean distance,
Manhattan distance, etc.
return 0 # Replace this with an appropriate heuristic for your problem
def a_star(self, start_node, target_node):
# A* Search Algorithm
priority_queue = []
heapq.heappush(priority_queue, (0, start_node)) # (f(n), node)
came_from = {} # To store the path
g_score = {start_node: 0} # Stores the cost from start to each node
f_score = {start_node: self.heuristic(start_node, target_node)} # f(n) = g(n) + h(n)
came_from[start_node] = None
A
Avijit Mohan 08617704423(MCA 3B)
while priority_queue:
current_f_score, current_node = heapq.heappop(priority_queue)
if current_node == target_node:
return self.reconstruct_path(came_from, current_node)
for neighbor, cost in self.graph.get(current_node, []):
tentative_g_score = g_score[current_node] + cost
if neighbor not in g_score or tentative_g_score < g_score[neighbor]:
# Update the path and scores
came_from[neighbor] = current_node
g_score[neighbor] = tentative_g_score
f_score[neighbor] = tentative_g_score + self.heuristic(neighbor, target_node)
heapq.heappush(priority_queue, (f_score[neighbor], neighbor))
return None # If no path is found
def reconstruct_path(self, came_from, current_node):
# Reconstruct the path from the start to the target node
path = []
while current_node is not None:
path.append(current_node)
current_node = came_from[current_node]
path.reverse() # Reverse the path to get it from start to goal
return path
# Example usage
if name == " main ":
A
Avijit Mohan 08617704423(MCA 3B)
g = Graph()
# Add edges to the graph (node, neighbor, cost)
g.add_edge(0, 1, 1)
g.add_edge(0, 2, 4)
g.add_edge(1, 2, 2)
g.add_edge(1, 3, 5)
g.add_edge(2, 3, 1)
# Perform A* search from node 0 to node 3
path = g.a_star(0, 3)
if path:
print("A* Path:", path)
else:
print("No path found")
OUTPUT
A
Avijit Mohan 08617704423(MCA 3B)
Q3. Write a program to implement water jug problem.
Sol.
CODE
from collections import deque
# Function to check if the goal is achievable
def is_solvable(x, y, z):
if z > max(x, y):
return False
return z % gcd(x, y) == 0
# Function to find the Greatest Common Divisor
def gcd(a, b):
while b:
a, b = b, a % b
return a
# BFS function to solve the water jug problem
def water_jug_bfs(x, y, z):
# Initialize the queue for BFS
queue = deque()
queue.append((0, 0)) # Starting state with both jugs empty
visited = set()
visited.add((0, 0))
# To store the sequence of steps
path = []
while queue:
A
Avijit Mohan 08617704423(MCA 3B)
a, b = queue.popleft()
path.append((a, b))
# If we reach the solution (either jug contains exactly z liters)
if a == z or b == z:
return path
# Explore all possible moves (fill, empty, pour)
possible_moves = [
(x, b), # Fill jug 1
(a, y), # Fill jug 2
(0, b), # Empty jug 1
(a, 0), # Empty jug 2
(a - min(a, y - b), b + min(a, y - b)), # Pour jug 1 into jug 2
(a + min(b, x - a), b - min(b, x - a)) # Pour jug 2 into jug 1
]
for move in possible_moves:
if move not in visited:
visited.add(move)
queue.append(move)
return None # If no solution is found
# Main function to run the water jug problem
if name == " main ":
X = 4 # Capacity of Jug 1
Y = 3 # Capacity of Jug 2
Z = 2 # The target amount of water
A
Avijit Mohan 08617704423(MCA 3B)
# Check if the solution is possible
if not is_solvable(X, Y, Z):
print("No solution possible")
else:
# Solve the water jug problem
result = water_jug_bfs(X, Y, Z)
if result:
print("Steps to achieve the target:")
for step in result:
print(f"Jug 1: {step[0]} liters, Jug 2: {step[1]} liters")
else:
print("No solution found")
OUTPUT
A
Avijit Mohan 08617704423(MCA 3B)
Q4. Write a program to implement 4 – Queen problem.
Sol.
CODE
# Function to check if a queen can be placed at (row, col)
def is_safe(board, row, col, N):
# Check for queens in the same column
for i in range(row):
if board[i][col] == 1:
return False
# Check upper left diagonal
for i, j in zip(range(row, -1, -1), range(col, -1, -1)):
if board[i][j] == 1:
return False
# Check upper right diagonal
for i, j in zip(range(row, -1, -1), range(col, N)):
if board[i][j] == 1:
return False
return True
# Recursive utility function to solve the 4-queen problem
def solve_n_queen(board, row, N):
# Base case: If all queens are placed
if row >= N:
return True
# Consider each column and try to place a queen
for col in range(N):
A
Avijit Mohan 08617704423(MCA 3B)
if is_safe(board, row, col, N):
board[row][col] = 1 # Place the queen
# Recur to place the rest of the queens
if solve_n_queen(board, row + 1, N):
return True
# If placing the queen doesn't lead to a solution, backtrack
board[row][col] = 0
# If no column can accommodate a queen, return False
return False
# Function to print the board
def print_board(board, N):
for row in range(N):
print(" ".join(str(board[row][col]) for col in range(N)))
print("\n")
# Main function to solve the 4-queen problem
def solve_4_queen_problem():
N = 4 # Size of the board (4x4)
board = [[0 for _ in range(N)] for _ in range(N)] # Initialize the board
if solve_n_queen(board, 0, N):
print("Solution for 4-Queen problem:")
print_board(board, N)
else:
print("No solution exists")
A
Avijit Mohan 08617704423(MCA 3B)
# Run the program
if name == " main ":
solve_4_queen_problem()
OUTPUT
A
Avijit Mohan 08617704423(MCA 3B)
Q5. Write a program to implement AO* algorithm.
Sol.
CODE
import math
class Graph:
def init (self):
# Dictionary to store graph as an adjacency list
# Each node can have AND/OR branches with cost
self.graph = {}
def add_edge(self, parent, children, is_and_node, cost):
# Add edges to the graph (parent -> children with a specified cost)
self.graph[parent] = {'children': children, 'is_and': is_and_node, 'cost': cost}
def get_heuristic(self, node):
# Heuristic function (you can define this according to your problem)
# For simplicity, we'll return a constant heuristic (usually derived from domain
knowledge)
return 0
def ao_star(self, node, backtrack=False):
open_list = {node} # Nodes that need to be processed
solution_graph = {} # Final solution graph
status = {} # Status of each node (SOLVED or not)
while open_list:
current_node = open_list.pop()
children_info = self.graph.get(current_node)
# Base case: If the node has no children, mark it as solved
A
Avijit Mohan 08617704423(MCA 3B)
if children_info is None:
status[current_node] = "SOLVED"
continue
children = children_info['children']
is_and_node = children_info['is_and']
cost = children_info['cost']
# Evaluate the cost of the current node
min_cost = math.inf
selected_children = []
# If this is an AND node, all children must be solved
if is_and_node:
total_cost = sum(self.get_heuristic(child) for child in children) + cost
if total_cost < min_cost:
min_cost = total_cost
selected_children = children
else: # OR node, select the child with the minimum cost
for child in children:
child_cost = self.get_heuristic(child) + cost
if child_cost < min_cost:
min_cost = child_cost
selected_children = [child]
# If no valid children were found, continue (to avoid NoneType error)
if not selected_children:
continue
# Update the solution graph with the best children
A
Avijit Mohan 08617704423(MCA 3B)
solution_graph[current_node] = selected_children
# Add children to open list for further exploration if they are not solved
for child in selected_children:
if status.get(child) != "SOLVED":
open_list.add(child)
# If all children are solved, mark the current node as solved
if all(status.get(child) == "SOLVED" for child in selected_children):
status[current_node] = "SOLVED"
return solution_graph
# Example usage of the AO* algorithm
if name == " main ":
# Creating a graph for the AO* algorithm
g = Graph()
# Add edges in the form (parent, [children], is_and_node, cost)
# For example, node A has children B and C with a cost, and it is an AND node.
g.add_edge('A', ['B', 'C'], True, 1)
g.add_edge('B', ['D', 'E'], True, 2)
g.add_edge('C', ['F'], False, 3)
g.add_edge('D', [], False, 0) # Terminal node
g.add_edge('E', [], False, 0) # Terminal node
g.add_edge('F', [], False, 0) # Terminal node
# Run AO* algorithm starting from node 'A'
solution = g.ao_star('A')
A
Avijit Mohan 08617704423(MCA 3B)
print("Solution Graph:")
for node in solution:
print(f"{node} -> {solution[node]}")
OUTPUT
A
Avijit Mohan 08617704423(MCA 3B)
Q6. Write a program to implement hill climbing & steepest ascent hill climbing algorithm.
Sol.
Hill climbing
CODE
import random
# Define the function to maximize
def objective_function(x):
return -(x ** 2) + 10 * x + 5
# Define the Hill Climbing algorithm
def hill_climbing(start, step_size, max_iterations):
current_solution = start
current_value = objective_function(current_solution)
for i in range(max_iterations):
# Generate neighboring solutions
neighbor_solution = current_solution + random.uniform(-step_size, step_size)
neighbor_value = objective_function(neighbor_solution)
# If the neighbor is better, move to the neighbor
if neighbor_value > current_value:
current_solution = neighbor_solution
current_value = neighbor_value
print(f"Iteration {i}: Moved to better solution at x = {current_solution}, f(x) =
{current_value}")
else:
print(f"Iteration {i}: No improvement at x = {current_solution}, f(x) =
{current_value}")
# Stop if no improvement
break
A
Avijit Mohan 08617704423(MCA 3B)
return current_solution, current_value
# Example usage of the Hill Climbing algorithm
if name == " main ":
# Starting point for hill climbing
start = random.uniform(-10, 10)
# Parameters for hill climbing
step_size = 0.1
max_iterations = 100
# Run hill climbing
best_solution, best_value = hill_climbing(start, step_size, max_iterations)
print(f"Best solution found: x = {best_solution}, f(x) = {best_value}")
OUTPUT
Steepest ascent hill climbing
CODE
import random
# Define the function to maximize
def objective_function(x):
return -(x ** 2) + 10 * x + 5
# Generate neighboring solutions
def generate_neighbors(solution, step_size, num_neighbors=10):
A
Avijit Mohan 08617704423(MCA 3B)
neighbors = []
for _ in range(num_neighbors):
# Generate a neighbor by making a small step
neighbor = solution + random.uniform(-step_size, step_size)
neighbors.append(neighbor)
return neighbors
# Define the Steepest Ascent Hill Climbing algorithm
def steepest_ascent_hill_climbing(start, step_size, max_iterations):
current_solution = start
current_value = objective_function(current_solution)
for i in range(max_iterations):
# Generate neighbors of the current solution
neighbors = generate_neighbors(current_solution, step_size)
# Evaluate all neighbors and find the best one
best_neighbor = current_solution
best_value = current_value
for neighbor in neighbors:
neighbor_value = objective_function(neighbor)
if neighbor_value > best_value:
best_value = neighbor_value
best_neighbor = neighbor
# If the best neighbor is better than the current solution, move to the best neighbor
if best_value > current_value:
current_solution = best_neighbor
current_value = best_value
A
Avijit Mohan 08617704423(MCA 3B)
print(f"Iteration {i}: Moved to better solution at x = {current_solution}, f(x) =
{current_value}")
else:
# If no better neighbor is found, stop
print(f"Iteration {i}: No better neighbor found, stopping at x = {current_solution},
f(x) = {current_value}")
break
return current_solution, current_value
# Example usage of the Steepest Ascent Hill Climbing algorithm
if name == " main ":
# Starting point for hill climbing
start = random.uniform(-10, 10)
# Parameters for hill climbing
step_size = 0.5
max_iterations = 100
# Run Steepest Ascent Hill Climbing
best_solution, best_value = steepest_ascent_hill_climbing(start, step_size, max_iterations)
print(f"Best solution found: x = {best_solution}, f(x) = {best_value}")
A
Avijit Mohan 08617704423(MCA 3B)
OUTPUT
A
Avijit Mohan 08617704423(MCA 3B)
Q7. Write a program to implement travelling salesman problem.
Sol.
CODE
from itertools import permutations
# Define a function to calculate the total distance of a given tour
def calculate_total_distance(tour, distance_matrix):
total_distance = 0
num_cities = len(tour)
# Sum the distances between consecutive cities in the tour
for i in range(num_cities - 1):
total_distance += distance_matrix[tour[i]][tour[i+1]]
# Add the distance to return to the starting city
total_distance += distance_matrix[tour[-1]][tour[0]]
return total_distance
# Brute Force method to solve TSP
def tsp_brute_force(distance_matrix):
num_cities = len(distance_matrix)
cities = list(range(num_cities)) # Cities are labeled 0, 1, 2, ...
# Generate all possible permutations of cities (except the starting city)
all_possible_tours = permutations(cities)
# Track the shortest tour and its distance
shortest_tour = None
shortest_distance = float('inf')
A
Avijit Mohan 08617704423(MCA 3B)
# Evaluate each possible tour
for tour in all_possible_tours:
current_distance = calculate_total_distance(tour, distance_matrix)
# Update if the current tour is shorter than the best found so far
if current_distance < shortest_distance:
shortest_tour = tour
shortest_distance = current_distance
return shortest_tour, shortest_distance
# Example usage of TSP solver
if name == " main ":
# Example distance matrix for 4 cities (symmetric matrix)
# Each element distance_matrix[i][j] represents the distance between city i and city j
distance_matrix = [
[0, 10, 15, 20],
[10, 0, 35, 25],
[15, 35, 0, 30],
[20, 25, 30, 0]
]
# Solve TSP using brute force
shortest_tour, shortest_distance = tsp_brute_force(distance_matrix)
# Print the result
print("Shortest tour:", shortest_tour)
print("Shortest distance:", shortest_distance)
A
Avijit Mohan 08617704423(MCA 3B)
OUTPUT