Hill Climbing Algorithm



The algorithms discussed in the previous chapters run systematically. To achieve the goal, one or more previously explored paths toward the solution need to be stored to find the optimal solution.

For many problems, the path to the goal is irrelevant. For example, in N-Queens problem, we don't need to care about the final configuration of the queens as well as in which order the queens are added.

Hill Climbing

Hill Climbing is a technique to solve certain optimization problems. In this technique, we start with a sub-optimal solution and the solution is improved repeatedly until some condition is maximized.

Hill Climbing

The idea of starting with a sub-optimal solution is compared to starting from the base of the hill, improving the solution is compared to walking up the hill, and finally maximizing some condition is compared to reaching the top of the hill.

Hence, the hill climbing technique can be considered as the following phases −

  • Constructing a sub-optimal solution obeying the constraints of the problem
  • Improving the solution step-by-step
  • Improving the solution until no more improvement is possible

Hill Climbing technique is mainly used for solving computationally hard problems. It looks only at the current state and immediate future state. Hence, this technique is memory efficient as it does not maintain a search tree.

Algorithm

 
Evaluate the initial state. 
Loop until a solution is found or there are no new operators left to 
be applied: 
   - Select and apply a new operator 
   - Evaluate the new state: 
      goal -→ quit 
      better than current state -→ new current state 

Iterative Improvement

In iterative improvement method, the optimal solution is achieved by making progress towards an optimal solution in every iteration. However, this technique may encounter local maxima. In this situation, there is no nearby state for a better solution.

This problem can be avoided by different methods. One of these methods is simulated annealing.

Random Restart

This is another method of solving the problem of local optima. This technique conducts a series of searches. Every time, it starts from a randomly generated initial state. Hence, optima or nearly optimal solution can be obtained comparing the solutions of searches performed.

Problems of Hill Climbing Technique

Local Maxima

If the heuristic is not convex, Hill Climbing may converge to local maxima, instead of global maxima.

Ridges and Alleys

If the target function creates a narrow ridge, then the climber can only ascend the ridge or descend the alley by zig-zagging. In this scenario, the climber needs to take very small steps requiring more time to reach the goal.

Plateau

A plateau is encountered when the search space is flat or sufficiently flat that the value returned by the target function is indistinguishable from the value returned for nearby regions, due to the precision used by the machine to represent its value.

Complexity of Hill Climbing Technique

This technique does not suffer from space related issues, as it looks only at the current state. Previously explored paths are not stored.

For most of the problems in Random-restart Hill Climbing technique, an optimal solution can be achieved in polynomial time. However, for NP-Complete problems, computational time can be exponential based on the number of local maxima.

Applications of Hill Climbing Technique

Hill Climbing technique can be used to solve many problems, where the current state allows for an accurate evaluation function, such as Network-Flow, Travelling Salesman problem, 8-Queens problem, Integrated Circuit design, etc.

Hill Climbing is used in inductive learning methods too. This technique is used in robotics for coordination among multiple robots in a team. There are many other problems where this technique is used.

Example

This technique can be applied to solve the travelling salesman problem. First an initial solution is determined that visits all the cities exactly once. Hence, this initial solution is not optimal in most of the cases. Even this solution can be very poor. The Hill Climbing algorithm starts with such an initial solution and makes improvements to it in an iterative way. Eventually, a much shorter route is likely to be obtained.

Following are the implementations of the above approach in various programming languages −

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define NUM_CITIES 4
// Distance matrix representing distances between cities
// Replace this with the actual distance matrix for your problem
int distance_matrix[NUM_CITIES][NUM_CITIES] = {
   {0, 10, 15, 20},
   {10, 0, 35, 25},
   {15, 35, 0, 30},
   {20, 25, 30, 0}
};
int total_distance(int* path, int num_cities) {
   // Calculate the total distance traveled in the given path
   int total = 0;
   for (int i = 0; i < num_cities - 1; i++) {
       total += distance_matrix[path[i]][path[i + 1]];
   }
   total += distance_matrix[path[num_cities - 1]][path[0]]; // Return to starting city
   return total;
}
void hill_climbing_tsp(int num_cities, int max_iterations) {
   int current_path[NUM_CITIES]; // Initial solution, visiting cities in order
   for (int i = 0; i < num_cities; i++) {
      current_path[i] = i;
   }
   int current_distance = total_distance(current_path, num_cities);
   for (int it = 0; it < max_iterations; it++) {
      // Generate a neighboring solution by swapping two random cities
      int neighbor_path[NUM_CITIES];
      for (int i = 0; i < num_cities; i++) {
         neighbor_path[i] = current_path[i];
      }
      int i = rand() % num_cities;
      int j = rand() % num_cities;
      int temp = neighbor_path[i];
      neighbor_path[i] = neighbor_path[j];
      neighbor_path[j] = temp;
      int neighbor_distance = total_distance(neighbor_path, num_cities);
      // If the neighbor solution is better, move to it
      if (neighbor_distance < current_distance) {
         for (int i = 0; i < num_cities; i++) {
            current_path[i] = neighbor_path[i];
         }
         current_distance = neighbor_distance;
      }
   }
   printf("Optimal path: ");
   for (int i = 0; i < num_cities; i++) {
      printf("%d ", current_path[i]);
   }
   printf("\nTotal distance: %d\n", current_distance);
}
int main() {
   srand(time(NULL));
   int max_iterations = 10000;
   hill_climbing_tsp(NUM_CITIES, max_iterations);
   return 0;
}

Output

