• 遗传算法学习笔记(3)


      学而不思则罔,思而不学则贻。 学习遗传算法这样貌似很神奇的东西,最困难的一点就是把理论知识转化为实际的程序,把头脑里的东西,弄成一个个实际的代码。这个距离有时候尽在咫尺,却挡住了很多人。至少我就被挡住了很多次。(说实话,知道遗传算法的概念已经很久了,但这么多年也没有真正动手过;就算是两天前,都想放弃过,给自己一个借口:反正我已经对遗传算法了解的很多了,理论也基本掌握了,这就差不多了,等以后真正需要再说吧)。我认为这里面有一个界限,那就是你真正去实现一个程序,哪怕这个程序是很简单的,甚至是很简陋的;但如果你完整的实现了,注意,是完整的实现。这或许就是一个质上的变化,至少表示你有能力把有一个概念转化为一个实际的项目。那么概念在头脑里萦绕的时候,你同时也知道如果用代码去实现,哪怕此时的代码是丑陋的,漏洞百出。我想说的是,写一个糟糕的程序,比灌一脑子完美的概念要好的多。

      还有一些朋友问学习这些有什么用?这个问题,我只能揣测地把自己体会说一下。首先,我觉得学习这些是对自己思维的一个很好的锻炼,扩展自己的思路。这在平常所做的代码工作中,很难有所突破。因为平时在项目的系统压力下,或者有时候是我们思维定势的影响,总是跳不出来某个圈子。而在一个完全陌生的东西前,一片漆黑地开始编程,或许会发现“原来还可以这样”。其次,遗传算法有很多的实际应用,公交调度,电力网规划等等,都是很重要的应用。

      最后说一下我自己的想法,工作中总是发现,但系统的规模增长到一定程度,随着逻辑越来越复杂,对项目的掌控越来越差,按下F5之前,总是要祈祷一下,担心莫名其妙的Bug张牙舞爪地跑出来。说实话,这个感觉很糟糕。我一直希望能够有一个良好的架构,一个解耦的架构,既可以良好地协作工作,也可以让我能集中精力到具体的细节单元,同时希望它们可以并行工作,在当前的多核越来越流行的情况下,能让自己的游戏以优秀的性能脱颖而出。我觉得遗传算法,神经网络,有限元这些有着化繁为简的特性,把一个复杂的系统问题,归结到了一个个单元(基因)这样的东西如果能够运用到游戏架构上来,会给开发者带来很大的好处,另外归结成一个个没有交叉的单元后,使用并行编程是很自然的事情,这就能使SilverlightGame的性能成倍地提高,这是很让人期待的。

      目前所做的东西都给予Silverlight来实现,虽然有时候是拿Silverlight但控制台来用的;但用SL可以让我以后扩展的时候更方便一些。

                                     ———————————— 自勉以及勉励很多和我一样的程序员朋友

      最开始选择了一个寻路的例子想实现,或许那个例子也不难,但尝试了一下,发现有些问题,因为做一个小人在迷宫中行走,至少要涉及到动画,地图敷设,甚至要做一个地图编辑器,碰撞...;这些东西和遗传算法并不是关系很大,但这些东西会牵扯很多的精力,让你很难集中精力注意在你的遗传算法上,我觉得这不是一个好主意,也建议大家在初涉新的知识时候,而且你对它把握不是很大,那么弄一个简单的,目的明确的系统,还是很明智的做法。

      我又开始尝试 对函数在区间 [a,b] 内的最大值的遗传算法求解。遗传算法是对问题参数的编码进行操作,而函数本身的参数正好是最好的编码对象。观察图形,会发现它是存在最优解和局部最优解的。(这个函数看上去很复杂,不过没有关系,它恰恰代表了,我们处理的很多问题可能更复杂,做一个不规则的物理碰撞,一个业务复杂的进销存规则,都可能比这个更复杂,但使用遗传算法恰恰是避免这种探究内部复杂机制,而采取的是探索的方法,这更具有通用性)

      我选择了一个更简单的函数 f(x) = x * x 来作为目标。对其在 00000 ~ 11111的区间内求最大值,适应度用来衡量个体对生存环境的适应程度。但适应度的选择是个不容易弄好的问题,有时候是要对适应度函数进行适当变换(可以进行线性变换或者指数变换,幂函数变换等等)。我这里简单的用函数值作为适应度函数,利用适应度函数取得被选择概率,然后应用选择方法,遗传算法中的选择方法很多,轮盘赌是一个常用的选择方法。

    在上面一篇文章也提到过轮盘赌选择法。

    具体的数据可以是这样的。

      童鞋们当然可以用这个思路来做赌轮算法,不过我使用了另外一种利用数组指针来模拟。长话短说吧,以下是全部的代码,说实话,把这样的东西放出来是要做好心理准备的,做好被拍倒的准备。因为这些东西确实很陌生,但请大家念我一腔的开源精神,共同学习。

    代码
    using System;
    using System.Net;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Ink;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Shapes;
    using System.Diagnostics;
    using System.Collections.Generic;
    using System.Text;
    using System.Threading;

    //----------------------------------------------------------------------//
    // 在一个区间内用遗传算法寻找某函数的最大值的练习,疏漏多多
    // 初学遗传算法,很痛苦,希望朋友们多交流切磋
    // 学习代码,随意张贴复制,敬请保留原签名
    // http://home.cnblogs.com/GameCode/
    // 2010.7.9 Copy By 向恺然 (博客园)
    //----------------------------------------------------------------------//

    namespace MaxValue
    {
    public class HandleGene
    {
    public Chromosome[] chromosomes; //种群
    public customFun myFun; //函数
    public int maxInt, minInt; //函数取值范围 【00000 ~ 11111】
    public int mm;

    public List<Chromosome> newChromo; //产生新种群的临时存放地点

    //_size 种群初始大小,这个值也很重要,太小不容易产生好的结果,太大计算代价高
    //有种说法是种群大小是染色体长度的2~3倍
    public HandleGene(int _size,int _max,int _min)
    {
    chromosomes
    = new Chromosome[_size];
    maxInt
    = _max;
    minInt
    = _min;
    myFun
    = MyFunction.SquareBinary;
    newChromo
    = new List<Chromosome>();
    InitGenomes();
    }

    public void Epoch()
    {
    //Match方法每次繁殖出来两个新染色体,所以进行种群数量一半的循环,就产生和种群数量一样的新种群
    for (int i = 0; i < chromosomes.Length / 2; i++)
    {
    Match();
    }
    int index = 0;
    foreach (Chromosome item in newChromo)
    {
    chromosomes[index]
    = item;
    index
    ++;
    }
    newChromo.Clear();
    UpdateGenome();
    }

    public void show()
    {
    foreach (Chromosome item in chromosomes)
    {
    Debug.WriteLine(
    "{0}:{1}:{2}", item.genesSt, item.genesInt, item.fitness);
    }
    }

    public void InitGenomes()
    {
    Chromosome chromosom;
    for (int i = 0; i < chromosomes.Length; i++)
    {
    chromosom
    = new Chromosome();
    //给一个不同的种子,可以得到不同的结果,更具有一般性
    chromosom.genesSt = GetGeneSt(i-10212);
    chromosom.fitness
    = myFun((double)Convert.ToInt32(chromosom.genesSt, 2));
    chromosom.genesInt
    = Convert.ToInt32(chromosom.genesSt,2);
    chromosomes[i]
    = chromosom;
    }
    }

    //这是最重要的一个方法,用赌轮选择找出概率最大的两个染色体,同时进行杂交或者变异
    public void Match()
    {
    int[] pa = new int[1000];
    int max = 0;
    int continueInt = 0;
    for (int j = 0; j < chromosomes.Length; j++)
    {
    int p = GetProbability(chromosomes[j].fitness);
    chromosomes[j].probability
    = (double)p / 100.0;
    if (p > max)
    {
    pa[
    999] = j;
    max
    = p;
    }
    for (int i = continueInt; i < p + continueInt; i++)
    {
    pa[i]
    = j;
    }
    continueInt
    += p;
    }
    Random ran
    = new Random(Chromosome.Seed);
    int point = ran.Next(0,1000);
    Debug.WriteLine(
    "{0}--{1}---{2}", Thread.CurrentThread.ManagedThreadId, point,Chromosome.Seed);

    Chromosome parent1
    = chromosomes[pa[point]];

    point
    = ran.Next(0, 1000);
    Debug.WriteLine(
    "{0}--{1}---{2}",Thread.CurrentThread.ManagedThreadId,point,Chromosome.Seed);

    Chromosome.Seed
    ++;

    Chromosome parent2
    = chromosomes[pa[point]];


    if (ran.NextDouble() < Chromosome.CrossoverRate)
    {
    string[] at = Crossover(parent1.genesSt, parent2.genesSt);
    parent1.genesSt
    = at[0];
    parent2.genesSt
    = at[1];
    }

    newChromo.Add(parent1);
    newChromo.Add(parent2);
    }

    public void UpdateGenome()
    {
    foreach (Chromosome item in chromosomes)
    {
    item.genesInt
    = Convert.ToInt32(item.genesSt, 2);
    item.fitness
    = myFun(item.genesInt);
    }
    }

    //杂交
    public string[] Crossover(string _a,string _b)
    {
    Random ran
    = new Random();
    int pos = ran.Next(0, Convert.ToString(maxInt, 2).Length);
    string sub1A = _a.Substring(0, pos);
    string sub2A = _a.Substring(pos, _a.Length - pos);

    string sub1B = _b.Substring(0, pos);
    string sub2B = _b.Substring(pos, _b.Length - pos);

    string newA = sub1A + sub2B;
    string newB = sub1B + sub2A;

    double t = ran.NextDouble();
    if (t < Chromosome.MutationRate)
    {
    Mutation(
    ref newA);
    Mutation(
    ref newB);
    }
    return new string[] {newA,newB };
    }

    //变异
    public void Mutation(ref string _gene)
    {
    Random ran
    = new Random();
    int pos = ran.Next(0, _gene.Length);
    string st = _gene.Substring(pos,1);
    string st2 = "";
    if (st == "0")
    {
    st
    = _gene.Remove(pos,1);
    st
    = st.Insert(pos, "1");
    }
    else
    {
    st
    = _gene.Remove(pos,1);
    st
    = st.Insert(pos, "0");
    }
    }

    public string GetGeneSt(int _randSeed)
    {
    StringBuilder stb
    = new StringBuilder();
    int len = Convert.ToString(maxInt, 2).Length;
    Random ran
    = new Random(_randSeed);
    for (int i = 0; i < len; i++)
    {
    int gene = ran.Next(0, 2);
    stb.Append(gene.ToString());
    }
    return stb.ToString();
    }

    public int GetProbability(double _fitness)
    {
    double sum = 0;
    foreach (Chromosome item in chromosomes)
    {
    sum
    += item.fitness;
    }
    int a = (int)((_fitness / sum)*1000);
    return a;
    }
    }

    public class Chromosome
    {
    public string genesSt;
    public int genesInt;
    public double fitness;

    public double probability;//被选中的概率

    public static int Seed;

    //大家可以尝试着改变数值看会有什么结果。
    //变异率
    public static double MutationRate = 0.01;
    //杂交率
    public static double CrossoverRate = 0.7;
    }

    public class MyFunction
    {
    //函数,这里选择了一个简单的平方函数,其实你选择其他函数也是一样的
    //遗传算法的好处也恰恰在这里,不需要十分精确地探寻问题的复杂内部机制
    public static double SquareBinary(double _x)
    {
    return _x * _x;
    }
    }

    public delegate double customFun(double _x);

    }

      

    代码
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Shapes;
    using System.Diagnostics;
    using System.Threading;

    namespace MaxValue
    {
    public partial class MainPage : UserControl
    {
    public MainPage()
    {
    InitializeComponent();

    HandleGene handle
    = new HandleGene(10, 31, 0);

    handle.show();

    for (int i = 0; i < 50; i++)
    {
    Debug.WriteLine(
    "----{0}------",i);
    handle.Epoch();
    handle.show();
    }
    }
    }
    }
  • 相关阅读:
    LINQ 详解
    oracle下查询的sql已经超出IIS响应时间
    IOC应用之 Ninject
    JSONP ---------跨域
    国内各大互联网公司相关技术站点2.0版 (集合腾讯、阿里、百度、搜狐、新浪、360等共49个)
    IO多路复用,以socket为例
    socket机制下实现的多用户与服务器交互
    在一个进程中定义多个线程
    基于tcp的socketserver,即tcp的多线程
    基于upd的socketserver,即udp的多线程
  • 原文地址:https://www.cnblogs.com/GameCode/p/1774606.html
Copyright © 2020-2023  润新知