• 遗传算法


    100个透明三角形拼成的Firefox

    这是个真实的故事。

    从前在海岸边有一群扇贝在悠哉游哉地生活繁衍着。它们自然是衣食不愁,连房子也有了着落。它们担忧的只有一件事:每隔一段时间,总有一个人来挖走它们之中的一部分。当然啦,挖回去干什么这大家都知道。但扇贝们不知道的是,这人的家族图腾是Firefox的图标,所以他总是选择那些贝壳花纹长得比较不像Firefox图标的扇贝。

    这种状况持续了好几十万代。大家应该也猜到扇贝们身上发生什么事情了:它们的贝壳上都印着很像Firefox图标的图案。

    可能有些读者会说:你这不是糊弄我们么,Firefox才有多少年历史,你就搞了个几十万代的扇贝?

    确有其事,但是这些扇贝不是真实的,它们在我电脑的内存里边生活。它们是一个遗传算法程序的一部分,这个程序的目的就是用100个半透明三角形把Firefox的图标尽可能像地画出来。

    什么是遗传算法呢?

    简单地说,遗传算法是一种解决问题的方法。它模拟大自然中种群在选择压力下的演化,从而得到问题的一个近似解。

    在二十世纪五十年代,生物学家已经知道基因在自然演化过程中的作用了,而他们也希望能在新出现的计算机上模拟这个过程,用以尝试定量研究基因与进化之间的关系。这就是遗传算法的滥觞。后来,有人将其用于解决优化问题,于是就产生了遗传算法。

    那么,具体来说,在计算机里边是怎么模拟进化过程的呢?

    我们还是以开头提到的程序为例。

    首先,我们知道,生物个体长什么样子很大程度上是由染色体上的基因决定的。同样,如果我们把100个半透明三角形组成的东西看成一个生物个体的话(为了说话方便,称为扇贝吧),我们也可以说它的样子是由这些三角形的具体位置和颜色决定的。所以,我们可以把一个一个的半透明三角形看作是这些扇贝的“基因”。而组成扇贝的这100个基因就组成了每个扇贝个体的“染色体”(chromosome)。

    从下面的图可以大约看出来这些基因是怎么决定扇贝的样子的(为了观看方便,我们只画出其中五个三角形):

    基因组成的个体

    然后,扇贝们除了生活,当然还要繁衍后代。生物界中的繁衍无非就是父母的基因组合产生新的个体,而在这个程序里边我们当然也这么办:选择两个原有的扇贝,然后从这两个扇贝的染色体中随机选取一共100个基因组成新个体的染色体。如图所示:(仍然是将扇贝看成是五个三角形组成的)

    为了产生新的基因,使基因的种类更多样化,在组合的时候,新的扇贝的基因有一定的概率发生变异。也就是说,其中的一个透明三角形的位置或者颜色会随机改变,如图(仍然是五个三角形……我偷工减料……):

    扇贝基因的变异

    其次,为了使扇贝的样子向Firefox图标靠近,我们要给它们加上一点选择压力,就是文章开头故事中提到的那个人的行动:在每一代把最不像Firefox的扇贝淘汰出去,同时也给新的个体留下生存的空间。怎么评价这个扇贝像不像Firefox呢?最直接的方法就是一个一个像素比较,颜色相差得越多就越不像。这个评价的函数叫做“适应函数”,它负责评价一个个体到底有多适应我们的要求。

    在淘汰的过程中,为了便于编程,我们通常会在淘汰旧个体和产生新个体的数目上进行适当的调整,使种群的大小保持不变。淘汰的作用就是使适应我们要求的个体存在的时间更长,从而达到选择的目的。

    最后,在自然界中,种群的演化是一个无休止的过程,但程序总要停下来给出一个结果。那么,什么时候终止演化输出结果呢?这就要订立一个终止条件,满足这个条件的话程序就输出当前最好的结果并停止。最简单的终止条件就是,如果种群经过了很多代(这里的“很多”是一个需要设定的参数)之后仍然没有显著改变适应性的变异的话,我们就停止并输出结果。我们就用这个终止条件。

    好了,现在是万事俱备只欠东风了。定义好基因,写好繁衍、变异、评价适应性、淘汰和终止的代码之后,只需要随机产生一个适当大小的种群,然后让它这样一代代的繁衍、变异和淘汰下去,到最后终止我们就会获得一个惊喜的结果:(这回是完整的了,图片下的数字表示这个扇贝是第几代中最好的)

    怎么样?虽说细节上很欠缺,但是也算是不错了。要不,你来试试用100个透明三角形画一个更像的?就是这样的看上去很简单的模拟演化过程也能解决一些我们这些有智慧的人类也感到棘手的问题。

    实际上,在生活和生产中,很多时候并不需要得到一个完美的答案;而很多问题如果要得到完美的答案的话,需要很大量的计算。所以,因为遗传算法能在相对较短的时间内给出一个足够好能凑合的答案,它从问世伊始就越来越受到大家的重视,对它的研究也是方兴未艾。当然,它也有缺点,比如说早期的优势基因可能会很快通过交换基因的途径散播到整个种群中,这样有可能导致早熟(premature),也就是说整个种群的基因过早同一化,得不到足够好的结果。这个问题是难以完全避免的。

    其实,通过微调参数和繁衍、变异、淘汰、终止的代码,我们有可能得到更有效的算法。遗传算法只是一个框架,里边具体内容可以根据实际问题进行调整,这也是它能在许多问题上派上用场的一个原因。像这样可以适应很多问题的算法还有模拟退火算法、粒子群算法、蚁群算法、禁忌搜索等等,统称为元启发式算法(Meta-heuristic algorithms)。

    另外,基于自然演化过程的算法除了在这里说到的遗传算法以外,还有更广泛的群体遗传算法和遗传编程等,它们能解决很多棘手的问题。这也从一个侧面说明,我们不一定需要一个智能才能得到一个构造精巧的系统。

    无论如何,如果我们要将遗传算法的发明归功于一个人的话,我会将它归功于达尔文,进化论的奠基人。如果我们不知道自然演化的过程,我们也不可能在电脑中模拟它,更不用说将它应用于实际了。

    向达尔文致敬!

    100个半透明三角形组成的达尔文肖像

    遗传算法求解问题的过程如下:

    1. 首先生成一组初始的候选解群体(假设为M个候选解个体),称为第0代;
    2. 计算群体中各个候选解的适应值;3. 如果有候选解满足算法终止条件,算法终止,否则继续4;4. 根据交配概率,将候选解群体中的个体随机两两配对,进行交配操作以生成新的候选解;5. 根据变异概率,对4中生成的候选解群中的每个个体进行变异操作;6. 使用选择机制形成新一代候选解;转2。从上面的算法过程中,我们可以知道,用遗传算法来求解问题有四个基本要素:1候选解的表示方式;2适应值的定义及度量方法;3算法的控制参数与变量;4算法终止准则。
    候选取解的表示方式,最简单的就是采用定长二进制编码。如我们可以将十进制的40转换成二进制的串,(40)10=(101000)2,反过来就可以将一个二进制串解码为一个十进制整数。
    适应值的定义及度量方法与要解决的问题有关,通常用目标函数来评估候选取解的优劣。
    算法的控制参数与变量。通常,我们把每一代中的候选解个数M称为群体规模,群体规模M在整个算法中一般是不变的一个常数。遗传操作主要是杂交和变异两个算 子,并有其相应的概率参数(Pc:杂交概率,Pm变异概率)来进行控制。遗传算法求解问题时,并不保证能找到满足问题要求的解,所以,还要设定算法的最大 迭代次数(或称为代数)。
    算法终止准则一般有:找到了满足问题的解;候选取群体已收敛于某一点;算法已达到了设定的代数等。
    这样,一个简单遗传算法的框架可非形式化地表示如下:Simple_Genetic_algorithm()
    {
    t:=1; /*变量t表示迭代代数*/
    初始化候选解群体Population(t);
    计算各个解的适应值;
    do while(终止条件不满足)
    {
    随机地将群中的个体两两配对,进行交配操作;
    执行变异操作;
    利用选择机制形成下一代候选取:Population(t+1):=Selection(Population(t));
    t:=t+1;
    }
    }
    三、 交配、变异及选择
    我们以二进制的表示方式来解释遗传算子及选择机制。
    在自然生物界,一个生物个体的特征通常被认为是由其父辈遗传下来的,当 然也可能有部分变异。而遗传是由DNA来决定的。我们现在将一个候选解看成为生物个体中的一条DNA(假设该种生物的遗传物质只有一条DNA,并且不存在 性别差异,即任意两个个体都可以进行交配),DNA链上的基因只有两种类型,可以分别将其定义为0或1。交配操作就是将两个父辈串随机从同一个位置分成两 段,然后进行相应段的交换,生成两个相应的子女。变异算子相对就要简单些,从个体串中随机将某一位按变异概率进行翻转(0翻转为1,1翻转为0,即所谓的基因变)。
    在实际算法中,双亲进行交配操作是由交配概率Pc来控制,交配点的选择也是随机产生的。变异操作由变异概率进行控制。在群体规模为M的算法中,由M个个体 的父辈群通过交配生成M个后代,M个后代再经过变异操作又生成M个子代,这样就要从M个父辈和M个子代中选择M个个体来形成下一代解群体。简单遗传算法就 从M个子代中选择M个个体来生成下一代。借鉴达尔文的进化论的观点,适者生存,不适应者被淘汰,那么,适应值高的个体就应该有更高的机会生存下来,而适应 值很低的个体就被淘汰。也就是说,适应值高的个体应该有更多的机会来繁殖多个后代,适应值低的个体就没有机会繁殖后代。
    在遗传算法中,采用按比选择的机制来形成下一代。设整个解群的平均适应值为f avg,则一个适应值为 f I的个体将被分配到f i/ f avg个后代。
    这样,高出平均适应值的个体将会获得多于一个的子女,而低于平均适应值的个体最多只能得到一个后代.
    简单遗传算法采用一种称为"轮转法"的方法来实现按比例选择机制。在一个圆中,整个圆的弧度角为2л。根据适应值,每个个体串f I在圆中分配这一个扇区(如图1),扇区弧度的大小为(2лf i/f avg)。所有扇区分配后,只要在0到2л之间产生一个随机数,该随机数落在哪个扇区内,则该个体将被选中一次,有期望获得繁殖以产生后代。在群体规模为M的算法中,只要重复地产生M个这样的随机数,就可以从经过遗传操作生成的M个子代中选择出M个体来形成下一代(当然,有些个体一次都可能不会被选中,而 有些个体被重复选中多次,下一代中就出现了多个同样的父辈)。
    四、 一个例子
    我们以一个极简单的函数优化的例子来说明遗传算法的工作过程。
    设函数f(x)=x2,x为整数且0≤x≤15,求Max(f(x))。由于0≤x≤15,所以一个解个体采用二进制编码时,只要4位长度就可以了(因为(15)10=(1111)2)。我们直接将x2作为个体x的适应值,适应值越大,解的满意度就越高。
    解的群体规模M=4;交配概率Pc=1.0;变异概率Pm=0.01。选择机制采用"轮转法。
    整个算法共执行了五代,在第五代中找到了一个满足要求的解。整个过程中,只有第三代中的S4的第二位发生了变异操作(Pm=0.01是个很小的值)。第一 代的结果中,S3的适应值为0而被淘汰,S4则被选择两次。所以第二代中S4的个体出现了两个。同样地,后续代中,S4被选择两次,相应的S1及S3被淘 汰。
     
    此文是转的
  • 相关阅读:
    python全栈-Day 4
    5个步骤实现软件质量的快速提升
    如何选择正确的静态应用安全测试(SAST)解决方案?
    安全工具箱必备技术之静态分析安全测试(SAST)
    精彩回顾:2020年自动化软件测试质量峰会
    怎样才能明智地利用代码覆盖率来最大限度地提高测试效率?
    软件开发你不可不知的那些事:如何有效减轻风险和质量债务?
    敏捷开发中不为人知的小秘密,你是否深有同感?
    当AI遇上API测试 — 敏捷开发已迎来革新时代!
    面对行业分析家和敏捷专家都认可的API测试,我们为什么会望而却步?
  • 原文地址:https://www.cnblogs.com/liangzh/p/2694791.html
Copyright © 2020-2023  润新知