from numpy.random import choice, randint, seed def get_first_random_solution(m, data): seed(42) random_indexes = choice(len(data.index), size=m) return data.iloc[random_indexes] def replace_worst_element(previous, data): solution = previous.copy() worst_index = previous["distance"].astype(float).idxmin() random_candidate = data.loc[randint(low=0, high=len(data.index))] solution.loc[worst_index] = random_candidate return solution def get_random_solution(previous, data): solution = replace_worst_element(previous, data) while solution["distance"].sum() <= previous["distance"].sum(): if solution.equals(previous): break solution = replace_worst_element(previous=solution, data=data) return solution def explore_neighbourhood(element, data, max_iterations=100000): neighbourhood = [] neighbourhood.append(element) for _ in range(max_iterations): previous_solution = neighbourhood[-1] neighbour = get_random_solution(previous=previous_solution, data=data) if neighbour.equals(previous_solution): break neighbourhood.append(neighbour) return neighbour def local_search(m, data): first_solution = get_first_random_solution(m=m, data=data) best_solution = explore_neighbourhood(element=first_solution, data=data) return best_solution