package problema; import jmetal.core.Problem; import jmetal.core.Solution; import jmetal.core.SolutionSet; import jmetal.core.Variable; import jmetal.encodings.solutionType.IntSolutionType; import jmetal.encodings.solutionType.PermutationSolutionType; import jmetal.encodings.solutionType.ZeroPermutationSolutionType; import jmetal.encodings.variable.Permutation; import jmetal.encodings.variable.ZeroPermutation; import jmetal.operators.mutation.MutationFactory; import jmetal.util.JMException; import jmetal.util.NonDominatedSolutionList; import jmetal.util.PseudoRandom; import jmetal.util.comparators.ObjectiveComparator; import problema.datos.Datos; import problema.datos.LlenadoInicial; import problema.datos.Velocidad; import java.io.*; import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; /** * Created by Cristiano on 27/07/2015. */ public class Problema extends Problem { public Datos datos; public int cantContenedores; public int cantCamiones; public int capCamiones; public int capCamionesAprox; //capCamiones + capCamiones / 2 public static TiempoComparator tiempoComparator = new TiempoComparator(); //Para instanciarlo sin datos. public Problema(){ numberOfObjectives_ = 2; //0: Min trayectoria, 1: Maximizar QoS solutionType_ = new IntSolutionType(this) ; problemName_ = "Recoleccion de basura"; } public Problema(Datos datos){ this.datos = datos; this.cantCamiones = datos.datosBasicos.cantidadCamiones; this.capCamiones = datos.datosBasicos.capacidadCamiones; this.capCamionesAprox = (this.capCamiones + this.capCamiones / 2); this.cantContenedores = datos.puntos.length; numberOfObjectives_ = 2; //0: Min trayectoria, 1: Maximizar QoS solutionType_ = new ZeroPermutationSolutionType(this) ; numberOfVariables_ = 1; length_ = new int[2]; length_[0] = this.capCamionesAprox * this.cantCamiones; length_[1] = this.cantContenedores-1; problemName_ = "Recoleccion de basura"; } //Devuelve un puntaje segun la cantidad de basura dejada en el contenedor public static double getPuntajeNoRecogido(double porcentaje){ double mult; if(porcentaje < 40){ mult = 1; } else if(porcentaje < 80){ mult = 2; } else { mult = 4; } return porcentaje * mult; } @Override public void evaluate(Solution solution) { //****************************************************************************************************************** //******** Variables que voy a necesitar ************ //****************************************************************************************************************** int[] variables = ((Permutation)solution.getDecisionVariables()[0]).vector_; int tiempoRecol = this.datos.datosBasicos.tiempoRecoleccionContenedor; float[][] distancias = this.datos.distancias; int[][] tiempos = this.datos.tiempos; int indice = 0; int indiceFinal = 0; LlenadoInicial[] b = this.datos.llenados; //basura inicial Velocidad[] velocidades = this.datos.velocidades; //velocidades de llenado //****************************************************************************************************************** //******** Restricciones y correccion de soluciones ************ //****************************************************************************************************************** boolean violoCond; int tiempoMax; int ultimoContenedor; //Itero hasta que la solucion sea factible do { violoCond = false; tiempoMax = 0; ultimoContenedor = -1; //La cantidad de basura recogida por cada camión no puede exceder su capacidad real //Para cada camión, luego que se tiene un 0 en su solución, todos los restantes valores a la derecha también deberán ser 0 for (int i = 0; i < this.cantCamiones; i++) { indice = i * this.capCamionesAprox; indiceFinal = indice + this.capCamionesAprox; try { //pongo todos los ceros lo mas a la derecha posible, y no dejo ceros entre medio. for (int j = indice; j < indiceFinal; j++) { if (variables[j] == 0) { //si tengo un cero, muevo de derecha a izquierda todo lo que no sea cero int indice3 = j + 1; //Hago una especie de selection sort, dejando todos los 0's a la derecha. while (indice3 < indiceFinal) { if (variables[indice3] != 0 ) { int temp = variables[j]; variables[j] = variables[indice3]; variables[indice3] = temp; break; } indice3++; } } } //La cantidad de basura recogida por cada camión no puede exceder su capacidad real, para esto se utiliza el % de basura recogido de cada contenedor y se valida que el total no supere su capacidad. double recogido = 0; //double para manejar fracciones. double sumaBasura; int tiempo = 0; int sumaTiempo = 0; int actual = 0; for(int j = indice; j < indiceFinal; j++){ int contenedor = variables[j]; if(contenedor != 0){ sumaTiempo = tiempo + tiempos[actual][contenedor]; sumaBasura = (b[contenedor].v + velocidades[contenedor].v * sumaTiempo) / 100; //divido entre 100 para utilizar fracciones de contenedores. if (recogido + sumaBasura <= this.capCamiones) { tiempo = sumaTiempo + tiempoRecol; recogido += sumaBasura; actual = contenedor; if(tiempo > tiempoMax){ tiempoMax = tiempo; ultimoContenedor = contenedor; } } else { violoCond = true; //Elimino el contenedor variables[j] = 0; } } } } catch (Throwable t) { throw new RuntimeException("ERROR: " + t.getMessage(), t); } } }while(violoCond); if(ultimoContenedor != -1){ tiempoMax+=tiempos[ultimoContenedor][0]; //Sumo tiempo desde el ultimo contenedor a la vuelta de ese camion. } //****************************************************************************************************************** //******** Calculo de funciones objetivo ************ //****************************************************************************************************************** double f1 = 0; // -- Primer funcion objetivo -- for(int i = 0; i < this.cantCamiones; i++){ indice = i * this.capCamionesAprox; indiceFinal = indice + this.capCamionesAprox; int actual = 0; try { for (int j = indice; j < indiceFinal; j++) { if (variables[j] != 0) { f1 += distancias[actual][variables[j]]; actual = variables[j]; } } if (actual != 0) { f1 += distancias[actual][0]; //Distancias del ultimo contenedor al origen } } catch (Throwable t){ throw t; } } // -- Segunda funcion objetivo --- //Veo que contenedores son recogidos double f2 = 0; boolean[] recogidos = new boolean[this.cantContenedores]; for(int i = 0; i < this.cantContenedores; i++){ recogidos[i] = false; } for(int i = 0; i < variables.length; i++) { int contenedor = variables[i]; if (contenedor != 0) { recogidos[contenedor] = true; } } //Por ultimo sumo todos los contenedores no recogidos for (int i = 1; i < this.cantContenedores; i++) { if(!recogidos[i]) { f2 += getPuntajeNoRecogido(b[i].v + tiempoMax*velocidades[i].v); } } solution.setObjective(0, f1); solution.setObjective(1, f2); } // evaluate /** * Genera archivos con datos de la solucion/es. * @param path * @param soluciones * @throws IOException * @throws JMException */ public void imprimirSolucion(String path, SolutionSet soluciones) throws IOException, JMException { int tiempoRecol = this.datos.datosBasicos.tiempoRecoleccionContenedor; // --- Calculo de primer funcion objetivo ---- float[][] distancias = this.datos.distancias; int[][] tiempos = this.datos.tiempos; LlenadoInicial[] b = this.datos.llenados; Velocidad[] velocidades = this.datos.velocidades; FileOutputStream fos = new FileOutputStream(path) ; OutputStreamWriter osw = new OutputStreamWriter(fos) ; BufferedWriter bw = new BufferedWriter(osw) ; //Elimino duplicados... SolutionSet hof = new NonDominatedSolutionList(); for(int i = 0; i < soluciones.size();i++) { hof.add(soluciones.get(i)); } //Tengo hardcodeado el acceso a la estructura ya que ya conozco la estructura de la solucion //Pero idealmente deberia ser generico usando los datos como cantidad de objetivos, cantidad de variables etc.. hof.sort(new ObjectiveComparator(0)); for (int i = 0; i < hof.size(); i++){ Solution s = hof.get(i); if (s.getOverallConstraintViolation() == 0.0) { int[] variables = ((Permutation)s.getDecisionVariables()[0]).vector_; for (int j = 0; j < variables.length; j++) { if (j % this.capCamionesAprox == 0 && j != 0) { bw.write(" | "); } else if (j != 0) { bw.write(" "); } //String id = this.datos.puntos[(int)s.getDecisionVariables()[j].getValue()].id; //bw.write(id); bw.write(String.valueOf(variables[j])); } bw.newLine(); bw.write(String.valueOf(s.getObjective(0))); bw.newLine(); bw.write(String.valueOf(s.getObjective(1))); bw.newLine(); int tiempoFinReal = 0; int ultimoContenedor = -1; //primero calculo tiempo de fin. for (int j = 0; j < this.cantCamiones; j++) { int indice = j * this.capCamionesAprox; int indiceFinal = indice + this.capCamionesAprox; try { int tiempo = 0; int sumaTiempo = 0; int actual = 0; for(int k = indice; k < indiceFinal; k++){ int contenedor = variables[k]; if(contenedor != 0){ sumaTiempo = tiempo + tiempos[actual][contenedor]; if(sumaTiempo > tiempoFinReal){ tiempoFinReal = sumaTiempo; ultimoContenedor = contenedor; } tiempo = sumaTiempo + tiempoRecol; actual = contenedor; } } } catch (Throwable t) { throw new RuntimeException("ERROR: " + t.getMessage(), t); } } bw.write("Tiempo real en que es recolectado ultimo contenedor en h: " + String.valueOf(tiempoFinReal/60.0/60.0)); bw.newLine(); //Agrego tiempo de recoleccion y de vuelta if(ultimoContenedor != -1){ tiempoFinReal+=tiempoRecol + tiempos[ultimoContenedor][0]; } bw.write("Tiempo real en que retorna el ultimo camion en h: " + String.valueOf(tiempoFinReal / 60.0 / 60.0)); bw.newLine(); bw.newLine(); boolean[] recogidos = new boolean[this.cantContenedores]; for(int j = 0; j < this.cantContenedores; j++){ recogidos[j] = false; } for (int j = 0; j < this.cantCamiones; j++) { int indice = j * this.capCamionesAprox; int indiceFinal = indice + this.capCamionesAprox; try { int tiempo = 0; int sumaTiempo = 0; int actual = 0; for(int k = indice; k < indiceFinal; k++){ int contenedor = variables[k]; if(contenedor != 0){ sumaTiempo = tiempo + tiempos[actual][contenedor]; bw.write(String.format("Contenedor [%s] Recogido: al %s %%", contenedor, b[contenedor].v + velocidades[contenedor].v * sumaTiempo)); tiempo = sumaTiempo + tiempoRecol; actual = contenedor; bw.newLine(); recogidos[contenedor] = true; } } } catch (Throwable t) { throw new RuntimeException("ERROR: " + t.getMessage(), t); } } for (int j = 1; j < this.cantContenedores; j++) { if(!recogidos[j]) { bw.write(String.format("Contenedor [%s] no recogido, dejado en %s %%", j,b[j].v + velocidades[j].v * tiempoFinReal)); bw.newLine(); } } bw.write("--------------------"); bw.newLine(); } } /* Close the file */ bw.close(); } /** * Devuelve la solucion de compromiso. Esto es, la que esta mas cerca de los mejores valores extremos. * @param soluciones * @return */ public Solution getSolucionDeCompromiso(SolutionSet soluciones){ double bestF1 = Double.MAX_VALUE; //Asigno peor valor que puede tomar f1 double bestF2 = Double.MAX_VALUE; //idem f2. Siempre se minimiza... for(int i = 0; i < soluciones.size();i++) { Solution sol = soluciones.get(i); if(sol.getObjective(0) < bestF1){ bestF1 = sol.getObjective(0); } if (sol.getObjective(1) < bestF2){ bestF2 = sol.getObjective(1); } } //tengo los mejores valores objetivos, busco la solucion que este mas cerca //En mi caso estoy buscando el x tal que min ||f(x)-z|| siendo z el optimo. //uso la normal euclidiana... : ||b - a||_2 = sqrt((b1-a1)^2 + (b2-a2)^2 + ....) //Ademas tengo que normalizar los valores antes para que de bien la distancia. double largo = Math.sqrt(Math.pow(bestF1, 2) + Math.pow(bestF2, 2)); bestF1 = bestF1 / largo; bestF2 = bestF2 / largo; Solution best = null; double bestNorm = Double.MAX_VALUE; for(int i = 0; i < soluciones.size();i++) { Solution sol = soluciones.get(i); double f1 = sol.getObjective(0); double f2 = sol.getObjective(1); double largof = Math.sqrt(Math.pow(f1, 2) + Math.pow(f2, 2)); f1 = f1 / largof; f2 = f2 / largof; double euclid = Math.sqrt( Math.pow(f1-bestF1, 2) + Math.pow(f2-bestF2, 2) ); if (euclid < bestNorm){ bestNorm = euclid; best = sol; } } return best; } //Deforma una solucion public Solution deformarSolucion(Solution sol) throws JMException { int[] variables = ((Permutation)sol.getDecisionVariables()[0]).vector_; int indice; int indiceFinal; boolean eliminar = false; if(PseudoRandom.randDouble() <= 0.2) { eliminar = true; } for (int i = 0; i < this.cantCamiones; i++) { indice = i * this.capCamionesAprox; indiceFinal = indice + this.capCamionesAprox; try { //Por cada camion, deformo la mitad de sus contenedores for(int j = indice; j < indiceFinal; j++){ if(PseudoRandom.randDouble()<= 0.5) { int contenedor = variables[j]; //Una opcion, elimina el contenedor if(eliminar) { if (contenedor != 0) { variables[j] = 0; } } //La otra, mueve contenedores a otros camiones de derecha a izquierda else { if (contenedor != 0) { for (int z = variables.length - 1; z >= 0; z--) { if(z < indice || z >= indiceFinal) { int contenedor2 = variables[z]; if (contenedor2 == 0) { variables[j] = contenedor2; variables[z] = contenedor; break; } } } } } } } } catch (Throwable t) { throw new RuntimeException("ERROR: " + t.getMessage(), t); } } this.evaluate(sol); this.evaluateConstraints(sol); return sol; } //Devuelve una lista de soluciones greedy, incluyendo la original y deformadas. //cant1: cantidad de soluciones greedy public Solution[] getSolucionesGreedy(int cant) throws JMException { Solution[] res = new Solution[cant]; /** for(int i = 0; i < cant; i++){ Solution solucionGreedy = new Solution(this, new Variable[]{new ZeroPermutation(length_[0], length_[1])}); this.evaluate(solucionGreedy); this.evaluateConstraints(solucionGreedy); res[i] = solucionGreedy; } **/ double[] params = {0.0, 0.05, 0.1, 0.15, 0.20, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5}; ZeroPermutation[] permGreedys = new ZeroPermutation[params.length]; for(int i = 0; i < params.length; i++){ permGreedys[i] = MainGreedy.ejecutarGreedy(this.datos, params[i]); } HashMap parameters = new HashMap() ; parameters.put("probability", 1.0) ; for(int i = 0; i < cant; i++){ Solution solucionGreedy = null ; if (i < params.length) { solucionGreedy = new Solution(this, new Variable[]{new ZeroPermutation(permGreedys[i])}); } else { solucionGreedy = new Solution(this, new Variable[]{new ZeroPermutation(permGreedys[i % params.length])}); for(int j = 0; j < i; j++) { MutationFactory.getMutationOperator("ZeroPermBitFlipMutation", parameters).execute(solucionGreedy); } } this.evaluate(solucionGreedy); this.evaluateConstraints(solucionGreedy); res[i] = solucionGreedy; } return res; } } class TiempoComparator implements Comparator<int[]> { @Override public int compare(int[] o1, int[] o2) { int v1 = o1[0]; int v2 = o2[0]; if(v1 < v2){ return -1; } else if(v1 > v2){ return 1; } else { return 0; } } }