예제 #1
0
        public Solucion GRASPFijo()
        {
            SolucionFVRP    mejorSolucion          = null;
            TimeSpan        mejorTiempo            = new TimeSpan();
            Stopwatch       sw                     = new Stopwatch();
            TipoTerminacion terminacion            = TipoTerminacion.Completa;
            int             iteracionesSinMejora   = 0;
            int             solucionesMenorEpsilon = 0;

            sw.Start();

            for (var i = 0; i < cantidadCorridas && iteracionesSinMejora < cantidadDeIteracionesSinMejora; i++)
            {
                SolucionFVRP solucion = GolosoAleatorio(alfaFijo);
                RealizarBusquedaLocal(solucion);

                ValidarSolucion(solucion);

                if (mejorSolucion == null || mejorSolucion.Costo > solucion.Costo)
                {
                    sw.Stop();
                    decimal porcentajeDeMejora = (decimal)100;
                    if (mejorSolucion != null)
                    {
                        porcentajeDeMejora = 100 - ((solucion.Costo * 100) / mejorSolucion.Costo);
                    }
                    mejorSolucion = solucion;
                    mejorTiempo   = sw.Elapsed;
                    sw.Start();
                    iteracionesSinMejora = 0;
                    if (porcentajeDeMejora < epsilon)
                    {
                        if (solucionesMenorEpsilon > cantidadSolucionesMenorEpsilon)
                        {
                            terminacion = TipoTerminacion.Epsilon;
                            break;
                        }
                        solucionesMenorEpsilon++;
                    }
                }
                else
                {
                    iteracionesSinMejora++;
                }
            }

            if (iteracionesSinMejora >= cantidadDeIteracionesSinMejora)
            {
                terminacion = TipoTerminacion.IteracionesSinMejora;
            }

            return(new Solucion()
            {
                SolucionFVRP = mejorSolucion,
                MejorAlfa = alfaFijo,
                MejorTiempo = mejorTiempo,
                TipoTerminacion = terminacion.ToString()
            });
        }
예제 #2
0
 void AgregarCaminoConNodoInicial(SolucionFVRP solucion)
 {
     solucion.Caminos.Add(new Camino()
     {
         Nodos = new List <int>(), Costo = 0, Capacidad = InstanciaProblema.capacidad
     });
     solucion.Caminos[solucion.Caminos.Count - 1].Nodos.Add(0);
 }
예제 #3
0
        void RealizarBusquedaLocal(SolucionFVRP solucionCompleta, int cant_iteraciones_busquedas_locales = 2)
        {
            for (var i = 0; i < cant_iteraciones_busquedas_locales; i++)
            {
                busquedasLocales.relocate(solucionCompleta.Caminos);
                busquedasLocales.exchange(solucionCompleta.Caminos);
                busquedasLocales.cross_exchange(solucionCompleta.Caminos);
                busquedasLocales.tail_exchange(solucionCompleta.Caminos);

                foreach (var solucion in solucionCompleta.Caminos)
                {
                    // Si son 6 nodos o menos conviene permutar todo a andar haciendo búsquedas locales heurísticas
                    if (solucion.Nodos.Count <= 6)
                    {
                        Utilidades.PermutarTodo(celdas, solucion);
                    }
                    else
                    {
                        busquedasLocales.relocate(solucion);
                        busquedasLocales.exchange(solucion);
                        busquedasLocales.or_opt(solucion);
                        busquedasLocales.four_opt(solucion);
                        busquedasLocales.two_opt(solucion);
                    }

                    // Hay que reacomodar porque estas búsquedas locales te mueven el depósito para cualquier lado
                    while (solucion.Nodos[0] != 0)
                    {
                        var valor = solucion.Nodos[0];
                        for (var cam = 0; cam < solucion.Nodos.Count; cam++)
                        {
                            var siguiente_posicion = cam + 1 == solucion.Nodos.Count ? 0 : cam + 1;
                            var _valor             = solucion.Nodos[siguiente_posicion];
                            solucion.Nodos[siguiente_posicion] = valor;
                            valor = _valor;
                        }
                    }
                }

                busquedasLocales.family_exchange(solucionCompleta.Caminos);
            }

            // Por búsquedas locales puede ocurrir que quede algún vehículo solo con el depósito.
            // No afecta a la distancia, solo agrega un vehículo al pedo. Se lo sacamos y listo.
            var j = 0;

            while (j < solucionCompleta.Caminos.Count)
            {
                if (solucionCompleta.Caminos[j].Nodos.Count == 1)
                {
                    solucionCompleta.Caminos.RemoveAt(j);
                }
                else
                {
                    j++;
                }
            }
        }
