简体   繁体   English

遗传算法在C#中的实现

[英]Genetic Algorithm implementation in C#

I've recently started working with C# and I'm currently trying to implement a version of GA to solve Schwefel's function (See code below). 我最近开始使用C#,目前正在尝试实现GA版本来解决Schwefel的功能 (请参见下面的代码)。 The code is based on a working Processing code that I built. 该代码基于我构建的有效的处理代码。

The first generation(first 100 individuals) seems to work fine but after that the fitness function gets repetitive values. 第一代(前100个人)工作正常,但之后适应度函数得到重复的值。 I'm sure I'm missing something here but does anyone know what might be the problem? 我确定我在这里遗漏了一些东西,但是有人知道这可能是什么问题吗?

    public void button21_Click(object sender, EventArgs e)
    {
        Population p;
        // populationNum = 100;
        p = new Population();
        int gen = 0;
        while (gen < 8000)
        {
            p.evolve();
        }
        ++gen;
    }

    //Class Genotype
    public partial class Genotype
    {
        public int[] genes;

        public Genotype()
        {
            genes = new int[2];
            for (int i = 0; i < genes.Length; i++)
            {
                Random rnd = new Random(int.Parse(Guid.NewGuid().ToString().Substring(0, 8), System.Globalization.NumberStyles.HexNumber));
                //Random rnd = new Random(0);
                int random = rnd.Next(256);
                genes[i] = (int)random;
            }
        }

        public void mutate()
        {
            //5% mutation rate
            for (int i = 0; i < genes.Length; i++)
            {
                Random rnd = new Random(int.Parse(Guid.NewGuid().ToString().Substring(0, 8), System.Globalization.NumberStyles.HexNumber));
                int random = rnd.Next(100);
                if (random < 5)
                {
                    //Random genernd = new Random();
                    int generandom = rnd.Next(256);
                    genes[i] = (int)generandom;
                }
            }
        }
    }

    static Genotype crossover(Genotype a, Genotype b)
    {
        Genotype c = new Genotype();
        for (int i = 0; i < c.genes.Length; i++)
        {
            //50-50 chance of selection
            Random rnd = new Random(int.Parse(Guid.NewGuid().ToString().Substring(0, 8), System.Globalization.NumberStyles.HexNumber));

            float random = rnd.Next(0, 1);
            if (random < 0.5)
            {
                c.genes[i] = a.genes[i];
            }
            else
            {
                c.genes[i] = b.genes[i];
            }
        }
        return c;
    }


    //Class Phenotype
    public partial class Phenotype
    {
        double i_x;
        double i_y;

        public Phenotype(Genotype g)
        {
            i_x = g.genes[0] * 500 / 256;
            i_y = g.genes[1] * 500 / 256;
        }

        public double evaluate()
        {
            double fitness = 0;
            fitness -= (-1.0*i_x * Math.Sin(Math.Sqrt(Math.Abs(i_x)))) + (-1.0*i_y * Math.Sin(Math.Sqrt(Math.Abs(i_y))));
            Console.WriteLine(fitness);
            return fitness;  
        }
    }

    //Class Individual
    public partial class Individual : IComparable<Individual>
    {
        public Genotype i_genotype;
        public Phenotype i_phenotype;
        double i_fitness;

        public Individual()
        {
            this.i_genotype = new Genotype();
            this.i_phenotype = new Phenotype(i_genotype);
            this.i_fitness = 0;
        }

        public void evaluate()
        {
            i_fitness = i_phenotype.evaluate();
        }

        int IComparable<Individual>.CompareTo(Individual objI)
        {
            Individual iToCompare = (Individual)objI;
            if (i_fitness < iToCompare.i_fitness)
            {
                return -1; //if I am less fit than iCompare return -1
            }
            else if (i_fitness > iToCompare.i_fitness)
            {
                return 1; //if I am fitter than iCompare return 1
            }

            return 0; // if we are equally return 0
        }
    }

    static Individual breed(Individual a, Individual b)
    {
        Individual c = new Individual();
        c.i_genotype = crossover(a.i_genotype, b.i_genotype);
        c.i_genotype.mutate();
        c.i_phenotype = new Phenotype(c.i_genotype);
        return c;
    }

    //Class Population
    public class Population
    {
        Individual[] pop;
        int populationNum = 100;

        public Population()
        {
            pop = new Individual[populationNum];
            for (int i = 0; i < populationNum; i++)
            {
                this.pop[i] = new Individual();
                pop[i].evaluate();
            }
            Array.Sort(this.pop);
        }

        public void evolve()
        {
            Individual a = select();
            Individual b = select();
            //breed the two selected individuals
            Individual x = breed(a, b);
            //place the offspring in the lowest position in the population, thus replacing the previously weakest offspring
            pop[0] = x;
            //evaluate the new individual (grow)
            x.evaluate();
            //the fitter offspring will find its way in the population ranks
            Array.Sort(this.pop);
            //rnd = new Random(0);
        }

        Individual select()
        {
            Random rnd = new Random(int.Parse(Guid.NewGuid().ToString().Substring(0, 8), System.Globalization.NumberStyles.HexNumber));
            float random = rnd.Next(0, 1);
            //skew distribution; multiplying by 99.999999 scales a number from 0-1 to 0-99, BUT NOT 100
            //the sqrt of a number between 0-1 has bigger possibilities of giving us a smaller number
            //if we subtract that squares number from 1 the opposite is true-> we have bigger possibilities of having a larger number
            int which = (int)Math.Floor(((float)populationNum - 1e-6) * (1.0 - Math.Pow(random, random)));
            return pop[which];
        }
    }

