Compare commits

..

10 Commits

5 changed files with 165 additions and 143 deletions

View File

@ -2,4 +2,6 @@
with pkgs; with pkgs;
mkShell { buildInputs = [ python39 python39Packages.pandas ]; } mkShell {
buildInputs = [ python39 python39Packages.numpy python39Packages.pandas ];
}

55
src/greedy.py Normal file
View File

@ -0,0 +1,55 @@
from pandas import DataFrame, Series
def get_first_solution(n, data):
distance_sum = DataFrame(columns=["point", "distance"])
for element in range(n):
element_df = data.query(f"source == {element} or destination == {element}")
distance = element_df["distance"].sum()
distance_sum = distance_sum.append(
{"point": element, "distance": distance}, ignore_index=True
)
furthest_index = distance_sum["distance"].astype(float).idxmax()
furthest_row = distance_sum.iloc[furthest_index]
furthest_row["distance"] = 0
return furthest_row
def get_different_element(original, row):
if row.source == original:
return row.destination
return row.source
def get_closest_element(element, data):
element_df = data.query(f"source == {element} or destination == {element}")
closest_index = element_df["distance"].astype(float).idxmin()
closest_row = data.loc[closest_index]
closest_point = get_different_element(original=element, row=closest_row)
return Series(data={"point": closest_point, "distance": closest_row["distance"]})
def explore_solutions(solutions, data):
closest_elements = solutions["point"].apply(func=get_closest_element, data=data)
furthest_index = closest_elements["distance"].astype(float).idxmax()
return closest_elements.iloc[furthest_index]
def remove_duplicates(current, previous, data):
duplicate_free_df = data.query(
f"(source != {current} or destination not in @previous) and (source not in @previous or destination != {current})"
)
return duplicate_free_df
def greedy_algorithm(n, m, data):
solutions = DataFrame(columns=["point", "distance"])
first_solution = get_first_solution(n, data)
solutions = solutions.append(first_solution, ignore_index=True)
for _ in range(m):
element = explore_solutions(solutions, data)
solutions = solutions.append(element)
data = remove_duplicates(
current=element["point"], previous=solutions["point"], data=data
)
return solutions

60
src/local_search.py Normal file
View File

@ -0,0 +1,60 @@
from numpy.random import choice, seed
def get_first_random_solution(m, data):
seed(42)
random_indexes = choice(len(data.index), size=m, replace=False)
return data.loc[random_indexes]
def replace_worst_element(previous, data):
solution = previous.copy()
worst_index = solution["distance"].astype(float).idxmin()
random_element = data.sample().squeeze()
while solution.isin(random_element.values.ravel()).any().any():
random_element = data.sample().squeeze()
solution.loc[worst_index] = random_element
return solution, worst_index
def choose_best_solution(previous, current, index):
if previous.loc[index].distance >= current.loc[index].distance:
return previous
return current
def get_random_solution(previous, data):
candidates = []
candidates.append(previous)
solution, worst_index = replace_worst_element(previous, data)
previous_worst_distance = previous["distance"].loc[worst_index]
last_solution = candidates[-1]
while last_solution.distance.loc[worst_index] <= previous_worst_distance:
solution, _ = replace_worst_element(previous=solution, data=data)
if solution.equals(last_solution):
best_solution = choose_best_solution(
previous=previous, current=solution, index=worst_index
)
return best_solution
candidates.append(solution)
last_solution = candidates[-1]
return last_solution
def explore_neighbourhood(element, data, max_iterations=100000):
neighbourhood = []
neighbourhood.append(element)
for i in range(max_iterations):
print(f"Iteration {i}")
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

47
src/main.py Normal file
View File