예제 #4
0
        void CerrarUltimoCamino(SolucionFVRP solucion)
        {
            // Sumar al costo la vuelta al último nodo
            var ultimo_camino = solucion.Caminos.Count - 1;

            solucion.Caminos[ultimo_camino].Costo +=
                celdas.Get(solucion.Caminos[ultimo_camino].Nodos[0],
                           solucion.Caminos[ultimo_camino].Nodos[solucion.Caminos[ultimo_camino].Nodos.Count - 1]).distancia;
        }
예제 #5
0
        private SolucionFVRP GolosoAleatorio(decimal alfa)
        {
            // procedure GreedyRandomizedConstruction(α,Seed)
            // 1.  S ← empty;
            // 2.  Initialize the candidate set: C ← E;
            // 3.  Evaluate the incremental cost c(e) for all e ∈ C;
            // 4.  while C != empty do
            // 5.      c_min ← min{c(e) | e ∈C};
            // 6.      c_max ← max{c(e) | e ∈ C};
            // 7.      Build the restricted candidate list: RCL ← {e ∈ C | c(e) ≤ c_min +α(c_max − c_min)};
            // 8.      Choose s at random from RCL;
            // 9.      Incorporate s into solution: S ← S∪ {s};
            // 10.     Update the candidate set C;
            // 11.     Reevaluate the incremental cost c(e) for all e ∈ C;
            // 12. end;
            // 13. return S;
            // end.
            SolucionFVRP solucion = new SolucionFVRP(InstanciaProblema.Distancias.Count);

            solucion.Caminos            = new List <Camino>();
            solucion.FamiliasPendientes = new List <Familia>();

            foreach (var familia in InstanciaProblema.Familias)
            {
                if (!familia.nodos.Contains(0))
                {
                    solucion.FamiliasPendientes.Add(new Familia()
                    {
                        cantidad_visitada = 0,
                        cantidad_visitas  = familia.cantidad_visitas,
                        id    = familia.id,
                        nodos = new List <int>(familia.nodos.ToArray())
                    });
                }
            }

            AgregarCaminoConNodoInicial(solucion);

            while (solucion.FamiliasPendientes.Count > 0)
            {
                if (!Mover(solucion, alfa))
                {
                    CerrarUltimoCamino(solucion);
                    AgregarCaminoConNodoInicial(solucion);
                }
            }
            CerrarUltimoCamino(solucion);

            return(solucion);
        }
예제 #6
0
        public void ValidarSolucion(SolucionFVRP solucion)
        {
            // Validar que solo aparezca una vez el 0 en cada camino y este se encuentre en el 1er lugar
            foreach (var camino in solucion.Caminos)
            {
                if (camino.Nodos.Count != camino.Nodos.Distinct().Count())
                {
                    throw new Exception("Nodos repetidos");
                }
                if (camino.Nodos[0] != 0)
                {
                    throw new Exception("El 0 debe ir en el 1er lugar");
                }
            }

            var familias = new List <Familia>();

            foreach (var familia in InstanciaProblema.Familias)
            {
                familias.Add(new Familia()
                {
                    id                = familia.id,
                    demanda           = familia.demanda,
                    nodos             = familia.nodos,
                    cantidad_visitada = 0,
                    cantidad_visitas  = familia.cantidad_visitas
                });
            }

            foreach (var camino in solucion.Caminos)
            {
                foreach (var nodo in camino.Nodos)
                {
                    foreach (var familia in familias)
                    {
                        if (familia.nodos.Contains(nodo))
                        {
                            familia.cantidad_visitada++;
                            break;
                        }
                    }
                }
            }

            foreach (var familia in familias)
            {
                if (familia.cantidad_visitas != familia.cantidad_visitada && !familia.nodos.Contains(0))
                {
                    throw new Exception("Familia no visitada");
                }
            }

            // Validar demandas
            foreach (var camino in solucion.Caminos)
            {
                var demanda_camino = camino.Nodos.Select(x => InstanciaProblema.Demanda[x]).Sum();
                if (demanda_camino > InstanciaProblema.capacidad)
                {
                    throw new Exception("La demanda del camino supera la capacidad máxima");
                }

                if (InstanciaProblema.capacidad - demanda_camino != camino.Capacidad)
                {
                    throw new Exception("La capacidad del camino es distinta a la que debería ser");
                }

                var costo_camino = Utilidades.CalcularCosto(celdas, camino.Nodos);
                if (Math.Round(costo_camino) != Math.Round(camino.Costo))
                {
                    throw new Exception(string.Format("Costos distintos, calculado: {0}, valor manual: {1}",
                                                      Math.Round(costo_camino), Math.Round(camino.Costo)));
                }
            }

            var costo = Utilidades.CalcularCosto(celdas, solucion.Caminos);

            if (Math.Round(costo) != Math.Round(solucion.Costo))
            {
                throw new Exception(string.Format("Costos distintos, calculado: {0}, valor manual: {1}",
                                                  Math.Round(costo), Math.Round(solucion.Costo)));
            }
        }
