private static IEnumerable <Chromosome <TGene, TFitness> > GetImprovment(MutateChromosomeDelegate newChild,
                                                                                 GenerateParentDelegate generateParent)
        {
            var bestParent = generateParent();

            yield return(bestParent);

            while (true)
            {
                var child = newChild(bestParent);
                if (bestParent.Fitness.CompareTo(child.Fitness) > 0)
                {
                    continue;
                }
                if (child.Fitness.CompareTo(bestParent.Fitness) <= 0)
                {
                    bestParent = child;
                    continue;
                }

                yield return(child);

                bestParent = child;
            }

            // ReSharper disable once IteratorNeverReturns
        }
        private static IEnumerable <Chromosome <TGene, TFitness> > GetImprovement(MutateChromosomeDelegate newChild,
                                                                                  GenerateParentDelegate generateParent, int?maxAge = null)
        {
            var bestParent = generateParent();
            var parent     = bestParent;

            yield return(bestParent);

            var historicalFitnesses = new List <TFitness> {
                bestParent.Fitness
            };

            while (true)
            {
                var child = newChild(parent);
                if (parent.Fitness.CompareTo(child.Fitness) > 0)
                {
                    if (maxAge == null)
                    {
                        continue;
                    }

                    parent.Age++;
                    if (parent.Age < maxAge)
                    {
                        continue;
                    }

                    var index = historicalFitnesses.BinarySearch(child.Fitness);
                    if (index < 0)
                    {
                        index = ~index;
                    }
                    var difference        = historicalFitnesses.Count - index;
                    var proportionSimilar = (double)difference / historicalFitnesses.Count;
                    var exp = Math.Exp(-proportionSimilar);
                    if (Rand.Random.NextDouble() < exp)
                    {
                        parent = child;
                        continue;
                    }

                    bestParent.Age = 0;
                    parent         = bestParent;
                    continue;
                }

                if (parent.Fitness.CompareTo(child.Fitness) == 0)
                {
                    child.Age = parent.Age + 1;
                    parent    = child;
                    continue;
                }

                child.Age = 0;
                parent    = child;
                if (bestParent.Fitness.CompareTo(child.Fitness) >= 0)
                {
                    continue;
                }

                bestParent = child;
                historicalFitnesses.Add(bestParent.Fitness);
                yield return(bestParent);
            }

            // ReSharper disable once IteratorNeverReturns
        }
        private static Chromosome <TGene, TFitness> Crossover(IReadOnlyList <TGene> parentGenes, int index,
                                                              IList <Chromosome <TGene, TFitness> > parents, GetFitnessDelegate getFitness, CrossoverDelegate crossover,
                                                              MutateChromosomeDelegate mutate, GenerateParentDelegate generateParent)
        {
            var donorIndex = Rand.Random.Next(0, parents.Count);

            if (donorIndex == index)
            {
                donorIndex = (donorIndex + 1) % parents.Count;
            }
            var childGenes = crossover(parentGenes, parents[donorIndex].Genes);

            if (childGenes == null)
            {
                // parent and donor are indistinguishable
                parents[donorIndex] = generateParent();
                return(mutate(parents[index]));
            }

            var fitness = getFitness(childGenes);

            return(new Chromosome <TGene, TFitness>(childGenes, fitness, Strategy.Crossover));
        }
        private static IEnumerable <Chromosome <TGene, TFitness> > GetImprovement(
            StrategyDelegate newChild, GenerateParentDelegate generateParent, int?maxAge, int poolSize, int?maxSeconds)
        {
            var watch      = Stopwatch.StartNew();
            var bestParent = generateParent();

            if (maxSeconds != null && watch.ElapsedMilliseconds > maxSeconds * 1000)
            {
                throw new SearchTimeoutException(bestParent);
            }

            yield return(bestParent);

            var parents = new List <Chromosome <TGene, TFitness> > {
                bestParent
            };
            var historicalFitnesses = new List <TFitness> {
                bestParent.Fitness
            };

            while (parents.Count < poolSize)
            {
                var parent = generateParent();
                if (maxSeconds != null && watch.ElapsedMilliseconds > maxSeconds * 1000)
                {
                    throw new SearchTimeoutException(parent);
                }

                if (parent.Fitness.CompareTo(bestParent.Fitness) > 0)
                {
                    yield return(parent);

                    bestParent = parent;
                    historicalFitnesses.Add(parent.Fitness);
                }

                parents.Add(parent);
            }

            var lastParentIndex = poolSize - 1;
            var pIndex          = 1;

            while (true)
            {
                if (maxSeconds != null && watch.ElapsedMilliseconds > maxSeconds * 1000)
                {
                    throw new SearchTimeoutException(bestParent);
                }

                pIndex = pIndex > 0 ? pIndex - 1 : lastParentIndex;
                var parent = parents[pIndex];
                var child  = newChild(parent, pIndex, parents);
                if (parent.Fitness.CompareTo(child.Fitness) > 0)
                {
                    if (maxAge == null)
                    {
                        continue;
                    }

                    parent.Age++;
                    if (parent.Age < maxAge)
                    {
                        continue;
                    }

                    var index = historicalFitnesses.BinarySearch(child.Fitness);
                    if (index < 0)
                    {
                        index = ~index;
                    }
                    var difference        = historicalFitnesses.Count - index;
                    var proportionSimilar = (double)difference / historicalFitnesses.Count;
                    var exp = Math.Exp(-proportionSimilar);
                    if (Rand.Random.NextDouble() < exp)
                    {
                        parents[pIndex] = child;
                        continue;
                    }

                    bestParent.Age  = 0;
                    parents[pIndex] = bestParent;
                    continue;
                }

                if (parent.Fitness.CompareTo(child.Fitness) == 0)
                {
                    child.Age       = parent.Age + 1;
                    parents[pIndex] = child;
                    continue;
                }

                child.Age       = 0;
                parents[pIndex] = child;
                if (bestParent.Fitness.CompareTo(child.Fitness) >= 0)
                {
                    continue;
                }

                bestParent = child;
                historicalFitnesses.Add(bestParent.Fitness);
                yield return(bestParent);
            }

            // ReSharper disable once IteratorNeverReturns
        }