C# 遗传算法求函数最大值

简介

最近做一个项目要用到人工智能,于是了解了一下神经网络和相关的常用算法,其中遗传算法(Genetic Algorithm,简称 GA)最为有趣,因为我喜欢自然和生物:)
遗传算法基本思想来自于达尔文的进化论和孟德尔的遗传学说,模拟自然选择的过程,生物群落在繁殖的过程中产生基因交叉和突变,优胜劣汰。总体来说是一种带有随机性的启发式算法,可以在整个问题空间搜索,逐步逼近最优解。更详细介绍有很多书籍、论文和网上的文章,这里不再赘述。

关键流程
1.产生初始种群
  上帝创造一批良莠不齐的生物
2.计算适应度
  生物展开生存竞争
3.根据适应度选择交叉
  嘿咻繁殖,越适应环境的产生下一代的概率越大
4.变异
  被雷劈变成超级生物,或者被蜘蛛咬变成蜘蛛侠,极小概率事件 
5.循环 2~4 的过程,直到满足退出条件

经典测试函数
《C# 遗传算法求函数最大值》

本例计算其在区间 [-1, 2] 上的最大值,采用实数编码、单点交叉、赌轮选择。

 

代码

三个主要类: Defs 包含常量定义, FunctionProblem 求解的问题, Program 启动算法主程序,为简单起见,就不考虑其通用性了。
class Defs { public static float CROSSOVER_RATE = 0.7F; // 交叉概率 public static float MUTATION_RATE = 0.001F; // 变异概率 public static int POP_SIZE = 100; // 种群大小 public static int DELTA_LENGTH = 5; // 解小数点后的位数 public static int X_LENGTH = DELTA_LENGTH + 2; // 整体长度 }

 

class FunctionProblem : IComparable<FunctionProblem>, ICloneable { private int min; // x 取值区间最小值 private int max; // x 取值区间最大值 public string Expr { get; set; } // 用字符串表示的解(即 x) public float Fitness { get; set; } // 适应度 public double Total { get; set; } // 函数值 public bool IsPositive { get; set; } // x 是否为正 private static Random rand = new Random(DateTime.Now.Millisecond); public FunctionProblem(int min, int max) { this.min = min; this.max = max; Expr = GetRandomExpr(Defs.DELTA_LENGTH); AssignFitness(); } // 随机生成一个 x public string GetRandomExpr(int length) { bool isEven = (rand.Next(0, 1000) % 2) == 0; double val = rand.NextDouble(); if (isEven) { val = val * max; IsPositive = true; } else { val = – val * min; IsPositive = false; } string s = val.ToString(); if (s.Length < Defs.X_LENGTH) { s = s.PadRight(Defs.X_LENGTH – s.Length, ‘0’); } else if (s.Length > Defs.X_LENGTH) { s = s.Substring(0, Defs.X_LENGTH); } return s; } // 计算适应度,直接以函数值为适应度 public void AssignFitness() { Total = Eval(); Fitness = (float)Total; } // 交叉 public void CrossOver(ref FunctionProblem chromo) { if (rand.NextDouble() > Defs.CROSSOVER_RATE) { return; } int pos = rand.Next(Expr.Length); string exprHead = Expr.Substring(0, pos); string exprTail = Expr.Substring(pos); string chromoHead = chromo.Expr.Substring(0, pos); string chromoTail = chromo.Expr.Substring(pos); Expr = exprHead + chromoTail; chromo.Expr = chromoHead + exprTail; } // 变异 public void Mutate() { if (rand.NextDouble() > Defs.MUTATION_RATE) { return; } char current; int val = rand.Next(0, 10); int pos = rand.Next(0, Expr.Length); int dotPos = Expr.IndexOf(‘.’); while (pos == dotPos) // is decimal point? if yes, gen another { pos = rand.Next(0, Expr.Length); } string exprHead = “”; if (pos > 0) { exprHead = Expr.Substring(0, pos); } else if (pos == 0) // simple to limit the x range { val = 0; } string exprTail = Expr.Substring(pos + 1); Expr = exprHead + val.ToString() + exprTail; } // 计算函数值 private double Eval() { double x = double.Parse(Expr); if (!IsPositive) { x = -x; } return x * Math.Sin(x * 10 * Math.PI) + 2.0D; } public int CompareTo(FunctionProblem obj) { return Fitness.CompareTo(obj.Fitness); } public object Clone() { return this.MemberwiseClone(); } }

 

class Program { private static Random rand = new Random(DateTime.Now.Millisecond); static void Main(string[] args) { String x = “”; // 搜索到的最优解–x float max = 0.0F; // 搜索到的最大值 int gen = 0; // 种群代数 float totalFitness = 0.0F; // 种群适应度和 List<FunctionProblem> pool = new List<FunctionProblem>(); List<FunctionProblem> newPool = new List<FunctionProblem>(); // 生成初始种群 Console.WriteLine(“gen: {0}”, gen); for (int i = 0; i < Defs.POP_SIZE; i++) { FunctionProblem chromo = new FunctionProblem(-1, 2); pool.Add(chromo); if (chromo.Total > max) { x = chromo.Expr; max = (float)chromo.Total; } } // 进化,迭代 while (gen < Defs.MAX_ALLOWABLE_GENERATIONS) { totalFitness = 0.0F; pool.Sort(); for (int i = 0; i < Defs.POP_SIZE; i++) { totalFitness += pool[i].Fitness; Console.WriteLine(“NO. {0}: x {1}{2} Total: {3}”, i, pool[i].IsPositive?”+”:”-“, pool[i].Expr, (float)pool[i].Total); } gen++; Console.WriteLine(“gen: {0}”, gen); newPool.Clear(); for (int i = 0; i < Defs.POP_SIZE; i += 2) { FunctionProblem c1 = SelectMember(totalFitness, pool); FunctionProblem c2 = SelectMember(totalFitness, pool); c1.CrossOver(ref c2); c1.Mutate(); c2.Mutate(); c1.AssignFitness(); if (c1.Total > max) { x = c1.Expr; max = (float)c1.Total; } c2.AssignFitness(); if (c2.Total > max) { x = c2.Expr; max = (float)c2.Total; } newPool.Add(c1); newPool.Add(c2); } pool.Clear(); pool.AddRange(newPool); } Console.WriteLine(“MAX: x = {0} ==> {1}”, x, max); Console.ReadLine(); } // 赌轮选择 private static FunctionProblem SelectMember(float totalFitness, List<FunctionProblem> list) { float slice = (float)rand.NextDouble() * totalFitness; float fitnessSoFar = 0.0F; int i; for (i = 0; i < list.Count; i++) { fitnessSoFar += list[i].Fitness; if (fitnessSoFar >= slice) { return (FunctionProblem)list[i].Clone(); } } return (FunctionProblem)list[list.Count – 1].Clone(); } }

 

多次运行发现当 x = 1.85 左右时约有最大值 3.85

 

运行参数

种群规模大则计算量大,但多样性高,不易陷入局部最优解

交叉概率小则容易早熟

变异概率大则容易变成随机算法,难以收敛

 

结论

遗传算法实现起来还是相当容易,但是参数选择方面比较困难,需要多实践。

    原文作者:遗传算法
    原文地址: https://blog.csdn.net/coldljy/article/details/5671569
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