예제 #7
0
        bool Mover(SolucionFVRP solucion, decimal alfa)
        {
            var nro_camino_actual = solucion.Caminos.Count - 1;
            var camino_actual     = solucion.Caminos[nro_camino_actual].Nodos;
            var nodo_actual       = camino_actual[camino_actual.Count - 1];

            // No hace falta conocer los adyaecentes porque es un grafo completo, entonces hay que mirar todos.
            List <int> candidatos = new List <int>();

            for (int ady = 0; ady < InstanciaProblema.Distancias.Count; ady++)
            {
                if (ady == nodo_actual)
                {
                    continue;
                }

                // Se corrobora que podamos satisfacer la demanda del nodo adyacente.
                if (InstanciaProblema.Demanda[ady] > solucion.Caminos[nro_camino_actual].Capacidad)
                {
                    continue;
                }

                // Si el nodo adyacente fue utilizado no se puede volver a usar.
                if (solucion.NodosUtilizadosGrasp[ady])
                {
                    continue;
                }

                // La familia del nodo adyacente debe pertenecer a las familias pendientes.
                var familiaPendiente = solucion.FamiliasPendientes.Any(x => x.id == InstanciaProblema.FamiliaPorNodo[ady]);
                if (!familiaPendiente)
                {
                    continue;
                }

                candidatos.Add(ady);
            }

            if (candidatos.Count == 0)
            {
                return(false);
            }

            var max = decimal.MinValue;
            var min = decimal.MaxValue;

            foreach (var ady in candidatos)
            {
                if (nodo_actual != ady)
                {
                    if (InstanciaProblema.Distancias[nodo_actual][ady] > max)
                    {
                        max = InstanciaProblema.Distancias[nodo_actual][ady];
                    }
                    if (InstanciaProblema.Distancias[nodo_actual][ady] < min)
                    {
                        min = InstanciaProblema.Distancias[nodo_actual][ady];
                    }
                }
            }

            // Retorno temprano. Si entre todos los posibles candidatos, el mas cercano esta mas lejos que el
            // deposito, se vuelve con una probabilidad determinada.
            if (retornoTemprano && (solucion.Caminos[nro_camino_actual].Nodos.Count > 1) &&
                (InstanciaProblema.Distancias[nodo_actual][0] <= min) &&
                (new Random().Next(0, 100) < probRetornoTemprano))
            {
                return(false);
            }

            if (tipoAlfa == TipoAlfa.Aleatorio)
            {
                // En la version aleatoria, cambia el alfa por cada movimiento de manera random.
                alfa = ((decimal)(new Random().Next(0, 100)) / 100);
            }

            // Construccion de la lista de candidatos.
            List <int> RCL = new List <int>();

            foreach (var ady in candidatos)
            {
                if (InstanciaProblema.Distancias[nodo_actual][ady] <= (min + alfa * (max - min)))
                {
                    RCL.Add(ady);
                }
            }
            if (RCL.Count == 0)
            {
                throw new Exception("Error, la RCL no puede ser nunca vacia!");
            }

            // Seleccion random del candidato.
            Random rnd        = new Random();
            int    r          = rnd.Next(RCL.Count);
            int    vecino     = RCL[r];
            int    familia_id = InstanciaProblema.FamiliaPorNodo[vecino];

            // Se actualiza el camino y se mete el vecino en la solucion
            solucion.Caminos[nro_camino_actual].Costo     += InstanciaProblema.Distancias[nodo_actual][vecino];
            solucion.Caminos[nro_camino_actual].Capacidad -= InstanciaProblema.Demanda[vecino];
            solucion.Caminos[nro_camino_actual].Nodos.Add(vecino);
            solucion.NodosUtilizadosGrasp[vecino] = true;

            // Se actualizan las familias y si ya se visitaron todos los nodos, se remueven.
            for (var i = 0; i < solucion.FamiliasPendientes.Count; i++)
            {
                if (solucion.FamiliasPendientes[i].id == familia_id)
                {
                    solucion.FamiliasPendientes[i].nodos.Remove(vecino);
                    solucion.FamiliasPendientes[i].cantidad_visitada++;

                    if (solucion.FamiliasPendientes[i].cantidad_visitada == solucion.FamiliasPendientes[i].cantidad_visitas)
                    {
                        solucion.FamiliasPendientes.RemoveAt(i);
                    }

                    break;
                }
            }
            return(true);
        }
