遗传算法在C#中的实现

本文关键字:实现 遗传算法 | 更新日期: 2023-09-27 18:17:05

我最近开始使用C#,目前正在尝试实现一个GA版本来解决Schwefel的函数(请参阅下面的代码(。该代码是基于我构建的一个工作处理代码。

第一代(前100个人(似乎工作得很好,但在那之后,适应度函数得到了重复值。我肯定我错过了什么,但有人知道可能是什么问题吗?

    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];
        }
    }

遗传算法在C#中的实现

这是一个我认为性能良好的更新代码:

    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];
        }
    }
}

}

这是一个问题:

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

尝试

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

并且用封装阵列并允许简单的Add()InsertAt()RemoveAt()方法的List<Individual>替换Individual[]的所有实例。

PS。此外,常见的惯例是对所有方法和属性使用PascalCasing。

我认为最大的问题是您的select函数。

遗传算法的成功在很大程度上取决于选择正确的突变、评估和选择技术,尽管乍一看,你的选择函数似乎很容易扭曲分布,你只是根据相对位置(即Pop[0]<Pop[1](来扭曲它,但你没有考虑它们之间的差异。

在GA中,最好的个人拥有100.0的健身度和第二个拥有99.9的健身度与最好的个人有100.0的体能和第二个人拥有75.0的体能之间存在巨大差异,而你的选择函数完全忽略了这一事实。

正在发生的事情,你为什么看到重复的健身值,是因为你一次又一次地挑选大致相同的个体,使你的基因库停滞,并停滞在局部最小值(或最大值,无论你在寻找什么(。

如果你在寻找像Roullette这样的方法(http://en.wikipedia.org/wiki/Fitness_proportionate_selection)他们选择的概率是个人适合度除以总适合度的函数,根据他们的行为方式,在更多的人中分享被选中的"机会",尽管这种方法也可能被困在当地人身上,但它比你目前拥有的方法更不容易发生,这应该会大大促进你探索搜索空间。

TL;DR-选择功能不够好,因为它过于严重地扭曲了分布,并且只考虑了相对比较。

Random.next(int min,int max(将只生成最小值和最大值之间的整数。尝试(rnd.NextDouble(生成一个介于0和1之间的随机数。这就是我现在可以帮助的:(