Compare commits
11 Commits
3aaf328a2c
...
daf3af574c
Author | SHA1 | Date |
---|---|---|
coolneng | daf3af574c | |
coolneng | 1f6624b770 | |
coolneng | acb9b35c7a | |
coolneng | d82fe81f78 | |
coolneng | b8b1fe9368 | |
coolneng | 5cff3199c6 | |
coolneng | ceea2d8824 | |
coolneng | 550f0bb043 | |
coolneng | 9a4831e31e | |
coolneng | dfdd142fdb | |
coolneng | 38585aa16b |
|
@ -116,7 +116,7 @@ Los parámetros posibles son:
|
|||
| Cualquier archivo de la carpeta data | greedy |
|
||||
| | local |
|
||||
|
||||
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:
|
||||
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:
|
||||
|
||||
#+begin_src shell
|
||||
python src/execution.py
|
||||
|
@ -133,23 +133,23 @@ Los resultados obtenidos se encuentran en el archivo /algorithm-results.xlsx/, p
|
|||
#+CAPTION: Algoritmo greedy
|
||||
[[./assets/greedy.png]]
|
||||
|
||||
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:
|
||||
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:
|
||||
|
||||
- Dataset con n=500: 8-10 segundos
|
||||
- Dataset con n=2000: 6-7 minutos
|
||||
- Dataset con n=500: 7-10 segundos
|
||||
- Dataset con n=2000: 5-12 minutos
|
||||
|
||||
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.
|
||||
La distancia total obtenida, por lo general, es inferior al algoritmo de búsqueda local, aunque no difiere significativamente.
|
||||
|
||||
*** Algoritmo de búsqueda local
|
||||
|
||||
#+CAPTION: Algoritmo de búsqueda local
|
||||
[[./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 es prácticamente constante con cada 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 varía considerablemente según el dataset:
|
||||
|
||||
- Dataset con n=500: 1-3 segundos
|
||||
- Dataset con n=2000: 8-12 segundos
|
||||
- Dataset con n=500: 1-2 minutos
|
||||
- Dataset con n=2000: 20-25 minutos
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
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: 131 KiB After Width: | Height: | Size: 141 KiB |
Binary file not shown.
Before Width: | Height: | Size: 134 KiB After Width: | Height: | Size: 155 KiB |
|
@ -59,19 +59,17 @@ def script_execution(filenames, greedy, local, iterations=3):
|
|||
script = "src/main.py"
|
||||
for dataset in filenames:
|
||||
print(f"Running on dataset {dataset}")
|
||||
greedy_list = []
|
||||
greedy_cmd = run(
|
||||
[executable, script, dataset, "greedy"], capture_output=True
|
||||
).stdout.splitlines()
|
||||
local_list = []
|
||||
for _ in range(iterations):
|
||||
greedy_cmd = run(
|
||||
[executable, script, dataset, "greedy"], capture_output=True
|
||||
).stdout.splitlines()
|
||||
local_cmd = run(
|
||||
[executable, script, dataset, "local"], capture_output=True
|
||||
).stdout.splitlines()
|
||||
greedy_list.append(greedy_cmd)
|
||||
local_list.append(local_cmd)
|
||||
greedy, local = populate_dataframes(
|
||||
greedy, local, greedy_list, local_list, dataset
|
||||
greedy, local, [greedy_cmd], local_list, dataset
|
||||
)
|
||||
return greedy, local
|
||||
|
||||
|
|
|
@ -29,15 +29,18 @@ def get_closest_element(element, data):
|
|||
return Series(data={"point": closest_point, "distance": closest_row["distance"]})
|
||||
|
||||
|
||||
def explore_solutions(solutions, data):
|
||||
def explore_solutions(solutions, data, index):
|
||||
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]
|
||||
solution = closest_elements.iloc[furthest_index]
|
||||
solution.name = index
|
||||
return solution
|
||||
|
||||
|
||||
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})"
|
||||
"(source != @current or destination not in @previous) and \
|
||||
(source not in @previous or destination != @current)"
|
||||
)
|
||||
return duplicate_free_df
|
||||
|
||||
|
@ -46,8 +49,8 @@ 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)
|
||||
for iteration in range(m - 1):
|
||||
element = explore_solutions(solutions, data, index=iteration + 1)
|
||||
solutions = solutions.append(element)
|
||||
data = remove_duplicates(
|
||||
current=element["point"], previous=solutions["point"], data=data
|
||||
|
|
|
@ -1,50 +1,75 @@
|
|||
from numpy.random import choice, seed
|
||||
from numpy.random import choice, seed, randint
|
||||
from pandas import DataFrame
|
||||
|
||||
|
||||
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 element_in_dataframe(solution, element):
|
||||
duplicates = solution.query(
|
||||
f"(source == {element.source} and destination == {element.destination}) or (source == {element.destination} and destination == {element.source})"
|
||||
def get_row_distance(source, destination, data):
|
||||
row = data.query(
|
||||
"""(source == @source and destination == @destination) or \
|
||||
(source == @destination and destination == @source)"""
|
||||
)
|
||||
return not duplicates.empty
|
||||
return row["distance"].values[0]
|
||||
|
||||
|
||||
def replace_worst_element(previous, data):
|
||||
solution = previous.copy()
|
||||
worst_index = solution["distance"].astype(float).idxmin()
|
||||
random_element = data.sample().squeeze()
|
||||
while element_in_dataframe(solution=solution, element=random_element):
|
||||
random_element = data.sample().squeeze()
|
||||
solution.loc[worst_index] = random_element
|
||||
return solution, worst_index
|
||||
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_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)
|
||||
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 explore_neighbourhood(element, data, max_iterations=100000):
|
||||
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, data=data)
|
||||
neighbour = get_random_solution(previous=previous_solution, n=n, data=data)
|
||||
neighbourhood.append(neighbour)
|
||||
return neighbour
|
||||
|
||||
|
||||
def local_search(m, data):
|
||||
first_solution = get_first_random_solution(m=m, data=data)
|
||||
def local_search(n, m, data):
|
||||
first_solution = get_first_random_solution(n, m, data)
|
||||
best_solution = explore_neighbourhood(
|
||||
element=first_solution, data=data, max_iterations=100
|
||||
element=first_solution, n=n, data=data, max_iterations=100
|
||||
)
|
||||
return best_solution
|
||||
|
|
28
src/main.py
28
src/main.py
|
@ -1,28 +1,41 @@
|
|||
from preprocessing import parse_file
|
||||
from greedy import greedy_algorithm
|
||||
from local_search import local_search
|
||||
from local_search import local_search, get_row_distance
|
||||
from sys import argv
|
||||
from time import time
|
||||
from itertools import combinations
|
||||
|
||||
|
||||
def execute_algorithm(choice, n, m, data):
|
||||
if choice == "greedy":
|
||||
return greedy_algorithm(n, m, data)
|
||||
elif choice == "local":
|
||||
return local_search(m, data)
|
||||
return local_search(n, 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()
|
||||
def get_fitness(solutions, data):
|
||||
accumulator = 0
|
||||
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()
|
||||
print(solutions)
|
||||
print("Total distance: " + str(distance_sum))
|
||||
print(f"Total distance: {fitness}")
|
||||
if not duplicates:
|
||||
print("No duplicates found")
|
||||
print("Execution time: " + str(time_delta))
|
||||
print(f"Execution time: {time_delta}")
|
||||
|
||||
|
||||
def usage(argv):
|
||||
|
@ -40,7 +53,8 @@ def main():
|
|||
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)
|
||||
fitness = get_fitness(solutions, data)
|
||||
show_results(solutions, fitness, time_delta=end_time - start_time)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
Loading…
Reference in New Issue