Hill climbing algorithm
Hill climbing is a mathematical
optimization algorithm, which means its purpose is to find the best solution to a problem which has a
(large) number of possible solutions. Explaining the algorithm
(and optimization in general) is best done using an example.Hill
climbing tries to find the best solution to this problem by starting out
with a random solution, and then generate neighbours: solutions that only
slightly differ from the current one. If the best of those neighbours is better
(i.e. shorter) than the current one, it replaces the current solution with this
better solution. It then repeats the pattern by again creating neighbours. If
at some point no neighbour is better than the current solution, it returns the
then current solution.
In this blog we will
optimize Hill climbing algorithm with Random Re Start Varient. My self Happy khatun. I
am a student of city university. And this blog is a part of our AI (Artificial
Intilegent) Lab Course conducted by our most honourable teacher Nuruzzaman
Faruqui.
Now we will solve the
following problem
In
this problem, there are hospitals and houses in different places. We have to
find the best cost path between the hospital and the house. When the cost will
less than the previous then it will be the best cost. In order to finding this
we need to implement following code :
import random # pseudo-random number generatorsAfter running this code we get the following result
class Space():
def __init__(self, height, width, num_hospitals):
"""Create a new state space with given dimensions."""
self.height = height
self.width = width
self.num_hospitals = num_hospitals
self.houses = set()
self.hospitals = set()
def add_house(self, row, col):
"""Add a house at a particular location in state space."""
self.houses.add((row, col))
def available_spaces(self):
"""Returns all cells not currently used by a house or hospital."""
# Consider all possible cells
candidates = set(
(row, col)
for row in range(self.height)
for col in range(self.width)
)
# Remove all houses and hospitals
for house in self.houses:
candidates.remove(house)
for hospital in self.hospitals:
candidates.remove(hospital)
return candidates
def hill_climb(self, maximum=None, image_prefix=None, log=False):
"""Performs hill-climbing to find a solution."""
count = 0
# Start by initializing hospitals randomly
self.hospitals = set()
for i in range(self.num_hospitals):
self.hospitals.add(random.choice(list(self.available_spaces())))
if log:
print("Initial state: cost", self.get_cost(self.hospitals))
if image_prefix:
self.output_image(f"{image_prefix}{str(count).zfill(3)}.png")
'''zfill() method adds zeros (0) at the beginning of the string'''
# Continue until we reach maximum number of iterations
while maximum is None or count < maximum:
count += 1
best_neighbors = []
best_neighbor_cost = None
# Consider all hospitals to move
for hospital in self.hospitals:
# Consider all neighbors for that hospital
for replacement in self.get_neighbors(*hospital):
# Generate a neighboring set of hospitals
neighbor = self.hospitals.copy() # Slide 28
neighbor.remove(hospital) # slide 29
neighbor.add(replacement) # Slide 30
# Check if neighbor is best so far
cost = self.get_cost(neighbor)
if best_neighbor_cost is None or cost < best_neighbor_cost:
best_neighbor_cost = cost
best_neighbors = [neighbor]
elif best_neighbor_cost == cost:
best_neighbors.append(neighbor)
# None of the neighbors are better than the current state
if best_neighbor_cost >= self.get_cost(self.hospitals):
return self.hospitals
# Move to a highest-valued neighbor
else:
if log:
print(f"Found better neighbor: cost {best_neighbor_cost}")
self.hospitals = random.choice(best_neighbors)
# Generate image
if image_prefix:
self.output_image(f"{image_prefix}{str(count).zfill(3)}.png")
def random_restart(self, maximum, image_prefix=None, log=False):
"""Repeats hill-climbing multiple times."""
best_hospitals = None
best_cost = None
# Repeat hill-climbing a fixed number of times
for i in range(maximum):
hospitals = self.hill_climb()
cost = self.get_cost(hospitals)
if best_cost is None or cost < best_cost:
best_cost = cost
best_hospitals = hospitals
if log:
print(f"{i}: Found new best state: cost {cost}")
else:
if log:
print(f"{i}: Found state: cost {cost}")
if image_prefix:
self.output_image(f"{image_prefix}{str(i).zfill(3)}.png")
return best_hospitals
def get_cost(self, hospitals):
"""Calculates sum of distances from houses to nearest hospital."""
cost = 0
for house in self.houses:
cost += min(
abs(house[0] - hospital[0]) + abs(house[1] - hospital[1])
for hospital in hospitals
)
return cost
def get_neighbors(self, row, col):
"""Returns neighbors not already containing a house or hospital."""
candidates = [
(row - 1, col),
(row + 1, col),
(row, col - 1),
(row, col + 1)
]
neighbors = []
for r, c in candidates:
if (r, c) in self.houses or (r, c) in self.hospitals:
continue
if 0 <= r < self.height and 0 <= c < self.width:
neighbors.append((r, c))
return neighbors
def output_image(self, filename):
"""Generates image with all houses and hospitals."""
from PIL import Image, ImageDraw, ImageFont
cell_size = 100
cell_border = 2
cost_size = 40
padding = 10
# Create a blank canvas
img = Image.new(
"RGBA",
(self.width * cell_size,
self.height * cell_size + cost_size + padding * 2),
"white"
)
house = Image.open("assets/images/House.png").resize(
(cell_size, cell_size)
)
hospital = Image.open("assets/images/Hospital.png").resize(
(cell_size, cell_size)
)
font = ImageFont.truetype("assets/fonts/OpenSans-Regular.ttf", 30)
draw = ImageDraw.Draw(img)
for i in range(self.height):
for j in range(self.width):
# Draw cell
rect = [
(j * cell_size + cell_border,
i * cell_size + cell_border),
((j + 1) * cell_size - cell_border,
(i + 1) * cell_size - cell_border)
]
draw.rectangle(rect, fill="black")
if (i, j) in self.houses:
img.paste(house, rect[0], house)
if (i, j) in self.hospitals:
img.paste(hospital, rect[0], hospital)
# Add cost
draw.rectangle(
(0, self.height * cell_size, self.width * cell_size,
self.height * cell_size + cost_size + padding * 2),
"black"
)
draw.text(
(padding, self.height * cell_size + padding),
f"Cost: {self.get_cost(self.hospitals)}",
fill="white",
font=font
)
img.save(filename)
# Create a new space and add houses randomly
s = Space(height=10, width=20, num_hospitals=3)
for i in range(15):
s.add_house(random.randrange(s.height), random.randrange(s.width))
# Use local search to determine hospital placement
hospitals = s.hill-climb(image_prefix="hospitals", log=True)
For Hill climbing to work, it has to start with a random solution to our Travelling salesman problem. From there, it can generate neighbouring solutions and start the optimization process.
In this blog we learned how to implement Hill Climbing Algorithm & Random Restart variants to optimize solutions in a easiest way.


No comments:
Post a Comment