Maintenance Manager @ Grupo Integrado de Transporte Masivo S.A.
Fecha de publicación
December 8, 2023
Taxonomía
El Procedimiento de Greedy Randomized Adaptative Search es un algoritmo Metaheurístico y de Optimización Global, originalmente propuesto para los practicantes de la Investigación de Operaciones. La aplicación iterativa de una técnica de Local Search incrustada relaciona el enfoque con Iterated Local Search y las técnicas de Multi-Start.
Estrategia
El objetivo del Greedy Randomized Adaptative Search es muestrear repetidamente soluciones codiciosas y, a continuación, utilizar un procedimiento de Local Search para refinarlas hasta alcanzar un óptimo local. La estrategia del procedimiento se centra en el mecanismo de construcción por pasos estocásticos y codiciosos que restringe la selección y el orden de inclusión de los componentes de una solución en función del valor que se espera que aporten.
Procedimiento
Se presenta un pseudocódigo del Greedy Randomized Adaptative Search para minimizar una función de costo.
Pseudocódigo Greedy Randomized Adaptative Search
Además, el pseudocódigo de la función de construcción aleatoria codiciosa. La función consiste en la construcción paso a paso de una solución candidata utilizando un proceso de construcción estocástico. La función trabaja construyendo una Lista Restringida de Candidatos (RCL por sus siglas en inglés) que restringe los componentes de una solución (características) que pueden seleccionarse en cada ciclo. La RCL puede limitarse mediante un tamaño explícito o utilizando un umbral \((⍺ ∈ [0,1])\) en el costo de añadir cada característica a la solución candidata actual.
Pseudocódigo Función de Construcción
Heurística
El umbral \(⍺\) define el grado de avaricia del mecanismo de construcción, donde valores cercanos a 0 pueden ser demasiado codiciosos, y valores cercanos a 1 pueden ser demasiado generalizados.
Como alternativa al uso del umbral \(⍺\), el RCL se puede puede limitarse al top \(n%\) de las características candidatas que pueden seleccionarse en cada ciclo de construcción.
La técnica se diseñó para clases de problemas discretos, como los problemas de optimización combinatoria.
Código
El algoritmo se aplica a la instancia Berlin52 de Travling Salesman Problem (TSP), tomada de la TSPLIB. El problema busca una permutación del orden de visita de las ciudades (llamada tour o recorrido) que minimice la distancia total recorrida. La distancia óptima del recorrido para el caso Berlín52 es de 7.542 unidades.
La construcción estocástica y codiciosa por pasos de un recorrido implica la evaluación de las ciudades candidatas por el costo que aportan por ser la siguiente ciudad del recorrido. El algoritmo utiliza un procedimiento estocástico 2-opt para Local Search con un número fijo de iteraciones no mejoradas como como condición de parada.
# Función para calcular la distancia euclidiana entre dos puntoseuc_2d <-function(c1, c2) {return(round(sqrt((c1[1] - c2[1])^2+ (c1[2] - c2[2])^2)))}# Función para calcular el costo de una permutación de ciudadescost <-function(perm, cities) { distance <-0for (i inseq_along(perm)) { c1 <- perm[i] c2 <-if (i ==length(perm)) perm[1] else perm[i +1] distance <- distance +euc_2d(cities[c1, ], cities[c2, ]) }return(distance)}# Función para realizar una operación de dos-opt estocástica en una permutaciónstochastic_two_opt <-function(permutation) { perm <- permutation c1 <-sample(length(perm), 1) exclude <-c(c1, if (c1 ==1) length(perm) else c1 -1, if (c1 ==length(perm)) 1else c1 +1) c2 <-sample(length(perm), 1)while (c2 %in% exclude) { c2 <-sample(length(perm), 1) }if (c2 < c1) { temp <- c1 c1 <- c2 c2 <- temp } perm[c1:c2] <-rev(perm[c1:c2])return(perm)}# Función para realizar una búsqueda local en el espacio de las permutacioneslocal_search <-function(best, cities, max_no_improv) { count <-0repeat { candidate <-list() candidate$vector <-stochastic_two_opt(best$vector) candidate$cost <-cost(candidate$vector, cities)if (candidate$cost < best$cost) { count <-0 best <- candidate } else { count <- count +1 }if (count >= max_no_improv) break }return(best)}# Función para construir una solución codiciosa aleatorizadaconstruct_randomized_greedy_solution <-function(cities, alpha) { candidate <-list() candidate$vector <-sample(1:nrow(cities), 1) allCities <-1:nrow(cities)while (length(candidate$vector) <nrow(cities)) { candidates <-setdiff(allCities, candidate$vector) costs <-sapply(candidates, function(i) euc_2d(cities[candidate$vector[length(candidate$vector)], ], cities[i, ])) rcl <- candidates[which(costs <=min(costs) + alpha * (max(costs) -min(costs)))] candidate$vector <-c(candidate$vector, sample(rcl, 1)) } candidate$cost <-cost(candidate$vector, cities)return(candidate)}# Función de búsqueda principalsearch <-function(cities, max_iter, max_no_improv, alpha) { best <-NULL cost_progress <-list() # Lista para registrar el progreso del costofor (iter inseq_len(max_iter)) { candidate <-construct_randomized_greedy_solution(cities, alpha) candidate <-local_search(candidate, cities, max_no_improv)if (is.null(best) || candidate$cost < best$cost) { best <- candidate } cost_progress[[iter]] <- best$cost # Registrar el costo en la listacat(" > iteration", iter, ", best=", best$cost, "\n") }return(list(best = best, cost_progress = cost_progress)) # Devolver la mejor solución y el progreso del costo}# Configuración del problemaberlin52 <-matrix(c(565,575,25,185,345,750,945,685,845,655,880,660,25,230,525,1000,580,1175,650,1130,1605,620,1220,580,1465,200,1530,5,845,680,725,370,145,665,415,635,510,875,560,365,300,465,520,585,480,415,835,625,975,580,1215,245,1320,315,1250,400,660,180,410,250,420,555,575,665,1150,1160,700,580,685,595,685,610,770,610,795,645,720,635,760,650,475,960,95,260,875,920,700,500,555,815,830,485,1170,65,830,610,605,625,595,360,1340,725,1740,245), ncol =2, byrow =TRUE)# Configuración del algoritmomax_iter <-50max_no_improv <-50greediness_factor <-0.3# Ejecutar el algoritmoresult <-search(berlin52, max_iter, max_no_improv, greediness_factor)