From a060f93554325e9c13bc4d3748f6c69cf3d9b45d Mon Sep 17 00:00:00 2001 From: Cory Balaton Date: Tue, 1 Oct 2024 19:20:34 +0200 Subject: [PATCH] Improve the code. - Add a docstring. - Add type hinting. - Change algorithm from steepest ascent to simple hill climbing. --- hill_climbing.py | 79 ++++++++++++++++++++++++++++-------------------- 1 file changed, 46 insertions(+), 33 deletions(-) diff --git a/hill_climbing.py b/hill_climbing.py index 7babb0d..cc9cfda 100644 --- a/hill_climbing.py +++ b/hill_climbing.py @@ -1,53 +1,66 @@ +import copy from typing import Tuple import numpy as np import numpy.typing as npt -import copy -from common import plot_plan, read_data +from common import indexes_to_cities, plot_plan, read_data -original_perm = None def hill_climbing(distances: npt.NDArray) -> Tuple[float, npt.NDArray]: - size = len(distances) - perm = np.arange(size) - np.random.shuffle(perm) + """A simple hill climbing algorithm. + + The algorithm starts on a random permutation and attempts to improve + the circuit by trying to switch neighboring elements. Each iteration + tries to switch adjacent neighbors and sees which one yields the largest + improvement. + + Args: + distances npt.NDArray: A matrix containing the distances between cities. + + Returns: + Tuple[float, npt.NDArray] A tuple containing the distance of the + solution and the solution itself. + + """ + + size: int = len(distances) # The size of the permutation array + perm: npt.NDArray = np.arange(size) # Create an array from 0..size + + np.random.shuffle(perm) # Get random permutation + + # Get the distance of the random permutation + current_distance: float = np.sum( + [distances[perm[i - 1], perm[i]] for i in range(size)] + ) + + found_improvement: bool = True - print(perm) - global original_perm - original_perm = copy.deepcopy(perm) - current_route: float = np.sum([distances[perm[i - 1], perm[i]] for i in range(size)]) - found_improvement = True while found_improvement: - found_improvement = False - tmp_improvement: float = current_route - improvement_index: int = -1 + + found_improvement = False # Assume we haven't found an improvement + tmp_distance: float = current_distance + + # Try to find an improvement for i in range(size): - perm[i - 1], perm[i] = perm[i], perm[i - 1] - tmp_route: float = np.sum([distances[perm[i - 1], perm[i]] for i in range(size)]) - if tmp_route < tmp_improvement: - tmp_improvement = tmp_route - improvement_index = i - found_improvement = True + perm[[i - 1, i]] = perm[[i, i - 1]] # Swap i - 1 and i - perm[i - 1], perm[i] = perm[i], perm[i - 1] - - if found_improvement: - current_route = tmp_improvement - perm[improvement_index - 1], perm[improvement_index] = ( - perm[improvement_index], - perm[improvement_index - 1], + tmp_distance: float = np.sum( + [distances[perm[i - 1], perm[i]] for i in range(size)] ) - print(perm) + if tmp_distance < current_distance: + current_distance = tmp_distance + found_improvement = True + break - return (current_route, perm) + perm[[i - 1, i]] = perm[[i, i - 1]] # Swap back i - 1 and i + + return (current_distance, perm) if __name__ == "__main__": cities, data = read_data("./european_cities.csv") - distance, perm = hill_climbing(data[:10,:10]) - - plot_plan(list(map(lambda i: cities[i], list(perm)))) - plot_plan(list(map(lambda i: cities[i], list(original_perm)))) + distance, perm = hill_climbing(data[:10, :10]) + plot_plan(indexes_to_cities(perm, cities))