This an updated code that I think it performs well: 我认为这是一个更新的代码,性能良好:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Threading;

    namespace ConsoleApplication8
   {
    class Program
     {
       static Random random = new Random();

        static void Main(string[] args)
        {
        Population p;
        System.IO.StreamWriter file = new System.IO.StreamWriter("c:\\test.txt");
        int population = 100;
        p = new Population(file, population);

        int gen = 0;
        while (gen <= 1000)
        {
            p.evolve(file);
            ++gen;
        }
        file.Close();
    }

    public static double GetRandomNumber(double min, double max)
    {
        return (random.NextDouble() * (max - min)) + min;
        //return random.NextDouble() *random.Next(min,max);
    }

    //Class Genotype
    public class Genotype
    {
        public int[] genes;

        public Genotype()
        {
            this.genes = new int[2];
            for (int i = 0; i < genes.Length; i++)
            {
                this.genes[i] = (int)GetRandomNumber(-500.0, 500.0);
            }
        }

        public void mutate()
        {
            //5% mutation rate
            for (int i = 0; i < genes.Length; i++)
            {
                 if (GetRandomNumber(0.0, 100) < 5)
                 {
                    //Random genernd = new Random();
                    this.genes[i] = (int)GetRandomNumber(0.0, 256.0);
                 }
            }
        }
    }

    static Genotype crossover(Genotype a, Genotype b)
    {

        Genotype c = new Genotype();
        for (int i = 0; i < c.genes.Length; i++)
        {
            //50-50 chance of selection
            if (GetRandomNumber(0.0, 1) < 0.5)
            {
                c.genes[i] = a.genes[i];
            }
            else
            {
                c.genes[i] = b.genes[i];
            }
        }
        return c;
    }

    //Class Phenotype
    public class Phenotype
    {
        double i_x;
        double i_y;

        public Phenotype(Genotype g)
        {
            this.i_x = g.genes[0];
            this.i_y = g.genes[1];
        }

        public double evaluate(System.IO.StreamWriter file)
        {
            double fitness = 0;
            //fitness -= i_x + i_y;
            fitness -= (i_x*Math.Sin(Math.Sqrt(Math.Abs(i_x)))) + i_y*(Math.Sin(Math.Sqrt(Math.Abs(i_y))));
            file.WriteLine(fitness);
            return fitness;  
        }
    }

    //Class Individual
    public class Individual : IComparable<Individual>
    {
        public Genotype i_genotype;
        public Phenotype i_phenotype;
        double i_fitness;

        public Individual()
        {
            this.i_genotype = new Genotype();
            this.i_phenotype = new Phenotype(i_genotype);
            this.i_fitness = 0.0;
        }

        public void evaluate(System.IO.StreamWriter file)
        {
            this.i_fitness = i_phenotype.evaluate(file);
        }

        int IComparable<Individual>.CompareTo(Individual objI)
        {
            Individual iToCompare = (Individual)objI;
            if (i_fitness < iToCompare.i_fitness)
            {
                return -1; //if I am less fit than iCompare return -1
            }
            else if (i_fitness > iToCompare.i_fitness)
            {
                return 1; //if I am fitter than iCompare return 1
            }
            return 0; // if we are equally return 0
        }
    }

    public static Individual breed(Individual a, Individual b)
    {
        Individual c = new Individual();
        c.i_genotype = crossover(a.i_genotype, b.i_genotype);
        c.i_genotype.mutate();
        c.i_phenotype = new Phenotype(c.i_genotype);
        return c;
    }

    //Class Population
    public class Population
    {
        Individual[] pop;
        //int populationNum = 100;

        public Population(System.IO.StreamWriter file, int populationNum)
        {
            this.pop = new Individual[populationNum];

            for (int i = 0; i < populationNum; i++)
            {
                this.pop[i] = new Individual();
                this.pop[i].evaluate(file);
            }
            Array.Sort(pop);
        }

        public void evolve(System.IO.StreamWriter file)
        {
            Individual a = select(100);
            Individual b = select(100);
            //breed the two selected individuals
            Individual x = breed(a, b);
            //place the offspring in the lowest position in the population, thus  replacing the previously weakest offspring
            this.pop[0] = x;
            //evaluate the new individual (grow)
            x.evaluate(file);
            //the fitter offspring will find its way in the population ranks
            Array.Sort(pop);
        }

        Individual select(int popNum)
        {
            //skew distribution; multiplying by 99.999999 scales a number from 0-1 to  0-99, BUT NOT 100
            //the sqrt of a number between 0-1 has bigger possibilities of giving us a smaller number
            //if we subtract that squares number from 1 the opposite is true-> we have bigger possibilities of having a larger number
           int which = (int)Math.Floor(((float)popNum - 1E-6) * (1.0 - Math.Pow(GetRandomNumber(0.0, 1.0), 2)));
           return pop[which];
        }
    }
}

} }

