from numpy.random import choice, seed, randint from pandas import DataFrame def get_row_distance(source, destination, data): row = data.query( """(source == @source and destination == @destination) or \ (source == @destination and destination == @source)""" ) return row["distance"].values[0] def compute_distance(element, solution, data): accumulator = 0 distinct_elements = solution.query(f"point != {element}") for _, item in distinct_elements.iterrows(): accumulator += get_row_distance( source=element, destination=item.point, data=data, ) return accumulator def get_first_random_solution(n, m, data): solution = DataFrame(columns=["point", "distance"]) seed(42) solution["point"] = choice(n, size=m, replace=False) solution["distance"] = solution["point"].apply( func=compute_distance, solution=solution, data=data ) return solution def element_in_dataframe(solution, element): duplicates = solution.query(f"point == {element}") return not duplicates.empty def replace_worst_element(previous, n, data): solution = previous.copy() worst_index = solution["distance"].astype(float).idxmin() random_element = randint(n) while element_in_dataframe(solution=solution, element=random_element): random_element = randint(n) solution["point"].loc[worst_index] = random_element solution["distance"].loc[worst_index] = compute_distance( element=solution["point"].loc[worst_index], solution=solution, data=data ) return solution def get_random_solution(previous, n, data): solution = replace_worst_element(previous, n, data) while solution["distance"].sum() <= previous["distance"].sum(): solution = replace_worst_element(previous=solution, n=n, data=data) return solution def explore_neighbourhood(element, n, data, max_iterations=100000): neighbourhood = [] neighbourhood.append(element) for _ in range(max_iterations): previous_solution = neighbourhood[-1] neighbour = get_random_solution(previous=previous_solution, n=n, data=data) neighbourhood.append(neighbour) return neighbour def local_search(first_solution, n, data): best_solution = explore_neighbourhood( element=first_solution, n=n, data=data, max_iterations=50 ) return best_solution