Compare commits

..

No commits in common. "daf3af574cbd0ed6e7f096c3ee9d8c4385496aea" and "3aaf328a2c772bfc31a3e80dc853db3933fd2b7e" have entirely different histories.

9 changed files with 53 additions and 93 deletions

View File

@ -116,7 +116,7 @@ Los parámetros posibles son:
| Cualquier archivo de la carpeta data | greedy | | Cualquier archivo de la carpeta data | greedy |
| | local | | | local |
También se proporciona un script que ejecuta 1 iteración del algoritmo greedy y 3 iteraciones de la búsqueda local, con cada uno de los /datasets/, y guarda los resultados en una hoja de cálculo. Se puede ejecutar mediante el siguiente comando: También se proporciona un script que ejecuta 3 iteraciones de ambos algoritmos, con cada uno de los /datasets/, y guarda los resultados en una hoja de cálculo. Se puede ejecutar mediante el siguiente comando:
#+begin_src shell #+begin_src shell
python src/execution.py python src/execution.py
@ -133,23 +133,23 @@ Los resultados obtenidos se encuentran en el archivo /algorithm-results.xlsx/, p
#+CAPTION: Algoritmo greedy #+CAPTION: Algoritmo greedy
[[./assets/greedy.png]] [[./assets/greedy.png]]
El algoritmo greedy es determinista, por lo tanto la desviación típica es nula, dado que se ejecuta una única vez. El tiempo de ejecución varía considerablemente según el dataset: El algoritmo greedy es determinista, por lo tanto la desviación típica es prácticamente nula (varía en el tiempo de ejecución únicamente). El tiempo de ejecución varía considerablemente según el dataset:
- Dataset con n=500: 7-10 segundos - Dataset con n=500: 8-10 segundos
- Dataset con n=2000: 5-12 minutos - Dataset con n=2000: 6-7 minutos
La distancia total obtenida, por lo general, es inferior al algoritmo de búsqueda local, aunque no difiere significativamente. Por lo tanto, el algoritmo o la implementación de éste no escalan al aumentar el número de casos.
Las distancias obtenidas son considerablemente peores que las del algoritmo de búsqueda local, aunque hay que tener en cuenta que en la implementación del algoritmo de búsqueda local la distancia del primer elemento no es 0, lo cual tiene afecta el resultado final.
*** Algoritmo de búsqueda local *** Algoritmo de búsqueda local
#+CAPTION: Algoritmo de búsqueda local #+CAPTION: Algoritmo de búsqueda local
[[./assets/local.png]] [[./assets/local.png]]
El algoritmo de búsqueda local es estocástico, debido a que para la obtención de cada una de las soluciones se utiliza un generador de números pseudoaleatorio. El tiempo de ejecución varía considerablemente según el dataset: El algoritmo de búsqueda local es estocástico, debido a que para la obtención de cada una de las soluciones se utiliza un generador de números pseudoaleatorio. El tiempo de ejecución es prácticamente constante con cada dataset:
- Dataset con n=500: 1-2 minutos - Dataset con n=500: 1-3 segundos
- Dataset con n=2000: 20-25 minutos - Dataset con n=2000: 8-12 segundos
La distancia total obtenida, por lo general, es superior al algoritmo greedy lo cual indica que la búsqueda local obtiene mejores resultados a expensas del tiempo de ejecución. Por lo tanto éste escala bien al aumentar el número de casos. Aún así, al realizar ciertas pruebas aumentando el número de iteraciones máximas, el rendimiento del algoritmo emperoaba considerablemente. Por este motivo, las ejecuciones de este algoritmo se han hecho con 100 iteraciones máximas.
Debido a nuestras limitaciones computacionales, las ejecuciones de este algoritmo se hicieron con 100 iteraciones máximas.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 141 KiB

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 155 KiB

After

Width:  |  Height:  |  Size: 134 KiB

View File