Optimal path: 1 0 2 3 
Total distance: 80
#include <iostream>
#include <vector>
#include <algorithm>
#include <ctime>
#include <cstdlib>
using namespace std;
#define NUM_CITIES 4
// Distance matrix representing distances between cities
// Replace this with the actual distance matrix for your problem
int distance_matrix[NUM_CITIES][NUM_CITIES] = {
   {0, 10, 15, 20},
   {10, 0, 35, 25},
   {15, 35, 0, 30},
   {20, 25, 30, 0}
};
int total_distance(const std::vector<int>& path) {
   // Calculate the total distance traveled in the given path
   int total = 0;
   for (size_t i = 0; i < path.size() - 1; i++) {
       total += distance_matrix[path[i]][path[i + 1]];
   }
   total += distance_matrix[path.back()][path[0]]; // Return to starting city
   return total;
}
void hill_climbing_tsp(int num_cities, int max_iterations) {
   vector<int> current_path(num_cities); // Initial solution, visiting cities in order
   for (int i = 0; i < num_cities; i++) {
      current_path[i] = i;
   }
   int current_distance = total_distance(current_path);
   for (int it = 0; it < max_iterations; it++) {
      // Generate a neighboring solution by swapping two random cities
      std::vector<int> neighbor_path = current_path;
      int i = rand() % num_cities;
      int j = rand() % num_cities;
      swap(neighbor_path[i], neighbor_path[j]);
      int neighbor_distance = total_distance(neighbor_path);
      // If the neighbor solution is better, move to it
      if (neighbor_distance < current_distance) {
         current_path = neighbor_path;
         current_distance = neighbor_distance;
      }
   }
   cout << "Optimal path: ";
   for (int city : current_path) {
      cout << city << " ";
   }
   cout << endl;
   cout << "Total distance: " << current_distance << endl;
}
int main() {
   srand(time(NULL));
   int max_iterations = 10000;
   hill_climbing_tsp(NUM_CITIES, max_iterations);
   return 0;
}

Output

Optimal path: 0 1 3 2 
Total distance: 80
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class HillClimbingTSP {
   private static final int NUM_CITIES = 4;
   // Distance matrix representing distances between cities
   // Replace this with the actual distance matrix for your problem
   private static final int[][] distanceMatrix = {
      {0, 10, 15, 20},
      {10, 0, 35, 25},
      {15, 35, 0, 30},
      {20, 25, 30, 0}
   };
   private static int totalDistance(List<Integer> path) {
      // Calculate the total distance traveled in the given path
      int total = 0;
      for (int i = 0; i < path.size() - 1; i++) {
         total += distanceMatrix[path.get(i)][path.get(i + 1)];
      }
      total += distanceMatrix[path.get(path.size() - 1)][path.get(0)]; // Return to starting city
      return total;
   }
   private static List<Integer> generateRandomPath(int numCities) {
      List<Integer> path = new ArrayList<>();
      for (int i = 0; i < numCities; i++) {
         path.add(i);
      }
      Random rand = new Random();
      for (int i = numCities - 1; i > 0; i--) {
         int j = rand.nextInt(i + 1);
         int temp = path.get(i);
         path.set(i, path.get(j));
         path.set(j, temp);
      }
      return path;
   }
   public static void hillClimbingTSP(int numCities, int maxIterations) {
      List<Integer> currentPath = generateRandomPath(numCities); // Initial solution
      int currentDistance = totalDistance(currentPath);
      for (int it = 0; it < maxIterations; it++) {
         // Generate a neighboring solution by swapping two random cities
         List<Integer> neighborPath = new ArrayList<>(currentPath);
         int i = new Random().nextInt(numCities);
         int j = new Random().nextInt(numCities);
         int temp = neighborPath.get(i);
         neighborPath.set(i, neighborPath.get(j));
         neighborPath.set(j, temp);
         int neighborDistance = totalDistance(neighborPath);
         // If the neighbor solution is better, move to it
         if (neighborDistance < currentDistance) {
            currentPath = neighborPath;
            currentDistance = neighborDistance;
         }
      }
      System.out.print("Optimal path: ");
      for (int city : currentPath) {
         System.out.print(city + " ");
      }
      System.out.println();
      System.out.println("Total distance: " + currentDistance);
   }
   public static void main(String[] args) {
      int maxIterations = 10000;
      hillClimbingTSP(NUM_CITIES, maxIterations);
   }
}

Output

Optimal path: 1 3 2 0 
Total distance: 80
import random
# Distance matrix representing distances between cities
# Replace this with the actual distance matrix for your problem
distance_matrix = [
    [0, 10, 15, 20],
    [10, 0, 35, 25],
    [15, 35, 0, 30],
    [20, 25, 30, 0]
]
def total_distance(path):
    # Calculate the total distance traveled in the given path
    total = 0
    for i in range(len(path) - 1):
        total += distance_matrix[path[i]][path[i+1]]
    total += distance_matrix[path[-1]][path[0]]  # Return to starting city
    return total
def hill_climbing_tsp(num_cities, max_iterations=10000):
    current_path = list(range(num_cities))  # Initial solution, visiting cities in order
    current_distance = total_distance(current_path) 
    for _ in range(max_iterations):
        # Generate a neighboring solution by swapping two random cities
        neighbor_path = current_path.copy()
        i, j = random.sample(range(num_cities), 2)
        neighbor_path[i], neighbor_path[j] = neighbor_path[j], neighbor_path[i]
        neighbor_distance = total_distance(neighbor_path)
        
        # If the neighbor solution is better, move to it
        if neighbor_distance < current_distance:
            current_path = neighbor_path
            current_distance = neighbor_distance
    return current_path
def main():
    num_cities = 4  # Number of cities in the TSP
    solution = hill_climbing_tsp(num_cities)
    print("Optimal path:", solution)
    print("Total distance:", total_distance(solution))
if __name__ == "__main__":
    main()

Output

Optimal path: [1, 0, 2, 3]
Total distance: 80
Advertisements