I think the biggest issue is with your select function. 我认为最大的问题是您的选择功能。

The success of GA's depends a lot on picking the right Mutation, Evaluation and Selection techniques, although at first glance your selection function seems elegant to skew distribution, you're only skewing it based on relative position (ie Pop[0] < Pop[1]) but you're not taking into account how different they are from each other. 遗传算法的成功很大程度上取决于选择正确的突变,评估和选择技术,尽管乍一看您的选择功能似乎对分布偏斜比较优雅,但您只能根据相对位置对其进行倾斜(即Pop [0] <Pop [ 1]),但你没有考虑到他们彼此有什么不同。

In GA's there's a HUGE difference between having the best individual have 100.0 Fitness and the Second have 99.9 than the best have 100.0 and the second have 75.0 and your selection function completely ignores this fact. 在GA中,最佳个人的100.0健身度和第二个人的99.9高于最佳个人的100.0健身度和第二个75.0的差异,您的选择功能完全忽略了这一事实。

What is happening, why you see the repetitive fitness values, is because you're picking roughly the same individuals over and over, making your genetic pool stagnant and stalling in a local minimum (or maximum whatever you're looking for). 之所以会出现这种情况,是因为您一遍又一遍地选择了大致相同的个体,这使您的基因库停滞不前,并停滞在局部最小值(或最大值)。

If you look for a method like Roullette ( http://en.wikipedia.org/wiki/Fitness_proportionate_selection ) they pick the probability as a function of the individual fitness divided over the total fitness, sharing the 'chance' of being picked among more individuals depending on how they behave, although this method can also get trapped in locals, it far less prone to than what you currently have, this should give you a very good boost on exploring the search space. 如果您寻找类似Roullette( http://en.wikipedia.org/wiki/Fitness_proportionate_selection )的方法,他们会选择概率作为个体适应度除以总适应度的函数,在更多情况下共享被选择的“机会”取决于他们的行为方式,尽管这种方法也可能会被当地人所困,但它比您目前所拥有的方法要容易得多,这应该可以极大地促进您探索搜索空间。

TL;DR - The selection function is not good enough as it is skewing the distribution too harshly and is only taking into account relative comparisons. TL; DR-选择功能不够好,因为它过于严格地扭曲了分布,并且仅考虑了相对比较。

This is a problem: 这是个问题:

float random = rnd.Next(0, 1);   // returns an integer from 0 to 0 as a float
// Documentation states the second argument is exclusive

Try 尝试

float random = (float)rnd.NextDouble(); // rnd should be static, init'd once.

and replace all instances of Individual[] with List<Individual> which wraps an array and allows for easy Add() , InsertAt() and RemoveAt() methods. 并用List<Individual>替换所有InsertAt() Individual[]实例,后者包装一个数组,并允许简单的Add()InsertAt()RemoveAt()方法。

PS. PS。 Also common convention has it to use PascalCasing for all methods and properties. 同样常见的约定是,对所有方法和属性都使用PascalCasing

Random.next(int min,int max), will generate only integers between the min and max values. Random.next(int min,int max),将仅在最小值和最大值之间生成整数。 try the (rnd.NextDouble) to generate a random number between 0 and 1. this what i can help right now :) 尝试(rnd.NextDouble)生成一个介于0和1之间的随机数。这是我现在可以提供的帮助:)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM