- Design and Analysis of Algorithms
- Home
- Basics of Algorithms
- DAA - Introduction to Algorithms
- DAA - Analysis of Algorithms
- DAA - Methodology of Analysis
- DAA - Asymptotic Notations & Apriori Analysis
- DAA - Time Complexity
- DAA - Master's Theorem
- DAA - Space Complexities
- Divide & Conquer
- DAA - Divide & Conquer Algorithm
- DAA - Max-Min Problem
- DAA - Merge Sort Algorithm
- DAA - Strassen's Matrix Multiplication
- DAA - Karatsuba Algorithm
- DAA - Towers of Hanoi
- Greedy Algorithms
- DAA - Greedy Algorithms
- DAA - Travelling Salesman Problem
- DAA - Prim's Minimal Spanning Tree
- DAA - Kruskal's Minimal Spanning Tree
- DAA - Dijkstra's Shortest Path Algorithm
- DAA - Map Colouring Algorithm
- DAA - Fractional Knapsack
- DAA - Job Sequencing with Deadline
- DAA - Optimal Merge Pattern
- Dynamic Programming
- DAA - Dynamic Programming
- DAA - Matrix Chain Multiplication
- DAA - Floyd Warshall Algorithm
- DAA - 0-1 Knapsack Problem
- DAA - Longest Common Subsequence Algorithm
- DAA - Travelling Salesman Problem using Dynamic Programming
- Randomized Algorithms
- DAA - Randomized Algorithms
- DAA - Randomized Quick Sort Algorithm
- DAA - Karger's Minimum Cut Algorithm
- DAA - Fisher-Yates Shuffle Algorithm
- Approximation Algorithms
- DAA - Approximation Algorithms
- DAA - Vertex Cover Problem
- DAA - Set Cover Problem
- DAA - Travelling Salesperson Approximation Algorithm
- Sorting Techniques
- DAA - Bubble Sort Algorithm
- DAA - Insertion Sort Algorithm
- DAA - Selection Sort Algorithm
- DAA - Shell Sort Algorithm
- DAA - Heap Sort Algorithm
- DAA - Bucket Sort Algorithm
- DAA - Counting Sort Algorithm
- DAA - Radix Sort Algorithm
- DAA - Quick Sort Algorithm
- Searching Techniques
- DAA - Searching Techniques Introduction
- DAA - Linear Search
- DAA - Binary Search
- DAA - Interpolation Search
- DAA - Jump Search
- DAA - Exponential Search
- DAA - Fibonacci Search
- DAA - Sublist Search
- DAA - Hash Table
- Graph Theory
- DAA - Shortest Paths
- DAA - Multistage Graph
- DAA - Optimal Cost Binary Search Trees
- Heap Algorithms
- DAA - Binary Heap
- DAA - Insert Method
- DAA - Heapify Method
- DAA - Extract Method
- Complexity Theory
- DAA - Deterministic vs. Nondeterministic Computations
- DAA - Max Cliques
- DAA - Vertex Cover
- DAA - P and NP Class
- DAA - Cook's Theorem
- DAA - NP Hard & NP-Complete Classes
- DAA - Hill Climbing Algorithm
- DAA Useful Resources
- DAA - Quick Guide
- DAA - Useful Resources
- DAA - Discussion

# 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.

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