//Operador Genético de cruce de tipo one-point
        public void Cruce_unpunto(ref Cromosoma cromosoma2, out Cromosoma hijo1, out Cromosoma hijo2)
        {
            int punto = TheSeed.Next(1, Poblacion.numTrabajadores);

            hijo1 = new Cromosoma();
            hijo2 = new Cromosoma();
            CopiarInformacionCromosoma(hijo1);
            CopiarInformacionCromosoma(hijo2);

            int cont = 0;

            for (int i = 0; i < Poblacion.numTrabajadores; i++)
            {
                for (int j = 0; j < Poblacion.numPuestosDeTrabajo; j++)
                {
                    if (i < punto)
                    {
                        hijo1.TheArray.Add(TheArray[cont]);
                        hijo2.TheArray.Add(cromosoma2.TheArray[cont]);
                    }
                    else
                    {
                        hijo1.TheArray.Add(cromosoma2.TheArray[cont]);
                        hijo2.TheArray.Add(TheArray[cont]);
                    }
                    cont++;
                }
            }
        }
        //Se copia la información de un cromosoma a otro
        public void CopiarInformacionCromosoma(Cromosoma dest)
        {
            Cromosoma cromosoma = dest;

            cromosoma.Length = Length;
            cromosoma.TheMin = TheMin;
            cromosoma.TheMax = TheMax;
        }
 //Se copia la información y contenido de un cromosoma a otro
 public void CopiarCromosoma(out Cromosoma dest)
 {
     dest               = new Cromosoma();
     dest.Length        = Length;
     dest.TheMin        = TheMin;
     dest.TheMax        = TheMax;
     dest.FitnessActual = FitnessActual;
     for (int i = 0; i < Length; i++)
     {
         dest.TheArray.Add(TheArray[i]);
     }
 }
        //Ordena los cromosomas de la generación actual y actualiza el fitness total de dicha generación
        private void RankPopulation()
        {
            totalFitness = 0.0;
            for (int i = 0; i < PoblacionActual; i++)
            {
                Cromosoma cromosoma = (Cromosoma)Cromosomas[i];
                totalFitness += cromosoma.FitnessActual;
            }
            Cromosomas.Sort(new ComparadorCromosoma());
            double fitness = 0.0;

            tablaFitness.Clear();
            for (int j = 0; j < PoblacionActual; j++)
            {
                fitness += ((Cromosoma)Cromosomas[j]).FitnessActual;
                tablaFitness.Add(fitness);
            }
        }
        //Este constructor, genera la población inicial
        public Poblacion(ArrayList trab, ArrayList proc, int td)
        {
            //Se inicializan las variables principales de Población
            numTrabajadores     = trab.Count;
            numPuestosDeTrabajo = proc.Count;
            duracionTurno       = td;
            procesos            = proc;
            trabajadores        = trab;

            for (int i = 0; i < poblacionInicial; i++)
            {
                //Aquí se generan los cromosomas de la poblacion inicial
                Cromosoma cromosoma = new Cromosoma(numPuestosDeTrabajo * numTrabajadores, kMin, kMax);
                cromosoma.CalcularFitness();
                Cromosomas.Add(cromosoma);
            }
            //Se proceden a ordenar los cromosomas de menor a mayor fitness
            RankPopulation();
        }
        private static void Main(string[] args)
        {
            int       duracionTurno = 0;  //Indica la duracion total de un dia de trabajo en minutos
            ArrayList trabajadores  = new ArrayList();
            ArrayList procesos      = new ArrayList();

            //Se procede a leer la data inicial desde un archivo .csv
            leerDataEntrada(trabajadores, procesos, ref duracionTurno);
            StreamWriter reporte = new StreamWriter("reporte.txt");

            var watch = System.Diagnostics.Stopwatch.StartNew();

            //Se genera la población inicial
            int       generacion = 1; //Indica el numero de la generacion
            Poblacion poblacion  = new Poblacion(trabajadores, procesos, duracionTurno);

            //Se obtiene el mejor cromosoma de la poblacion inicial
            Cromosoma mejorCromosoma  = poblacion.obtenerMejorCromosoma();
            Cromosoma ultimoCromosoma = new Cromosoma();

            reporte.WriteLine("Población inicial");
            reporte.WriteLine("Mejor cromosoma");
            mejorCromosoma.mostrarCromosoma(reporte);

            int repetido = 0, i = 0;

            // Condicion de parada del algoritmo genetico
            while ((i < MAX_ITERACIONES) && (repetido < MAX_REPETIDOS))
            {
                reporte.WriteLine();
                reporte.WriteLine("Generación " + generacion);
                // Generar la siguiente generacion
                poblacion.SiguienteGeneracion();

                //Se obtiene el mejor cromosoma de la actual generacion
                Cromosoma nuevoCromosoma = poblacion.obtenerMejorCromosoma();

                //Se compara si el mejor cromosoma de la generacion actual es mejor que el mejor cromosoma historico
                if (nuevoCromosoma.FitnessActual > mejorCromosoma.FitnessActual)
                {
                    mejorCromosoma = nuevoCromosoma;
                }

                if (Math.Truncate(nuevoCromosoma.FitnessActual) == Math.Truncate(ultimoCromosoma.FitnessActual))
                {
                    //Si el mejor cromosoma de la generacion actual es igual al mejor cromosoma de la generacion anterior
                    repetido++;
                }
                else
                {
                    //Si el mejor cromosoma de la generacion actual no es igual al mejor cromosoma de la generacion anterior
                    repetido = 0;
                }
                generacion++;
                i++;

                // Se guarda el mejor cromosoma actual para tenerlo en cuenta en la siguiente generacion
                ultimoCromosoma = nuevoCromosoma;
                reporte.WriteLine("Mejor cromosoma:");
                nuevoCromosoma.mostrarCromosoma(reporte);
            }

            reporte.WriteLine();
            reporte.WriteLine("RESULTADOS");
            reporte.WriteLine("Mejor cromosoma global");
            mejorCromosoma.mostrarCromosoma(reporte);
            reporte.Close();

            // Se muestran las asignaciones correspondientes
            //Console.WriteLine("Asignaciones");
            //mejorCromosoma.mostrarAsignaciones();
            mejorCromosoma.exportarCSV();

            watch.Stop();
            var elapsedMs = watch.ElapsedMilliseconds;

            Console.WriteLine("Tiempo del algoritmo {0} segundos.", elapsedMs / 1000.0);
            Console.WriteLine("Fitness del mejor cromosoma {0}.", mejorCromosoma.FitnessActual);
            Console.WriteLine("Presione ENTER para continuar");
            Console.ReadLine();
        }
        //Esta función acciona el operador genético de cruce y de mutación
        public void Cruce(ArrayList cromosomas)
        {
            CromosomasResultantes.Clear();
            ArrayList CromosomasMadre = new ArrayList();
            ArrayList CromosomasPadre = new ArrayList();

            //Se procede a repartir equitativamente los cromosomas para generar los cruces posteriores
            repartirCromosomas(cromosomas, CromosomasMadre, CromosomasPadre);

            //Se proceden a realizar los cruces entre padre y madre escogiendo estos por el método de la ruleta
            for (int j = 0; j < CromosomasPadre.Count; j++)
            {
                Cromosoma hijo1;
                Cromosoma hijo2;
                int       ind1  = RouletteSelection(); //Indica el indice del cromosoma padre
                int       ind2  = RouletteSelection(); //Indica el indice del cromosoma madre
                Cromosoma padre = (Cromosoma)CromosomasPadre[ind1];
                Cromosoma madre = (Cromosoma)CromosomasMadre[ind2];

                //Dependiendo de la frecuencia de cruce, puede que se haga cruce o no
                if (Cromosoma.TheSeed.NextDouble() < frecuenciaCruce)
                {
                    //Se procede a accionar el operador genético de cruce de tipo uniforme
                    padre.Cruce_uniforme(ref madre, out hijo1, out hijo2);

                    //Se verifica que los cromosomas después del cruce sigan siendo validos en estructura
                    if (!hijo1.esValido())
                    {
                        padre.CopiarCromosoma(out hijo1);
                    }
                    if (!hijo2.esValido())
                    {
                        madre.CopiarCromosoma(out hijo2);
                    }
                }
                else
                {
                    padre.CopiarCromosoma(out hijo1);
                    madre.CopiarCromosoma(out hijo2);
                }

                // Dependiendo de la frecuencia de mutación, puede que se mute o no
                if (Cromosoma.TheSeed.NextDouble() < frecuenciaMutacion)
                {
                    // Se procede a accionar el operador genético de mutación
                    hijo1.Mutar_intercambio();
                    hijo2.Mutar_intercambio();

                    // Se verifica que los cromosomas después de la mutación sigan siendo validos en estructura
                    if (!hijo1.esValido())
                    {
                        padre.CopiarCromosoma(out hijo1);
                    }
                    if (!hijo2.esValido())
                    {
                        madre.CopiarCromosoma(out hijo2);
                    }
                }

                // Se agrupan los hijos para generar la siguiente generación
                CromosomasResultantes.Add(hijo1);
                CromosomasResultantes.Add(hijo2);
            }
        }
        //Devuelve el mejor cromosoma de la generación actual
        public Cromosoma obtenerMejorCromosoma()
        {
            Cromosoma valor = (Cromosoma)Cromosomas[PoblacionActual - 1];

            return(valor);
        }