Compare commits
No commits in common. "bd4a88bb4e7eb4de7d42143090fae42fcc4e0bc3" and "aa4a3fdec99043ae104f0db4b58350b538260dea" have entirely different histories.
bd4a88bb4e
...
aa4a3fdec9
|
@ -2,6 +2,4 @@
|
||||||
|
|
||||||
with pkgs;
|
with pkgs;
|
||||||
|
|
||||||
mkShell {
|
mkShell { buildInputs = [ python39 python39Packages.pandas ]; }
|
||||||
buildInputs = [ python39 python39Packages.numpy python39Packages.pandas ];
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,55 +0,0 @@
|
||||||
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
|
|
|
@ -1,60 +0,0 @@
|
||||||
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
47
src/main.py
|
@ -1,47 +0,0 @@
|
||||||
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()
|
|
|
@ -0,0 +1,142 @@
|
||||||
|
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()
|
Loading…
Reference in New Issue