예제 #8
0
        public Solucion GRASPReactivo()
        {
            SolucionFVRP    mejorSolucion           = null;
            TimeSpan        mejorTiempo             = new TimeSpan();
            Stopwatch       sw                      = new Stopwatch();
            TipoTerminacion terminacion             = TipoTerminacion.Completa;
            int             iteracionesSinMejora    = 0;
            int             iteracionesHastaReincio = 0;
            int             solucionesMenorEpsilon  = 0;
            int             iteracionesActualizar   = 0;

            sw.Start();

            decimal mejorAlfa = 0;

            for (var i = 0; i < cantidadCorridas && iteracionesSinMejora < cantidadDeIteracionesSinMejora; i++)
            {
                SolucionFVRP solucion = null;

                // Reactive GRASP
                decimal alfa = (decimal)0.0;
                if (!promedioSolCompleto)
                {
                    // Se hace una primera corrida con cada alfa para empezar a completar la tabla promedioSoluciones.
                    for (int j = 0; j < 10; j++)
                    {
                        alfa = (decimal)alfas[j];
                        SolucionFVRP solParcial = GolosoAleatorio(alfa);
                        RealizarBusquedaLocal(solParcial);
                        solucionesPorAlfa[j] = new List <decimal>();
                        solucionesPorAlfa[j].Add(solParcial.Costo);
                        if (solucion == null || solucion.Costo > solParcial.Costo)
                        {
                            solucion  = solParcial;
                            mejorAlfa = alfa;
                        }
                    }
                }
                else
                {
                    // A partir de que se cargan para todo alfa, arrancamos el algoritmo posta.
                    if (iteracionesActualizar >= cantidadIteracionesActualizarPromedio && mejorSolucion != null)
                    {
                        decimal   sum         = 0;
                        decimal[] normalizado = new decimal[10];
                        for (int j = 0; j < 10; j++)
                        {
                            decimal promedioSolucionAlfa = solucionesPorAlfa[j].Sum() / solucionesPorAlfa[j].Count;
                            normalizado[j] = (decimal)Math.Pow((double)(mejorSolucion.Costo / promedioSolucionAlfa), delta);
                            sum           += normalizado[j];
                        }
                        for (int j = 0; j < 10; j++)
                        {
                            probabilidad[j] = normalizado[j] / sum;
                        }
                        iteracionesActualizar = 0;
                    }
                    else
                    {
                        iteracionesActualizar++;
                    }

                    var random = ((decimal)(new Random().Next(0, 100)) / 100);
                    var acum   = (decimal)0.0;
                    var actual = -1;
                    while (acum < random)
                    {
                        actual++;
                        acum += probabilidad[actual];
                    }

                    if (actual == -1)
                    {
                        actual = 0;
                    }

                    alfa = (decimal)alfas[actual];

                    SolucionFVRP solParcial = GolosoAleatorio(alfa);
                    RealizarBusquedaLocal(solParcial);
                    if (solucion == null || solucion.Costo > solParcial.Costo)
                    {
                        solucion  = solParcial;
                        mejorAlfa = alfa;
                    }

                    solucionesPorAlfa[actual].Add(solParcial.Costo);
                }

                // En la primer iteracion se completa... es horrible como me esta quedando, lo se.
                if (!promedioSolCompleto)
                {
                    promedioSolCompleto = true;
                }

                ValidarSolucion(solucion);

                if (mejorSolucion == null || mejorSolucion.Costo > solucion.Costo)
                {
                    sw.Stop();
                    decimal porcentajeDeMejora = (decimal)100;
                    if (mejorSolucion != null)
                    {
                        porcentajeDeMejora = 100 - ((solucion.Costo * 100) / mejorSolucion.Costo);
                    }
                    mejorSolucion = solucion;
                    mejorTiempo   = sw.Elapsed;
                    sw.Start();
                    iteracionesSinMejora = 0;
                    if (porcentajeDeMejora < epsilon)
                    {
                        if (solucionesMenorEpsilon > cantidadSolucionesMenorEpsilon)
                        {
                            terminacion = TipoTerminacion.Epsilon;
                            break;
                        }
                        solucionesMenorEpsilon++;
                    }
                }
                else
                {
                    iteracionesSinMejora++;
                }

                iteracionesHastaReincio++;
            }

            if (iteracionesSinMejora >= cantidadDeIteracionesSinMejora)
            {
                terminacion = TipoTerminacion.IteracionesSinMejora;
            }

            return(new Solucion()
            {
                SolucionFVRP = mejorSolucion,
                MejorAlfa = mejorAlfa,
                MejorTiempo = mejorTiempo,
                TipoTerminacion = terminacion.ToString()
            });
        }