@ -59,17 +59,19 @@ def script_execution(filenames, greedy, local, iterations=3):
script = "src/main.py" script = "src/main.py"
for dataset in filenames: for dataset in filenames:
print(f"Running on dataset {dataset}") print(f"Running on dataset {dataset}")
greedy_list = []
local_list = []
for _ in range(iterations):
greedy_cmd = run( greedy_cmd = run(
[executable, script, dataset, "greedy"], capture_output=True [executable, script, dataset, "greedy"], capture_output=True
).stdout.splitlines() ).stdout.splitlines()
local_list = []
for _ in range(iterations):
local_cmd = run( local_cmd = run(
[executable, script, dataset, "local"], capture_output=True [executable, script, dataset, "local"], capture_output=True
).stdout.splitlines() ).stdout.splitlines()
greedy_list.append(greedy_cmd)
local_list.append(local_cmd) local_list.append(local_cmd)
greedy, local = populate_dataframes( greedy, local = populate_dataframes(
greedy, local, [greedy_cmd], local_list, dataset greedy, local, greedy_list, local_list, dataset
) )
return greedy, local return greedy, local

View File

@ -29,18 +29,15 @@ def get_closest_element(element, data):
return Series(data={"point": closest_point, "distance": closest_row["distance"]}) return Series(data={"point": closest_point, "distance": closest_row["distance"]})
def explore_solutions(solutions, data, index): def explore_solutions(solutions, data):
closest_elements = solutions["point"].apply(func=get_closest_element, data=data) closest_elements = solutions["point"].apply(func=get_closest_element, data=data)
furthest_index = closest_elements["distance"].astype(float).idxmax() furthest_index = closest_elements["distance"].astype(float).idxmax()
solution = closest_elements.iloc[furthest_index] return closest_elements.iloc[furthest_index]
solution.name = index
return solution
def remove_duplicates(current, previous, data): def remove_duplicates(current, previous, data):
duplicate_free_df = data.query( duplicate_free_df = data.query(
"(source != @current or destination not in @previous) and \ f"(source != {current} or destination not in @previous) and (source not in @previous or destination != {current})"
(source not in @previous or destination != @current)"
) )
return duplicate_free_df return duplicate_free_df
@ -49,8 +46,8 @@ def greedy_algorithm(n, m, data):
solutions = DataFrame(columns=["point", "distance"]) solutions = DataFrame(columns=["point", "distance"])
first_solution = get_first_solution(n, data) first_solution = get_first_solution(n, data)
solutions = solutions.append(first_solution, ignore_index=True) solutions = solutions.append(first_solution, ignore_index=True)
for iteration in range(m - 1): for _ in range(m):
element = explore_solutions(solutions, data, index=iteration + 1) element = explore_solutions(solutions, data)
solutions = solutions.append(element) solutions = solutions.append(element)
data = remove_duplicates( data = remove_duplicates(
current=element["point"], previous=solutions["point"], data=data current=element["point"], previous=solutions["point"], data=data

View File

@ -1,75 +1,50 @@
from numpy.random import choice, seed, randint from numpy.random import choice, seed
from pandas import DataFrame
def get_row_distance(source, destination, data): def get_first_random_solution(m, 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) seed(42)
solution["point"] = choice(n, size=m, replace=False) random_indexes = choice(len(data.index), size=m, replace=False)
solution["distance"] = solution["point"].apply( return data.loc[random_indexes]
func=compute_distance, solution=solution, data=data
)
return solution
def element_in_dataframe(solution, element): def element_in_dataframe(solution, element):
duplicates = solution.query(f"point == {element}") duplicates = solution.query(
f"(source == {element.source} and destination == {element.destination}) or (source == {element.destination} and destination == {element.source})"
)
return not duplicates.empty return not duplicates.empty
def replace_worst_element(previous, n, data): def replace_worst_element(previous, data):
solution = previous.copy() solution = previous.copy()
worst_index = solution["distance"].astype(float).idxmin() worst_index = solution["distance"].astype(float).idxmin()
random_element = randint(n) random_element = data.sample().squeeze()
while element_in_dataframe(solution=solution, element=random_element): while element_in_dataframe(solution=solution, element=random_element):
random_element = randint(n) random_element = data.sample().squeeze()
solution["point"].loc[worst_index] = random_element solution.loc[worst_index] = random_element
solution["distance"].loc[worst_index] = compute_distance( return solution, worst_index
element=solution["point"].loc[worst_index], solution=solution, data=data
)
def get_random_solution(previous, data):
solution, worst_index = replace_worst_element(previous, data)
previous_worst_distance = previous["distance"].loc[worst_index]
while solution.distance.loc[worst_index] <= previous_worst_distance:
solution, _ = replace_worst_element(previous=solution, data=data)
return solution return solution
def get_random_solution(previous, n, data): def explore_neighbourhood(element, data, max_iterations=100000):
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 = []
neighbourhood.append(element) neighbourhood.append(element)
for _ in range(max_iterations): for _ in range(max_iterations):
previous_solution = neighbourhood[-1] previous_solution = neighbourhood[-1]
neighbour = get_random_solution(previous=previous_solution, n=n, data=data) neighbour = get_random_solution(previous=previous_solution, data=data)
neighbourhood.append(neighbour) neighbourhood.append(neighbour)
return neighbour return neighbour
def local_search(n, m, data): def local_search(m, data):
first_solution = get_first_random_solution(n, m, data) first_solution = get_first_random_solution(m=m, data=data)
best_solution = explore_neighbourhood( best_solution = explore_neighbourhood(
element=first_solution, n=n, data=data, max_iterations=100 element=first_solution, data=data, max_iterations=100
) )
return best_solution return best_solution

View File

@ -1,41 +1,28 @@
from preprocessing import parse_file from preprocessing import parse_file
from greedy import greedy_algorithm from greedy import greedy_algorithm
from local_search import local_search, get_row_distance from local_search import local_search
from sys import argv from sys import argv
from time import time from time import time
from itertools import combinations
def execute_algorithm(choice, n, m, data): def execute_algorithm(choice, n, m, data):
if choice == "greedy": if choice == "greedy":
return greedy_algorithm(n, m, data) return greedy_algorithm(n, m, data)
elif choice == "local": elif choice == "local":
return local_search(n, m, data) return local_search(m, data)
else: else:
print("The valid algorithm choices are 'greedy' and 'local'") print("The valid algorithm choices are 'greedy' and 'local'")
exit(1) exit(1)
def get_fitness(solutions, data): def show_results(solutions, time_delta):
accumulator = 0 distance_sum = solutions["distance"].sum()
comb = combinations(solutions.index, r=2)
for index in list(comb):
elements = solutions.loc[index, :]
accumulator += get_row_distance(
source=elements["point"].head(n=1).values[0],
destination=elements["point"].tail(n=1).values[0],
data=data,
)
return accumulator
def show_results(solutions, fitness, time_delta):
duplicates = solutions.duplicated().any() duplicates = solutions.duplicated().any()
print(solutions) print(solutions)
print(f"Total distance: {fitness}") print("Total distance: " + str(distance_sum))
if not duplicates: if not duplicates:
print("No duplicates found") print("No duplicates found")
print(f"Execution time: {time_delta}") print("Execution time: " + str(time_delta))
def usage(argv): def usage(argv):
@ -53,8 +40,7 @@ def main():
start_time = time() start_time = time()
solutions = execute_algorithm(choice=argv[2], n=n, m=m, data=data) solutions = execute_algorithm(choice=argv[2], n=n, m=m, data=data)
end_time = time() end_time = time()
fitness = get_fitness(solutions, data) show_results(solutions, time_delta=end_time - start_time)
show_results(solutions, fitness, time_delta=end_time - start_time)
if __name__ == "__main__": if __name__ == "__main__":