AI Lab Record
AI Lab Record
AI Lab Record
Case 1
Fill it
Empty it
Case 2
Empty it
Fill it
Case 3 Fill it Fill it Redundant case
Case 4
Empty it
Empty it Already visited (Initial State)
Case 5
Unchanged
Fill it
Case 6
Fill it
Unchanged
Case 7
Unchanged
Empty
Case 8
Empty
Unchanged
From the table above, we can observe that the state where both the jugs are filled
is redundant as we won’t be able to continue ahead / do anything with this state in
any possible way.
So, we proceed, keeping in mind all the valid state cases (as shown in the table
above) and we do a BFS on them.
In the BFS, we firstly skip the states which was already visited or if the amount
of water in either of the jugs exceeded the jug quantity.
If we continue further, then we firstly mark the current state as visited and check
if in this state, if we have obtained the target quantity of water in either of the
jugs, we can empty the other jug and return the current state’s entire path.
But, if we have not yet found the target quantity, we then derive the intermediate
states from the current state of jugs i.e. we derive the valid cases, mentioned in
the table above (go through the code once if you have some confusion).
We keep repeating all the above steps until we have found our target or there are
no more states left to proceed with.
PROGRAM FOR IMPLEMENTING BFS:
# Map is used to store the states, every # state is hashed to binary value to
# indicate either that state is visited # before or not
m = {}
isSolvable = False path = []
# Filling the vector for constructing # the solution path path.append([u[0], u[1]])
# If we have not reached final state # then, start developing intermediate # states
to reach solution state q.append([u[0], b]) # Fill Jug2 q.append([a, u[1]]) # Fill
Jug1
Depth First Traversal (or Search) for a graph is similar to Depth First Traversal
of a tree. The only catch here is, unlike trees, graphs may contain cycles (a node
may be visited twice
). To avoid processing a node more than once, use a boolean visited array.
Example:
Input: n = 4, e = 6
0 -> 1, 0 -> 2, 1 -> 2, 2 -> 0, 2 ->
3, 3 -> 3
Output: DFS from vertex 1 : 1 2 0
3
Explanation:
DFS :
Input: n = 4, e = 6
2 -> 0, 0 -> 2, 1 -> 2, 0 -> 1, 3 ->
3, 1 -> 3
Output: DFS from vertex 2 : 2 0 1 3
Explanation:
• Time complexity: O(V + E), where V is the number of vertices and E is the
number of edges in the graph.
• Space Complexity: O(V), since an extra visited array of size V is required.
PROGRAM:
'''Python program to print DFS traversal for complete graph'''
from collections import defaultdict
# this class represents a directed graph using adjacency list representation
class Graph:
# Constructor
def init (self):
# default dictionary to store graph self.graph = defaultdict(list)
# Function to add an edge to graph
def addEdge(self, u, v): self.graph[u].append(v)
# A function used by DFS
def DFSUtil(self, v, visited):
# Mark the current node as visited and print it visited.add(v)
print(v,end=" ")
# recur for all the vertices adjacent to this vertex
for neighbour in self.graph[v]:
if neighbour not in visited: self.DFSUtil(neighbour, visited)
# The function to do DFS traversal. It uses recursive DFSUtil
def DFS(self):
# create a set to store all visited vertices visited = set()
# call the recursive helper function to print DFS traversal starting from all #
vertices one by one
OUTPUT:
Following is Depth First Traversal 0 1 2 3 9
COMPLEXITY ANALYSIS:
• TIME COMPLEXITY: O(V + E), where V is the number of vertices and E is the
number of edges in the graph.
• SPACE COMPLEXITY: O(V), since an extra visited array of size V is required.
RESULT: Hence BFS and DFS algorithm is implemented to solve water jug problem
AIM: To Implement A* and Memory Bounded A* Search in python A Star Search Algorithm
with a solved numerical
Numbers written on edges represent the distance between nodes. Numbers written on
nodes represent the heuristic value.
PSEUDOCODE:
Given the graph, find the cost-effective path from A to G. That is A is the source
node and G is the goal node.
Since the cost for A → B is less, we move forward with this path and compute the
f(x) for the children nodes of B.
Here the path A → B → G has the least cost but it is still more than the cost of A
→ E, thus we explore this path further.
PROGRAM
def aStarAlgo(start_node, stop_node):
open_set = set(start_node)
closed_set = set()
g = {} #store distance from starting node
g[start_node] = 0
parents[start_node] = start_node
n = None
for v in open_set:
n = v
pass
else:
#nodes 'm' not in first and last set are added to first
open_set.add(m)
parents[m] = n
#for each node m,compare its distance from start i.e g(m) to the
else:
#update g(m)
parents[m] = n
if m in closed_set:
closed_set.remove(m)
open_set.add(m)
if n == None:
return None
if n == stop_node:
path = []
while parents[n] != n:
path.append(n)
n = parents[n]
path.append(start_node)
path.reverse()
return path
open_set.remove(n)
closed_set.add(n)
return None
def get_neighbors(v):
'I': 1,
'J': 0
return H_dist[n]
Graph_nodes = {
aStarAlgo('A', 'J')
Output:
#for simplicity we ll consider heuristic distances given #and this function returns
heuristic distance for all nodes def heuristic(n):
H_dist = { 'A': 11,
'B': 6,
'C': 99,
'D': 1,
'E': 7,
'G': 0,
}
return H_dist[n]
aStarAlgo('A', 'G')
OUTPUT:
In Minimax the two players are called maximizer and minimizer. The maximizer tries
to get the highest score possible while the minimizer tries to do the opposite and
get the lowest score possible.
Every board state has a value associated with it. In a given state if the maximizer
has upper hand then, the score of the board will tend to be some positive value. If
the minimizer has the upper hand in that board state then it will tend to be some
negative value. The values of the board are calculated by some heuristics which are
unique for every type of game.
Example:
Consider a game which has 4 final states and paths to reach final state are from
root to 4 leaves of a perfect binary tree as shown below. Assume you are the
maximizing player and you get the first chance to move, i.e., you are at the root
and your opponent at next level. Which move you would make as a maximizing player
considering that your opponent also plays optimally?
Since this is a backtracking based algorithm, it tries all possible moves, then
backtracks and makes a decision.
• Maximizer goes LEFT: It is now the minimizers turn. The minimizer now has a
choice between 3 and 5. Being the minimizer it will definitely choose the least
among both, that is 3
• Maximizer goes RIGHT: It is now the minimizers turn. The minimizer now has a
choice between 2 and 9. He will choose 2 as it is the least among the two values.
Being the maximizer you would choose the larger value that is 3. Hence the optimal
move for the maximizer is to go LEFT and the optimal value is 3.
Note: Even though there is a value of 9 on the right subtree, the minimizer will
never pick that. We must always assume that our opponent plays optimally.
Below is the implementation for the same.
PROGRAM:
if (maxTurn):
return max(minimax(curDepth + 1, nodeIndex * 2, False, scores, targetDepth),
minimax(curDepth + 1, nodeIndex * 2 + 1, False, scores, targetDepth))
else:
return min(minimax(curDepth + 1, nodeIndex * 2, True, scores, targetDepth),
minimax(curDepth + 1, nodeIndex * 2 + 1, True, scores, targetDepth))
# Driver code
scores = [3, 5, 2, 9, 12, 5, 23, 23]
treeDepth = math.log(len(scores), 2)
OUTPUT:
The optimal value is: 12
a) ALPHA-BETA PRUNING
value = minimax(node, depth+1, true, alpha, beta) bestVal = min( bestVal, value)
beta = min( beta, bestVal)
if beta <= alpha:
break return bestVal
• The initial call starts from A. The value of alpha here is -INFINITY and the
value of beta is +INFINITY. These values are passed down to subsequent nodes in the
tree. At A the maximizer must choose max of B and C, so A calls B first
• At B it the minimizer must choose min of D and E and hence calls D first.
• At D, it looks at its left child which is a leaf node. This node returns a
value of 3. Now the value of alpha at D is max( -INF, 3) which is 3.
• To decide whether its worth looking at its right node or not, it checks the
condition beta<=alpha. This is false since beta = +INF and alpha = 3. So it
continues the search.
• D now looks at its right child which returns a value of 5.At D, alpha =
max(3, 5) which is 5. Now the value of node D is 5
• D returns a value of 5 to B. At B, beta = min( +INF, 5) which is 5. The
minimizer is now guaranteed a value of 5 or lesser. B now calls E to see if he can
get a lower value than 5.
• At E the values of alpha and beta is not -INF and +INF but instead -INF and 5
respectively, because the value of beta was changed at B and that is what B passed
down to E
• Now E looks at its left child which is 6. At E, alpha = max(-INF, 6) which is
6. Here the condition becomes true. beta is 5 and alpha is 6. So beta<=alpha is
true. Hence it breaks and E returns 6 to B
• Note how it did not matter what the value of E‘s right child is. It could
have been
+INF or -INF, it still wouldn’t matter, We never even had to look at it because the
minimizer was guaranteed a value of 5 or lesser. So as soon as the maximizer saw
the 6 he knew the minimizer would never come this way because he can get a 5 on the
left side of B. This way we dint have to look at that 9 and hence saved computation
time.
• E returns a value of 6 to B. At B, beta = min( 5, 6) which is 5.The value of
node B is also 5
So far this is how our game tree looks. The 9 is crossed out because it was never
computed.
This is how our final game tree looks like. As you can see G has been crossed out
as it was never computed.
PROGRAM:
OUTPUT :
The optimal value is : 5
Supervised Learning :
It is the learning where the value or result that we want to predict is within the
training data (labeled data) and the value which is in data that we want to study
is known as Target or Dependent Variable or Response Variable.
All the other columns in the dataset are known as the Feature or Predictor
Variable or Independent Variable.
Supervised Learning is classified into two categories:
1. Classification: Here our target variable consists of the categories.
2. Regression: Here our target variable is continuous and we usually try to find
out the line of the curve.
As we have understood that to carry out supervised learning we need labeled data.
How we can get labeled data? There are various ways to get labeled data:
1. Historical labeled Data
2. Experiment to get data: We can perform experiments to generate labeled data
like A/B Testing.
3. Crowd-sourcing
Now it’s time to understand algorithms that can be used to solve supervised machine
learning problem. In this post, we will be using popular scikit-learn package.
Note: It’s very important to have the right k-value when analyzing the dataset to
avoid overfitting and underfitting of the dataset.
OUTPUT:
Result:
k-nearest neighbor algorithm which is used to solve supervised machine learning
problems. We also explored measuring the accuracy of the model.
Using the k-nearest neighbor algorithm we fit the historical data (or train the
model) and predict the future.
We have a 3x3 board of where all numbers are in range 0 to 8 and no repeating
numbers are there. Now, we can swap the 0 with one of its 4 neighbors, and we are
trying to solve it to get all arranged sequence, we have to find minimum number of
steps required to reach the goal.
So, if the input is like
3 1 2
4 7 5
6 8 0
PSEUDOCODE
results = []
pos_0 = node.index(0)
for move in moves[pos_0]:
new_node = list(node)
new_node[move], new_node[pos_0] = new_node[pos_0], new_node[move]
results.append(tuple(new_node))
Input
Ouput
b) 8-QUEENS PROBLEM
The 8 queens problem is a problem in swhich we figure out a way to put 8 queens on
an 8×8 chessboard in such a way that no queen should attack the other. For basic
info about the queen in a chess game, you should know that a queen can move in any
direction ( vertically, horizontally, and diagonally) and to any number of places.
In the figure below you can see how to place 4 queens on a 4×4 chessboard.
What is Backtracking?
Backtracking the solution of the problem depends on the previous steps taken. We
take a step and then analyze it that whether it will give the correct answer or
not? and if not, then we move back and change the previous step.
##TAKING NUMBER OF QUEENS AS INPUT FROM USER
PROGRAM:
print ("Enter the number of queens") N = int(input())
# here we create a chessboard
# NxN matrix with all elements set to 0 board = [[0]*N for _ in range(N)]
def attack(i, j):
#checking vertically and horizontally for k in range(0,N):
if board[i][k]==1 or board[k][j]==1: return True
#checking diagonally for k in range(0,N):
for l in range(0,N):
if (k+l==i+j) or (k-l==i-j): if board[k][l]==1:
return True return False
def N_queens(n): if n==0:
return True
for i in range(0,N): for j in range(0,N):
if (not(attack(i,j))) and (board[i][j]!=1): board[i][j] = 1
if N_queens(n-1)==True: return True
board[i][j] = 0 return False
N_queens(N) for i in board: print (i)
Result:
Output
[1, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 1, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 1]
[0, 0, 0, 0, 0, 1, 0, 0]
[0, 0, 1, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 1, 0]
[0, 1, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 1, 0, 0, 0, 0]
global N N = 4
def printSolution(board):
for i in range(N):
for j in range(N):
print (board[i][j],end=' ')
print()
# A utility function to check if a queen can
# be placed on board[row][col]. Note that this # function is called when "col"
queens are
# already placed in columns from 0 to col -1. # So we need to check only left side
for
# attacking queens
def isSafe(board, row, col):
return True
# Consider this column and try placing # this queen in all rows one by one
for i in range(N):
if isSafe(board, i, col):
# Place this queen in board[i][col] board[i][col] = 1
# This function solves the N Queen problem using # Backtracking. It mainly uses
solveNQUtil() to
# solve the problem. It returns false if queens # cannot be placed, otherwise
return true and # placement of queens in the form of 1s.
# note that there may be more than one
# solutions, this function prints one of the # feasible solutions.
def solveNQ():
board = [ [0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
]
printSolution(board)
return True
# Stores the string formed # by concatenating every # occured character only # once
uniq = ""
# Update Hash[ch-'A]
Hash[ord(ch) - ord('A')] += pow(10, len(words[word]) - i - 1)
# If mp[ch-'A'] is -1
if mp[ord(ch) - ord('A')] == -1:
mp[ord(ch) - ord('A')] = 0 uniq += str(ch)
# If val is -1
if val != -1:
# Recursion
return solve(words, i + 1, S + val * Hash[ord(ch) - ord('A')], mp, used, Hash,
CharAtfront)
# If used[l] is true
if used[l] == 1:
continue
# Backtrack
mp[ord(ch) - ord('A')] = -1
# Function Call
if isSolvable(arr, S): print("Yes")
else:
print("No")
OUTPUT
Yes
TIME COMPLEXITY: O(N*M+10!), where M is the length of the largest string.
RESULT:
Hence 8-PUZZLE,8-QUEENS, and CRYPTARITHMETIC problem has been solved using python
Explanation:
play_game() is the main function, which performs following tasks :
• Calls create_board() to create a 9×9 board and initializes with 0.
• For each player (1 or 2), calls the random_place() function to randomly
choose a location on board and mark that location with the player number,
alternatively.
• Print the board after each move.
• Evaluate the board after each move to check whether a row or column or a
diagonal has the same player number. If so, displays the winner name. If after 9
moves, there are no winner then displays -1.
PROGRAM:
for i in range(len(board)):
for j in range(len(board)):
if board[i][j] == 0:
l.append((i, j))
return(l)
# Checks whether the player has three # of their marks in a horizontal row def
row_win(board, player):
for x in range(len(board)): win = True
for y in range(len(board)):
if board[x, y] != player: win = False continue
if win == True:
return(win) return(win)
# Checks whether the player has three # of their marks in a vertical row
def col_win(board, player):
for x in range(len(board)): win = True
for y in range(len(board)):
if board[y][x] != player: win = False
continue if win == True:
return(win) return(win)
# Checks whether the player has three # of their marks in a diagonal row
def diag_win(board, player): win = True
y = 0
for x in range(len(board)):
if board[x, x] != player: win = False
if win:
return win win = True
if win:
for x in range(len(board)): y = len(board) - 1 - x
if board[x, y] != player: win = False
return win
# Evaluates whether there is # a winner or a tie
def evaluate(board): winner = 0
Winner is: 2
SU DO KU
M = 9
def puzzle(a):
for i in range(M):
for j in range(M):
print(a[i][j],end = " ")
print()
def solve(grid, row, col, num):
for x in range(9):
if grid[row][x] == num:
return False
for x in range(9):
if grid[x][col] == num:
return False
grid[row][col] = num
if Suduko(grid, row, col + 1):
return True
grid[row][col] = 0
return False
if (Suduko(grid, 0, 0)):
puzzle(grid)
else:
print("Solution does not exist:(")
OUTPUT:
2 5 8 7 3 6 9 4 1
6 1 9 8 2 4 3 5 7
4 3 7 9 1 5 2 6 8
3 9 5 2 7 1 4 8 6
7 6 2 4 9 8 1 3 5
8 4 1 6 5 3 7 2 9
1 8 4 3 6 9 5 7 2
5 7 6 1 4 2 8 9 3
9 2 3 5 8 7 6 1 4