-
Notifications
You must be signed in to change notification settings - Fork 0
/
GeneradorSoluciones.h
236 lines (213 loc) · 12.5 KB
/
GeneradorSoluciones.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
#include "ArchivosIO.h"
#include <iostream>
#include <fstream>
#include <iomanip>
using namespace std;
// Funcion para generar recorridos para un vehiculo, de acuerdo con restricciones del GVRP
string verificarRestricciones(Vehiculo vehiculoDelNodo, ListaNodos clientesVisitados, Nodo nodoPorAsignar,
Nodo depot, Nodo anterior, Instancia inst){
string porQueNoCumple = "siCumple";
//cout << vehiculoDelNodo.distanciaDesdeRecarga<<"\n";
//Verificar si se tiene combustible para llegar al nodo asignado:
//(Además, checkEstaciones impide que se visiten 3 estaciones seguidas)
double distRecarga = vehiculoDelNodo.distanciaDesdeRecarga();
if(distRecarga>inst.maxDistancia || !vehiculoDelNodo.checkEstaciones(nodoPorAsignar)){
return "combustible";
}
//Verificar si se tiene tiempo para volver al depósito:
double distanciaDeposito = calcularDistancia(nodoPorAsignar,depot);
double tiempoAlDepot = distanciaDeposito/inst.velocidad;
if(vehiculoDelNodo.tiempoTranscurrido()+tiempoAlDepot > inst.maxTiempo){
return "tiempo";
}
//Verificar si el cliente ya fue asignado
if(nodoPorAsignar.tipo == 'c'){
if(clientesVisitados.find(nodoPorAsignar)!=-1){
//Cliente esta en la lista de clientes visitados
return "asignado";
}
}
if(nodoPorAsignar.ID == 0 && anterior.ID == 0){
return "sinSentido";
}
return porQueNoCumple;
}
ListaVehiculos generarSoluciones(int maxIteraciones, Instancia inst, ListaNodos clientes,
ListaNodos estaciones, Nodo depot){
int contadorIter = 0;
Nodo nodoAux;
Variable variableActual;
ListaNodos dominioAcotado,clientesRestantes,clientesVisitados;
ListaVehiculos mejorSolucion,solucionCandidata;
ListaVariables variables;
bool backtracking = false;
bool variableSeAsigno = false;
double distMejorSolucion = INFINITY;
double distActual = 0.0;
string restriccion;
Vehiculo vehiAux = Vehiculo(inst.velocidad, inst.tiempoServicio, inst.tiempoRecarga);
variableActual.asignarNodo(depot);
variables.append(variableActual);
clientesRestantes = concatenar(clientes,estaciones);
/***************LOOP PRINCIPAL******************/
while(contadorIter < maxIteraciones){
//vehiAux guardará al principio de cada iteración el recorrido en el que se esté en esta asignación
variableSeAsigno = false;
vehiAux = variables.recorridoDeVariable(variables.getLast(),inst.velocidad,
inst.tiempoServicio,inst.tiempoRecarga);
/*PARA RUTEO*/
//variables.printNodos();
/******Verificar si se visitaron todos los clientes (nueva solución candidata)******/
if(clientesVisitados.len()>=abs(inst.numClientes)){
//Determinar si nueva solución es mejor
Variable depotFinal;
depotFinal.asignarNodo(depot);
variables.append(depotFinal);
solucionCandidata = variables.extraerSolucionActual(inst.velocidad,inst.tiempoServicio,inst.tiempoRecarga);
distActual = solucionCandidata.calcularDistTotal();
//cout << "\n\n---------SOLUCION CANDIDATA-----------\n";
//solucionCandidata.mostrar();
if(distActual < distMejorSolucion){
//cout << "\n\nSolucion mejorada\n";
mejorSolucion = solucionCandidata;
distMejorSolucion = distActual;
}
//Quitar todos las variables correspondientes al ultimo recorrido, más el depot del último recorrido (backtracking hasta recorrido anterior)
for(int i=0;i<vehiAux.recorrido.len()+1;i++){
if(variables.getLast().nodoAsignado.tipo == 'c'){
clientesRestantes.append(variables.getLast().nodoAsignado);
clientesVisitados.remove(clientesVisitados.find(variables.getLast().nodoAsignado));
}
variables.pop();
}
//Luego se guarda el dominio de la última variable del recorrido anterior, y se elimina de la lista
backtracking = true;
variableSeAsigno = false;
dominioAcotado = variables.getLast().dominio;
//Actualizar listas que hacen seguimiento de clientes visitados y restantes
if(variables.getLast().nodoAsignado.tipo == 'c'){
clientesRestantes.append(variables.getLast().nodoAsignado);
clientesVisitados.remove(clientesVisitados.find(variables.getLast().nodoAsignado));
}
variables.pop();
contadorIter++;
continue;
}
//Se comprueba si estamos entrando al loop por backtracking o no
if(!backtracking){
//Si no es backtracking, se declara la nueva variable que se asignará en esta iteración
variableActual = Variable(clientesRestantes);
}
else{
//Si hicimos backtracking, la variable auxiliar "variableActual" parte con el dominio (acotado)
//de la variable que se tenía antes al final de la lista.
variableActual = Variable(dominioAcotado);
backtracking = false;
}
//Se verifica si el recorrido actual ya terminó, en cuyo caso se debe asignar un depot a la nueva variable
if(vehiAux.recorridoTerminado()){
//variableActual = Variable(clientes,estaciones);
variableActual.asignarNodo(depot);
variables.append(variableActual);
}
else{/*******************LOOP SECUNDARIO*******************/
//Se buscan asignaciones factibles para la nueva variable en un loop. Si el dominio esta vacío o se encontró
//la asignación, se termina el loop, habiendo asignado la variable.
//Verificar que no se haya asignado un nodo a la variable, y que su dominio no esté vacío
while(!variableSeAsigno && !variableActual.dominioVacio()){
//Si quedan clientes se busca más cercano:
if(variableActual.dominioTieneCliente()){
nodoAux = nodoMenorDistancia(variables.getLast().nodoAsignado, variableActual.dominioSoloClientes());
restriccion = verificarRestricciones(vehiAux,clientesVisitados,nodoAux,depot,variables.getLast().nodoAsignado,inst);
//Se verifica si el cliente a asignar cumple las restricciones
if(restriccion == "siCumple"){
//Si cumple las restricciones, se asigna el cliente a la variable
variableActual.asignarNodo(nodoAux);
variableActual.quitarDelDominio(variableActual.nodoAsignado);
variables.append(variableActual);
//Actualizar listas que hacen seguimiento de clientes visitados y restantes
if(variables.getLast().nodoAsignado.tipo == 'c'){
clientesRestantes.remove(clientesRestantes.find(variables.getLast().nodoAsignado));
clientesVisitados.append(variables.getLast().nodoAsignado);
}
variableSeAsigno = true;
}
else if(restriccion == "combustible"){
//Si no cumple restriccion de combustible, se deben quitar clientes del dominio, para ir estacion
variableActual.dominio = variableActual.quitarClientesDominio();
}
else if(restriccion == "tiempo"){
//Si no cumple restriccion de tiempo, se revisa si el depot está a una distancia tal que no se exceda
//la distanciaMax permitida por más de 30 millas (dando un rango razonable de error). Si es así, se debe
//hacer BT y probar con nodos que estén más cerca del depot.
//AGREGAR ALGO PARA QUE SE LIMITEN EN LA VARIABLE ANTERIOR LOS NODOS QUE ESTÉN MÁS LEJOS DEL DEPOT
double distAlDepot= calcularDistancia(variables.getLast().nodoAsignado, depot);
if(vehiAux.distanciaDesdeRecarga() + distAlDepot - inst.maxDistancia > 70){
variableActual.dominio = ListaNodos();
break;
}
//Chequear si alcanza el tiempo para volver al depot directo, de lo contrario hace BT
if(distAlDepot/inst.velocidad + vehiAux.tiempoTranscurrido() - inst.maxTiempo > 20){
variableActual.dominio = ListaNodos();
break;
}
variableActual.asignarNodo(depot);
variables.append(variableActual);
variableSeAsigno = true;
}
else if(restriccion == "asignado"){
//Si no cumple porque esta asignado a otro cliente, se quita de su dominio
variableActual.quitarDelDominio(nodoAux);
}
}
else{ //Si no quedan clientes en el dominio de variableActual
nodoAux = nodoMenorDistancia(variables.getLast().nodoAsignado,variableActual.dominio);
restriccion = verificarRestricciones(vehiAux,clientesVisitados,nodoAux,depot,variables.getLast().nodoAsignado,inst);
if(restriccion == "siCumple"){
//Si la estación cumple todas las restricciones, se pasa por ella.
variableActual.asignarNodo(nodoAux);
variableActual.quitarDelDominio(variableActual.nodoAsignado);
variables.append(variableActual);
variableSeAsigno = true;
}
else if(restriccion == "combustible" || restriccion == "tiempo" || restriccion == "sinSentido"){
variableActual.dominio = variableActual.quitarEstacionesDominio();
break;
}
}
}
/****CONDICIONES DE BACKTRACKING****/
distActual = variables.extraerSolucionActual(inst.velocidad,inst.tiempoServicio,inst.tiempoRecarga).calcularDistTotal();
if((variableActual.dominioVacio() && variableSeAsigno==false) || distActual > distMejorSolucion){
//1 Si el dominio de una variable queda vacío, sin haberle asignado un valor factible, significa que el vehiculo
//no cumple ninguna restriccion y debe hacerse backtracking
//
//2 Si la distancia de la solucion actual es mayor que la mejor solucion, se hace backtracking
//Chequeamos ademas si la penúltima variable en la lista tienen un depot, volvemos 2 veces hacia atras para evitar loops
if(variables.getVariable(variables.len()-1).nodoAsignado.tipo=='d' &&
variables.getVariable(variables.len()-2).nodoAsignado.tipo=='d'){
variables.pop();
variables.pop();
}
backtracking = true;
variableSeAsigno = false;
//se quita variable de la lista, pero se guarda su dominio para darselo a la variableActual en la sgte iteracion
//Actualizar listas que hacen seguimiento de clientes visitados y restantes
if(variables.getLast().nodoAsignado.tipo == 'c'){
clientesRestantes.append(variables.getLast().nodoAsignado);
clientesVisitados.remove(clientesVisitados.find(variables.getLast().nodoAsignado));
}
dominioAcotado = variables.getLast().dominio;
variables.pop();
variables.moveToEnd();
}
}/***FIN DEL LOOP SECUNDARIO***/
contadorIter ++;
}/***FIN DEL LOOP PRINCIPAL***/
if(mejorSolucion.len()==0){
variableActual.asignarNodo(depot);
variables.append(variableActual);
return variables.extraerSolucionActual(inst.velocidad,inst.tiempoServicio,inst.tiempoRecarga);
}
return mejorSolucion;
}