4 AI Lab Programs
4 AI Lab Programs
Depth First Search Description: Depth First Search (DFS) algorithm traverses a graph in a depth-ward motion and
uses a stack to remember to get the next vertex to start a search, when a dead end occurs in any iteration.
Algorithm:
Pseudo Algorithm:
DFS(G, u)
u.visited = true
for each v ∈ G.Adj[u]
if v.visited == false
DFS(G,v)
init() {
For each u ∈ G
u.visited = false
For each u ∈ G
DFS(G, u)
}
Program:
# Driver Code
print("Following is the Depth-First Search")
dfs(visited, graph, '5')
1
2
Input: Output:
532487
2
3
Algorithm:
The steps of the algorithm work as follow:
1. Start by putting any one of the graph’s vertices at the back of the queue.
2. Now take the front item of the queue and add it to the visited list.
3. Create a list of that vertex's adjacent nodes. Add those which are not within the visited list to the rear of
the queue.
4. Keep continuing steps two and three till the queue is empty.
Pseudo Algorithm:
create a queue Q
mark v as visited and put v into Q
while Q is non-empty
remove the head u of Q
mark and enqueue all (unvisited) neighbors of u
Program:
graph = {
'5' : ['3','7'],
'3' : ['2', '4'],
'7' : ['8'],
'2' : [],
'4' : ['8'],
'8' : []
}
# Driver Code
print("Following is the Breadth-First Search")
bfs(visited, graph, '5') # function calling
3
4
Input: Output:
537248
4
5
Algorithm:
1: Firstly, Place the starting node into OPEN and find its f (n) value.
2: Then remove the node from OPEN, having the smallest f (n) value. If it is a goal node, then stop and return to
success.
3: Else remove the node from OPEN, and find all its successors.
4: Find the f (n) value of all the successors, place them into OPEN, and place the removed node into CLOSE.
5: Goto Step-2.
6: Exit.
Pseudo code:
let openList equal empty list of nodes
let closedList equal empty list of nodes
put startNode on the openList (leave it's f at zero)
while openList is not empty
let currentNode equal the node with the least f value
remove currentNode from the openList
add currentNode to the closedList
if currentNode is the goal
You've found the exit!
let children of the currentNode equal the adjacent nodes
for each child in the children
if child is in the closedList
continue to beginning of for loop
child.g = currentNode.g + distance b/w child and current
child.h = distance from child to end
child.f = child.g + child.h
if child.position is in the openList's nodes positions
if child.g is higher than the openList node's g
continue to beginning of for loop
add the child to the openList
5
6
Program:
class Graph:
def __init__(self, adjac_lis):
self.adjac_lis = adjac_lis
# This is heuristic function which is having equal values for all nodes
def h(self, n):
H={
'A': 1,
'B': 1,
'C': 1,
'D': 1
}
return H[n]
if n == None:
print('Path does not exist!')
return None
6
7
while par[n] != n:
reconst_path.append(n)
n = par[n]
reconst_path.append(start)
reconst_path.reverse()
if m in closed_lst:
closed_lst.remove(m)
open_lst.add(m)
Input: Output:
adjac_lis = { Path found: ['A', 'B', 'D']
'A': [('B', 1), ('C', 3), ('D', 7)], ['A', 'B', 'D']
'B': [('D', 5)],
'C': [('D', 12)]
}
graph1 = Graph(adjac_lis)
graph1.a_star_algorithm('A', 'D')
7
8
Description:
The hill-climbing algorithm is a local search algorithm used in mathematical optimization. An important property
of local search algorithms is that the path to the goal does not matter, only the goal itself matters. Because of this,
we do not need to worry about which path we took in order to reach a certain goal state, all that matters is that
we reached it.
The basic principle behind the algorithm is moving across neighboring states according to elevation or increase in
value. This working principle also causes the algorithm to be susceptible to local maximums.
HillClimbing(problem) {
currentState = problem.startState
goal = false
while(!goal){
neighbour = highest valued successor of currentState
if neighbour.value <= currentState.value
goal = true
else
currentState = neighbour
}
}
Explanation:
1. We begin with a starting state that we assign to the currentState variable. Following that, we proceed to
perform a loop until we reach our goal state.
2. The current objective function of our algorithm is to find the maximum valued state or, in simpler terms, a
‘peak’.
3. In order to proceed, we first find out the immediate neighbors of our current state. The way this is done is left
up to the reader since the data organization of the problem may vary.
4. After we have found the neighbors, we make the highest valued neighbor and compare it with currentState.
5. If the neighbor’s value is higher than our current state, we move to the neighboring state; else, we end the
loop (since according to the algorithm we have found our peak).
Program:
import numpy as np
# left neighbour
if state[0] != 0:
neighbours.append((state[0] - 1, state[1]))
# right neighbour
if state[0] != dim[0] - 1:
neighbours.append((state[0] + 1, state[1]))
8
9
# top neighbour
if state[1] != 0:
neighbours.append((state[0], state[1] - 1))
# bottom neighbour
if state[1] != dim[1] - 1:
neighbours.append((state[0], state[1] + 1))
# top left
if state[0] != 0 and state[1] != 0:
neighbours.append((state[0] - 1, state[1] - 1))
# bottom left
if state[0] != 0 and state[1] != dim[1] - 1:
neighbours.append((state[0] - 1, state[1] + 1))
# top right
if state[0] != dim[0] - 1 and state[1] != 0:
neighbours.append((state[0] + 1, state[1] - 1))
# bottom right
if state[0] != dim[0] - 1 and state[1] != dim[1] - 1:
neighbours.append((state[0] + 1, state[1] + 1))
return neighbours
def __main__():
landscape = np.random.randint(1, high=50, size=(10, 10))
print(landscape)
start_state = (3, 6) # matrix index coordinates
current_state = start_state
count = 1
ascending = True
while ascending:
print("\nStep #", count)
print("Current state coordinates: ", current_state)
print("Current state value: ", landscape[current_state[0]][current_state[1]])
count += 1
9
10
__main__()
Example:
10
11
11
12
Problem description:
The Tower of Hanoi, is a mathematical problem which consists of three rods and multiple disks. Initially, all the
disks are placed on one rod, one over the other in ascending order of size similar to a cone
cone-shaped
shaped tower.
The objective of this problem is to move the stack of disks from the initial rod to another rod, following these rules:
A disk cannot be placed on top of a smaller disk
No disk isk can be placed on top of the smaller disk.
The goal is to move all the disks from the leftmost rod to the rightmost rod. To move N disks from one rod to
another, 2^𝑁−1−1 steps are required. So, to move 3 disks from star ng the rod to the ending rod, a total
t of 7 steps
are required
Example
This example runs for 3 disks and 3 rods as described in the diagram above. It displays all the steps it follows to
take the stack of disks from start to end.
Note: An Aux is the rod helping the movement of the disk. This rod contains the disks which are not to be moved
in the current function call.
Initially, aux rod is set as middle tower.
Program:
#include <iostream>
#include <string>
using namespace std;
12
13
cout << "Move disk 1 from rod " << from_tower << " to rod " << to_tower<<endl;
return;
}
TowerOfHanoi(n - 1, from_tower, aux_tower, to_tower);
cout << "Move disk " << n << " from rod " << from_tower << " to rod " << to_tower << endl;
TowerOfHanoi(n - 1, aux_tower, to_tower, from_tower);
}
int main()
{
int n = 3; // Number of disks
TowerOfHanoi(n, "Start", "End", "Mid"); //names of the towers
return 0;
}
Output:
Move disk 1 from rod Start to rod End
13
14
Problem description: Travelling Salesman Problem (TSP) : Given a set of cities and distances between every pair of
cities, the problem is to find the shortest possible route that visits every city exactly once and returns to the
starting point.
Algorithm:
Naive Solution:
1) Consider city 1 as the starting and ending point.
2) Generate all (n-1)! Permutations of cities.
3) Calculate cost of every permutation and keep track of minimum cost permutation.
4) Return
n the permutation with minimum cost.
Time Complexity: Θ(n!)
Program:
14
15
# update minimum
min_path = min(min_path, current_pathweight)
return min_path
# Driver Code
if __name__ == "__main__":
15
16
What is Annealing?
In simple terms, ‘Annealing’ is a technique, where a metal is heated to a high temperature and slowly cooled
down to improve its physical properties. When the metal is hot, the molecules randomly re-arrange
themselves at a rapid pace.
As the metal starts to cool down, the re-arranging process occurs at a much slower rate. In the end, the
resultant metal will be a desired workable metal. The factors of time and metal’s energy at a particular time
will supervise the entire process.
In machine learning, Simulated annealing algorithm mimics this process and is used to find optimal (or most
predictive) features in the feature selection process.
Let’s now try to draw parallel’s between Annealing in metallurgy and Simulated annealing for Feature
selection:
16
17
2. If the Random Number < Acceptance Probability then the new feature set is Accepted.
Accepted
The impact of randomness by this process helps simulated annealing to not get stuck at local optimums in
search of a global optimum.
Keep doing this for the chosen number of iterations.
Now, how is all of this related to ‘annealing’ concept of cooling temperature? you might wonder.
wonde
The ‘acceptance probability’ takes care of that. The formula for acceptance probability is designed in such a
way that, as the number of iterations increase, the probability of accepting bad performance comes down. As
a result, fewer changes are accepted.
Let’s look at the formula now.
Where,
i = No. Of Iterations,
c = Controls the amount of perturbation that can happ
happen,
old = Old score,
new = New score.
The acceptance probability can be understood as a function of time and change in performance with a
constant ‘c’, which is used to control the rate of perturbation happening in the features. Usually, ‘c’ is set to
be 1.
iteration i = 5 and c = 1
As you can see after 10 iterations the acceptance probability came down to 0.0055453. Thus, as the no. of
iterations increases, the chances of accepting a worse solution decreases.
Now,
ow, Why does simulated annealing accept worse performing feature sets ?
If the algorithm tends to accept only the best performing feature sets the probability of getting stuck in the
local optima gets very high which is not good. Even if the algorithm is going to continuously face poor-
poor
performing feature sets for a certain number of times it allows for better chances of finding the global optima
which may exist elsewhere. As the acceptance probability decreases with time (iterations), it tends to go back
to
o the last known local optimum and starts its search for global optimum once again. So the chances of
settling on a worser performing results is diminished.
17
18
When the temperature is high the chances of worse-performing features getting accepted is high and as the
no. of iterations goes up, temperature decreases, and that in turn decreases the chances of worse-
performing features getting accepted.
The intent here is that, when the temperature is high, the algorithm moves freely in the search space, and as
temperature decreases the algorithm is forced to converge at global optima.
Just an overview,
In this code, the steps taken by ‘Mia’ will be random and not user-fed values. Each time there is an
improvement/betterment in the steps taken towards global optimum, those values alongside the previous
value get saved into a list called outputs.
The initial step is to import necessary libraries.
Let’s define the objective function to evaluate the steps taken by mia.
Now that the objective function is defined. ‘Mia’ needs to start the search hunt from some point right ?. Only
if she has a start point she can progress towards the global optimum.
The below code cell gives us a random start point between the range of the area of the search space. Let’s
also see the evaluation of this start_point.
seed(1)
area = asarray([[-6.0, 6.0]])
18
19
# area[:,0] = -6.0, area[:,1] = 6.0, ran(len(area))generates random number within the length of area.
# length of interval is 1.
Seems like the new point obtained( objective function evaluated point ) is better than the start_point.
seed(1) is a Pseudorandom_number_generator.
By using seed(1) same random numbers will get generated each time the code cell is run,
Let’s now define the simulated annealing algorithm as a function.
he parameters needed are:
1. Objective function.
2. Area of the search space.
3. No. of iterations.
4. step_size.
5. Temperature.
After defining the function, the start_point is initialized then, this start_point is getting evaluated by
the objective function and that is stored into start_point_eval
def sa(objective, area = ([[-6.0,6.0]]), n_iterations = 1200, step_size = 0.1, temperature = 12):
# Generating a random start point for the search hunt
start_point = area[:, 0] + rand( len( area ) ) * ( area[:, 1] - area[:, 0] )
# Evaluating the start_point
start_point_eval = objective(start_point)
Now start_point and objective function evaluation of start point(start_point_eval) needs to be stored so that
each time an improvement happens, the progress can be seen.
# Storing the start point and its objective function evaluation into mia_start_point and mia_start_eval.
mia_start_point, mia_start_eval = start_point, start_point_eval
# this empty list will get updated over time once looping starts.
outputs = []
‘Mia’ start point and her start point evaluation are stored into mia_start_point and mia_start_eval. Outputs is
an empty list that will get updated over time once looping starts. As of now, ‘Mia’ started at a point and
evaluated that point. Now she has to take her first step towards her search hunt and to do so, a for loop is
defined ranging from 0 to the iteration number we specify.
The first step will be in accordance with Gaussian distribution where the mean is the current point and
standard deviation is defined by the step_size. In a nutshell, this means the steps taken will be 3
* step_size of the current point.
19
20
This new point obtained must be checked whether it is better than the current point, if it is better, then
replace the current point with the new point. Then append those new points into our outputs list. If the new
point is better:
# The new step is checked whether it is better than current step. If better, the current point is replaced with new
point.
if mia_step_eval < start_point_eval:
start_point, start_point_eval = mia_step, mia_step_eval
# Step gets appended into the list
outputs.append(start_point_eval)
# printing out the iteration number, best_so_far and new_best
print('iteration Number = ',i," ", 'best_so_far = ',start_point," " ,'new_best = ',start_point_eval)
If the new point isn’t a promising solution, then the difference between the objective function evaluation of
the current solution(mia_step_eval) and current working solution(mia_start_eval) is calculated. One of the
popular ways of calculating temperature is by using the “Fast Simulated Annealing Method” which is as
follows:
difference gives us the difference between the old point and the new point so that the acceptance
probability/metropolis acceptance criterion can be calculated. This helps in calculating the probability of
accepting a point with worse performance than the current point.
Then a random number is generated using rand() and if the Random Number > Acceptance Probability then
the new point will be Rejected and if Random Number < Acceptance Probability then the new point will
be Accepted.
The last step is to pass values to the parameters of the simulated annealing function.
seed(1)
# define the area of the search space
area = asarray([[-6.0, 6.0]])
20
21
# initial temperature
temperature = 12
# define the total no. of iterations
iterations = 1200
# define maximum step_size
step_size = 0.1
# perform the simulated annealing search
start_point, output, outputs = sa(objective, area, n_iterations, step_size, temp)
Program:
from numpy import asarray, exp
from numpy.random import randn, rand, seed
from matplotlib import pyplot
seed(1)
# define the area of the search space
area = asarray([[-6.0, 6.0]])
# initial temperature
temperature = 12
# define the total no. of iterations
21
22
iterations = 1200
# define maximum step_size
step_size = 0.1
# perform the simulated annealing search
start_point, output, outputs = sa(objective, area, iterations, step_size, temperature)
#plotting the values
pyplot.plot(outputs, 'ro-')
pyplot.xlabel('Improvement Value')
pyplot.ylabel('Evaluation of Objective Function')
pyplot.show()
22
23
Problem Description:
Given a 3×3 board with 8 tiles (every tile has one number from 1 to 8) and one empty space. The objective is to
place the numbers on tiles to match the final configuration using the empty space. We can slide four adjacent
(left, right, above, and below) tiles into the empty space.
1. DFS (Brute-Force)
We can perform a depth-first search on state-space (Set of all configurations of a given problem i.e. all states
that can be reached from the initial state) tree.
23
24
Complete Algorithm:
/* Algorithm LCSearch uses c(x) to find an answer node
* LCSearch uses Least() and Add() to maintain the list
of live nodes
* Least() finds a live node with least c(x), deletes
it from the list and returns it
* Add(x) adds x to the list of live nodes
* Implement list of live nodes as a min-heap */
struct list_node
{
list_node *next;
E = t; // E-node
24
25
The below diagram shows the path followed by the above algorithm to reach the final configuration from the
given initial configuration of the 8-Puzzle. Note that only nodes having the least value of cost function are
expanded.
Program:
# Python3 program to print the path from root
# node to destination node for N*N-1 puzzle
# algorithm using Branch and Bound
# The solution assumes that instance of
# puzzle is solvable
25
26
class priorityQueue:
# Constructor to initialize a
# Priority Queue
def __init__(self):
self.heap = []
# Node structure
class node:
26
27
count = 0
for i in range(n):
for j in range(n):
if ((mat[i][j]) and
(mat[i][j] != final[i][j])):
count += 1
return count
for i in range(n):
for j in range(n):
print("%d " % (mat[i][j]), end = " ")
print()
if root == None:
27
28
return
printPath(root.parent)
printMatrix(root.mat)
print()
if isSafe(new_tile_pos[0], new_tile_pos[1]):
28
29
new_tile_pos,
minimum.level + 1,
minimum, final,)
# Driver Code
# Initial configuration
# Value 0 is used for empty space
initial = [ [ 1, 2, 3 ],
[ 5, 6, 0 ],
[ 7, 8, 4 ] ]
29
30
Wumpus world:
The Wumpus world is a simple world example to illustrate the worth of a knowledge-based agent and to represent
knowledge representation. It was inspired by a video game Hunt the Wumpus by Gregory Yob in 1973.
The Wumpus world is a cave which has 4/4 rooms connected with passageways. So there are total 16 rooms which
are connected with each other. We have a knowledge-based agent who will go forward in this world. The cave has
a room with a beast which is called Wumpus, who eats anyone who enters the room. The Wumpus can be shot by
the agent, but the agent has a single arrow. In the Wumpus world, there are some Pits rooms which are
bottomless, and if agent falls in Pits, then he will be stuck there forever. The exciting thing with this cave is that in
one room there is a possibility of finding a heap of gold. So the agent goal is to find the gold and climb out the cave
without fallen into Pits or eaten by Wumpus. The agent will get a reward if he comes out with gold, and he will get
a penalty if eaten by Wumpus or falls in the pit.
Following is a sample diagram for representing the Wumpus world. It is showing some rooms with Pits, one room
with Wumpus and one agent at (1, 1) square location of the world.
There are also some components which can help the agent to navigate the cave. These components are given as
follows:
a. The rooms adjacent to the Wumpus room are smelly, so that it would have some stench. The room
adjacent to PITs has a breeze, so if the agent reaches near to PIT, then he will perceive the breeze.
b. There will be glitter in the room if and only if the room has gold.
c. The Wumpus can be killed by the agent if the agent is facing to it, and Wumpus will emit a horrible
scream which can be heard anywhere in the cave.
30
31
Performance measure:
o +1000 reward points if the agent comes out of the cave with the gold.
o -1000 points penalty for being eaten by the Wumpus or falling into the pit.
o -1 for each action, and -10 for using an arrow.
o The game ends if either agent dies or came out of the cave.
Environment:
o A 4*4 grid of rooms.
o The agent initially in room square [1, 1], facing toward the right.
o Location of Wumpus and gold are chosen randomly except the first square [1,1].
o Each square of the cave can be a pit with probability 0.2 except the first square.
Actuators:
o Left turn,
o Right turn
o Move forward
o Grab
o Release
o Shoot.
Sensors:
o The agent will perceive the stench if he is in the room adjacent to the Wumpus. (Not diagonally).
o The agent will perceive breeze if he is in the room directly adjacent to the Pit.
o The agent will perceive the glitter in the room where the gold is present.
o The agent will perceive the bump if he walks into a wall.
o When the Wumpus is shot, it emits a horrible scream which can be perceived anywhere in the cave.
o These percepts can be represented as five element list, in which we will have different indicators for each
sensor.
o Example if agent perceives stench, breeze, but no glitter, no bump, and no scream then it can be
represented as:
[Stench, Breeze, None, None, None].
The Wumpus world Properties:
o Partially observable: The Wumpus world is partially observable because the agent can only perceive the
close environment such as an adjacent room.
o Deterministic: It is deterministic, as the result and outcome of the world are already known.
o Sequential: The order is important, so it is sequential.
o Static: It is static as Wumpus and Pits are not moving.
o Discrete: The environment is discrete.
o One agent: The environment is a single agent as we have one agent only and Wumpus is not considered
as an agent.
Exploring the Wumpus world:
Now we will explore the Wumpus world and will determine how the agent will find its goal by applying logical
reasoning.
31
32
32
33
33
34
34
35
11. Build a bot which provides all the information related to your college.
Let us have a quick glance at Python’s ChatterBot to create our bot. ChatterBot is a Python library built based
on machine learning with an inbuilt conversational dialog flow and training engine. The bot created using this
library will get trained automatically with the response it gets from the user.
A simple implementation:
Installation
chatbot=ChatBot('corona bot')
35
36
12. Build A Virtual Assistant For Wikipedia Using Wolfram Alpha And Python
Problem Description: The Wolfram|Alpha Webservice API provides a web-based based API allowing the computational
and presentation capabilities of Wolfram|Alpha to be integrated into web, mobile, desktop, and enterprise
applications. Wolfram Alpha is an API which can compute expert
expert-level
level answers using Wolfram’s algorithms,
knowledgebase and AI technology.
ology. It is made possible by the Wolfram Language. This article tells how to create
a simple assistant application in Python which can answer simple questions like the ones listed below.
3. Now you will see the homepage of the website. Head to the section in the top right corner where you see
your email. In
n the drop down menu, select the My Apps (API) option.
5. In the next dialog box, give the app a suitable name and description.
36
37
6. Note down the APPID that appears in the next dialog box. This app id will be specif
specific
ic to the application.
Implementation:
Make sure that wolframalpha python package is installed beforehand. It can be done by running the following
command in the terminal or cmd –
pip install wolframalpha
# Python program to
# demonstrate creation of an
# assistant using wolf ram API
import wolframalpha
37
38
res = client.query(question)
print(answer)
Output:
38
39
13. The following is a function that counts the number of times a string occurs in another string: # Count the
number of times string s1 is found in string s2 def countsubstring(s1,s2): count = 0 for i in range(0,len(s2)-
len(s1)+1): if s1 == s2[i:i+len(s1)]: count += 1 return count For instance, countsubstring(’ab’,’cabalaba’) returns 2.
Write a recursive version of the above function. To get the rest of a string (i.e. everything but the first
character).
# Driver code
a = "GeeksforGeeks"
b = "Gks"
print(count(a, b, len(a),len(b)))
39
40
14. Higher order functions. Write a higher-order function count that counts the number of elements in a list that
satisfy a given test. For instance: count(lambda x: x>2, [1,2,3,4,5]) should return 3, as there are three elements in
the list larger than 2. Solve this task without using any existing higher-order function.
Output:
Count of odd numbers in a list : 6
Output:
Count of even numbers in a list : 3
Output:
Count of numbers in a list which are greater than 5: 9
Count numbers in a list which are greater than 5 but less than 20:
listOfElems = [11, 22, 33, 45, 66, 77, 88, 99, 101]
# count numbers in the list which are greater than 5 but less than 20
count = getCount(listOfElems, lambda x : x>5 and x < 20)
print('Count of numbers in a list which are greater than 5 but less than 20 : ', count)
Output:
Count of numbers in a list which are greater than 5 but less than 20 : 1
Output
40
41
41
42
Output:
**** Use map() & sum() to count elements in a list that satisfy certain conditions ****
** Example 1 **
Count of odd numbers in a list : 6
** Example 1 : Explanation **
Contents of map object : [True, False, True, True, False, True, False, True, True]
** Example 2**
Count of even numbers in a list : 3
** Example 3**
Count of numbers in a list which are greater than 5: 9
**** Using sum() & Generator expression to count elements in list based on conditions ****
Count of numbers in a list which are greater than 5: 9
Count of numbers in a list which are greater than 5 but less than 20 : 1
Total Number of elements in List: 9
**** Use List comprehension to count elements in list based on conditions ****
Count of numbers in a list which are greater than 5: 9
**** Use reduce() function to count elements in list based on conditions ****
Count of numbers in a list which are greater than 5: 9
42
43
15. Brute force solution to the Knapsack problem. Write a function that allows you to generate random problem
instances for the knapsack program. This function should generate a list of items containing N items that each
have a unique name, a random size in the range 1 ....... 5 and a random value in the range 1 ..... 10.
Next, you should perform performance measurements to see how long the given knapsack solver take to solve
different problem sizes. You should peform atleast 10 runs with different randomly generated problem
instances for the problem sizes 10,12,14,16,18,20 and 22. Use a backpack size of 2:5 x N for each value problem
size N. Please note that the method used to generate random numbers can also affect performance, since
different distributions of values can make the initial conditions of the problem slightly more or less demanding.
How much longer time does it take to run this program when we increase the number of items? Does the
backpack size affect the answer? Try running the above tests again with a backpack size of 1 x N and with 4:0 x
N.
Given weights and values of n items, we need to put these items in a knapsack of capacity W to get the maximum
total value in the knapsack.
In the 0-1 Knapsack problem, we are not allowed to break items. We either take the whole item or don’t take it.
Input:
Items as (value, weight) pairs
arr[] = {{60, 10}, {100, 20}, {120, 30}}
Knapsack Capacity, W = 50;
Output:
Maximum possible value = 240
by taking items of weight 10 and 20 kg and 2/3 fraction
of 30 kg. Hence total price will be 60+100+(2/3)(120) = 240
In Fractional Knapsack, we can break items for maximizing the total value of knapsack. This problem in which we
can break an item is also called the fractional knapsack problem.
Input :
Same as above
Output :
Maximum possible value = 240
By taking full items of 10 kg, 20 kg and
2/3rd of last item of 30 kg
A brute-force solution would be to try all possible subset with all different fraction but that will be too much time
taking.
An efficient solution is to use Greedy approach. The basic idea of the greedy approach is to calculate the ratio
value/weight for each item and sort the item on basis of this ratio. Then take the item with the highest ratio and
add them until we can’t add the next item as a whole and at the end add the next item as much as we can. Which
will always be the optimal solution to this problem.
A simple code with our own comparison function can be written as follows, please see sort function more closely,
the third argument to sort function is our comparison function which sorts the item according to value/weight
ratio in non-decreasing order.
43
44
After sorting we need to loop over these items and add them in our knapsack satisfying above-mentioned criteria.
class ItemValue:
# Greedy Approach
class FractionalKnapSack:
totalValue = 0
for i in iVal:
curWt = int(i.wt)
curVal = int(i.val)
if capacity - curWt >= 0:
capacity -= curWt
totalValue += curVal
else:
fraction = capacity / curWt
totalValue += curVal * fraction
capacity = int(capacity - (curWt * fraction))
break
return totalValue
# Driver Code
if __name__ == "__main__":
44
45
# Function call
maxValue = FractionalKnapSack.getMaxValue(wt, val, capacity)
print("Maximum value in Knapsack =", maxValue)
45
46
16. Assume that you are organising a party for N people and have been given a list L of people who, for social
reasons, should not sit at the same table. Furthermore, assume that you have C tables (that are infinitly large).
Write a function layout(N,C,L) that can give a table placement (ie. a number from 0 : : :C -1) for each guest such
that there will be no social mishaps.
or simplicity we assume that you have a unique number 0 ......N-1 for each guest and that the list of restrictions
is of the form [(X,Y), ...] denoting guests X, Y that are not allowed to sit together. Answer with a dictionary
mapping each guest into a table assignment, if there are no possible layouts of the guests you should answer
False.
Solution:
Input:
1. A 2D array graph[V][V] where V is the number of vertices in graph and graph[V][V] is an adjacency matrix
representation of the graph. A value graph[i][j] is 1 if there is a direct edge from i to j, otherwise
graph[i][j] is 0.
2. An integer m is the maximum number of colors that can be used.
Output:
An array color[V] that should have numbers from 1 to m. color[i] should represent the color assigned to the
ith vertex. The code should also return false if the graph cannot be colored with m colors.
Example:
Input:
graph = {0, 1, 1, 1},
{1, 0, 1, 0},
{1, 1, 0, 1},
{1, 0, 1, 0}
Output:
Solution Exists:
Following are the assigned colors
1 2 3 2
Explanation: By coloring the vertices
with following colors, adjacent
vertices does not have same colors
Input:
graph = {1, 1, 1, 1},
{1, 1, 1, 1},
{1, 1, 1, 1},
{1, 1, 1, 1}
Output: Solution does not exist.
46
47
Naive Approach: Generate all possible configurations of colors. Since each node can be coloured using any of
the m available colours, the total number of colour configurations possible are m^V.
After generating a configuration of colour, check if the adjacent vertices have the same colour or not. If the
conditions are met, print the combination and break the loop.
Algorithm:
1. Create a recursive function that takes current index, number of vertices and output color array.
2. If the current index is equal to number of vertices. Check if the output color configuration is safe, i.e
check if the adjacent vertices do not have same color. If the conditions are met, print the configuration
and break.
3. Assign a color to a vertex (1 to m).
4. For every assigned color recursively call the function with next index and number of vertices
5. If any recursive function returns true break the loop and returns true.
# if coloring is safe
if (isSafe(graph, color)):
47
48
# Driver code
if __name__ == '__main__':
48