@ -0,0 +1,47 @@
from preprocessing import parse_file
from greedy import greedy_algorithm
from local_search import local_search
from sys import argv
from time import time
def execute_algorithm(choice, n, m, data):
if choice == "greedy":
return greedy_algorithm(n, m, data)
elif choice == "local":
return local_search(m, data)
else:
print("The valid algorithm choices are 'greedy' and 'local'")
exit(1)
def show_results(solutions, time_delta):
distance_sum = solutions["distance"].sum()
duplicates = solutions.duplicated().any()
print(solutions)
print("Total distance: " + str(distance_sum))
if not duplicates:
print("No duplicates found")
print("Execution time: " + str(time_delta))
def usage(argv):
print(f"Usage: python {argv[0]} <file> <algorithm choice>")
print("algorithm choices:")
print("greedy: greedy algorithm")
print("local: local search algorithm")
exit(1)
def main():
if len(argv) != 3:
usage(argv)
n, m, data = parse_file(argv[1])
start_time = time()
solutions = execute_algorithm(choice=argv[2], n=n, m=m, data=data)
end_time = time()
show_results(solutions, time_delta=end_time - start_time)
if __name__ == "__main__":
main()

View File

@ -1,142 +0,0 @@
from preprocessing import parse_file
from numpy.random import choice, randint, seed
from pandas import DataFrame, Series
from sys import argv
from time import time
def get_first_solution(n, data):
distance_sum = DataFrame(columns=["point", "distance"])
for element in range(n):
element_df = data.query(f"source == {element} or destination == {element}")
distance = element_df["distance"].sum()
distance_sum = distance_sum.append(
{"point": element, "distance": distance}, ignore_index=True
)
furthest_index = distance_sum["distance"].astype(float).idxmax()
furthest_row = distance_sum.iloc[furthest_index]
furthest_row["distance"] = 0
return furthest_row
def get_different_element(original, row):
if row.source == original:
return row.destination
return row.source
def get_closest_element(element, data):
element_df = data.query(f"source == {element} or destination == {element}")
closest_index = element_df["distance"].astype(float).idxmin()
closest_row = data.loc[closest_index]
closest_point = get_different_element(original=element, row=closest_row)
return Series(data={"point": closest_point, "distance": closest_row["distance"]})
def explore_solutions(solutions, data):
closest_elements = solutions["point"].apply(func=get_closest_element, data=data)
furthest_index = closest_elements["distance"].astype(float).idxmax()
return closest_elements.iloc[furthest_index]
def remove_duplicates(current, previous, data):
data = data.query(
f"(source != {current} or destination not in @previous) and (source not in @previous or destination != {current})"
)
return data
def greedy_algorithm(n, m, data):
solutions = DataFrame(columns=["point", "distance"])
first_solution = get_first_solution(n, data)
solutions = solutions.append(first_solution, ignore_index=True)
for _ in range(m):
element = explore_solutions(solutions, data)
solutions = solutions.append(element)
data = remove_duplicates(
current=element["point"], previous=solutions["point"], data=data
)
return solutions
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
def execute_algorithm(choice, n, m, data):
if choice == "greedy":
return greedy_algorithm(n, m, data)
elif choice == "local":
return local_search(m, data)
else:
print("The valid algorithm choices are 'greedy' and 'local'")
exit(1)
def show_results(solutions, time_delta):
distance_sum = solutions["distance"].sum()
duplicates = solutions.duplicated().any()
print(solutions)
print("Total distance: " + str(distance_sum))
if not duplicates:
print("No duplicates found")
print("Execution time: " + str(time_delta))
def usage(argv):
print(f"Usage: python {argv[0]} <file> <algorithm choice>")
print("algorithm choices:")
print("greedy: greedy algorithm")
print("local: local search algorithm")
exit(1)
def main():
if len(argv) != 3:
usage(argv)
n, m, data = parse_file(argv[1])
start_time = time()
solutions = execute_algorithm(choice=argv[2], n=n, m=m, data=data)
end_time = time()
show_results(solutions, time_delta=end_time - start_time)
if __name__ == "__main__":
main()