Compare commits
No commits in common. "daf3af574cbd0ed6e7f096c3ee9d8c4385496aea" and "3aaf328a2c772bfc31a3e80dc853db3933fd2b7e" have entirely different histories.
daf3af574c
...
3aaf328a2c
|
@ -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.
|
|
||||||
|
|
BIN
docs/Summary.pdf
BIN
docs/Summary.pdf
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 |
|
@ -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_cmd = run(
|
greedy_list = []
|
||||||
[executable, script, dataset, "greedy"], capture_output=True
|
|
||||||
).stdout.splitlines()
|
|
||||||
local_list = []
|
local_list = []
|
||||||
for _ in range(iterations):
|
for _ in range(iterations):
|
||||||
|
greedy_cmd = run(
|
||||||
|
[executable, script, dataset, "greedy"], capture_output=True
|
||||||
|
).stdout.splitlines()
|
||||||
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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
28
src/main.py
28
src/main.py
|
@ -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__":
|
||||||
|
|
Loading…
Reference in New Issue