遗传算法详解


已剪辑自: https://zhuanlan.zhihu.com/p/33042667

大自然有种神奇的力量,它能够将优良的基因保留下来,从而进化出更加强大、更加适合生存的基因。遗传算法便基于达尔文的进化论,模拟了自然选择,物竞天择、适者生存,通过N代的遗传、变异、交叉、复制,进化出问题的最优解。遗传算法看似神奇,但实现思路却较为简单。本文先跟大家介绍遗传算法的基本思想,然后用遗传算法来解决一个实际问题,最后给出遗传算法的代码实现和解析。废话不多说,现在就开始吧~

遗传算法

在开始之前,我们先来了解下遗传算法中的几个概念。

概念1:基因和染色体

在遗传算法中,我们首先需要将要解决的问题映射成一个数学问题,也就是所谓的“数学建模”,那么这个问题的一个可行解即被称为一条“染色体”。一个可行解一般由多个元素构成,那么这每一个元素就被称为染色体上的一个“基因”。

比如说,对于如下函数而言,[1,2,3]、[1,3,2]、[3,2,1]均是这个函数的可行解(代进去成立即为可行解),那么这些可行解在遗传算法中均被称为染色体。

3x+4y+5z<100

这些可行解一共有三个元素构成,那么在遗传算法中,每个元素就被称为组成染色体的一个基因。

概念2:适应度函数

在自然界中,似乎存在着一个上帝,它能够选择出每一代中比较优良的个体,而淘汰一些环境适应度较差的个人。那么在遗传算法中,如何衡量染色体的优劣呢?这就是由适应度函数完成的。适应度函数在遗传算法中扮演者这个“上帝”的角色。

遗传算法在运行的过程中会进行N次迭代,每次迭代都会生成若干条染色体。适应度函数会给本次迭代中生成的所有染色体打个分,来评判这些染色体的适应度,然后将适应度较低的染色体淘汰掉,只保留适应度较高的染色体,从而经过若干次迭代后染色体的质量将越来越优良。

概念3:交叉

遗传算法每一次迭代都会生成N条染色体,在遗传算法中,这每一次迭代就被称为一次“进化”。那么,每次进化新生成的染色体是如何而来的呢?——答案就是“交叉”,你可以把它理解为交配。

交叉的过程需要从上一代的染色体中寻找两条染色体,一条是爸爸,一条是妈妈。然后将这两条染色体的某一个位置切断,并拼接在一起,从而生成一条新的染色体。这条新染色体上即包含了一定数量的爸爸的基因,也包含了一定数量的妈妈的基因。

img

那么,如何从上一代染色体中选出爸爸和妈妈的基因呢?这不是随机选择的,一般是通过轮盘赌算法完成。

在每完成一次进化后,都要计算每一条染色体的适应度,然后采用如下公式计算每一条染色体的适应度概率。那么在进行交叉过程时,就需要根据这个概率来选择父母染色体。适应度比较大的染色体被选中的概率就越高。这也就是为什么遗传算法能保留优良基因的原因。

染色体i被选择的概率 = 染色体i的适应度 / 所有染色体的适应度之和

概念4:变异

交叉能保证每次进化留下优良的基因,但它仅仅是对原有的结果集进行选择,基因还是那么几个,只不过交换了他们的组合顺序。这只能保证经过N次进化后,计算结果更接近于局部最优解,而永远没办法达到全局最优解,为了解决这一个问题,我们需要引入变异。

变异很好理解。当我们通过交叉生成了一条新的染色体后,需要在新染色体上随机选择若干个基因,然后随机修改基因的值,从而给现有的染色体引入了新的基因,突破了当前搜索的限制,更有利于算法寻找到全局最优解。

概念5:复制

每次进化中,为了保留上一代优良的染色体,需要将上一代中适应度最高的几条染色体直接原封不动地复制给下一代。

假设每次进化都需生成N条染色体,那么每次进化中,通过交叉方式需要生成N-M条染色体,剩余的M条染色体通过复制上一代适应度最高的M条染色体而来。

遗传算法的流程

通过上述概念,相信遗传算法的大致原理你已经了解,下面我们将这些概念串联起来,介绍遗传算法的执行流程。

  • 在算法初始阶段,它会随机生成一组可行解,也就是第一代染色体。
  • 然后采用适应度函数分别计算每一条染色体的适应程度,并根据适应程度计算每一条染色体在下一次进化中被选中的概率(这个上面已经介绍,这里不再赘述)。

上面都是准备过程,下面正式进入“进化”过程。

  • 通过“交叉”,生成N-M条染色体;
  • 再对交叉后生成的N-M条染色体进行“变异”操作;
  • 然后使用“复制”的方式生成M条染色体;

到此为止,N条染色体生成完毕!紧接着分别计算N条染色体的适应度和下次被选中的概率。

这就是一次进化的过程,紧接着进行新一轮的进化。

究竟需要进化多少次?

每一次进化都会更优,因此理论上进化的次数越多越好,但在实际应用中往往会在结果精确度和执行效率之间寻找一个平衡点,一般有两种方式。

1. 限定进化次数

在一些实际应用中,可以事先统计出进化的次数。比如,你通过大量实验发现:不管输入的数据如何变化,算法在进化N次之后就能够得到最优解,那么你就可以将进化的次数设成N。

然而,实际情况往往没有那么理想,往往不同的输入会导致得到最优解时的迭代次数相差甚远,这是你可以考虑采用第二种方式。

2. 限定允许范围

如果算法要达到全局最优解可能要进过很多很多很多次的进化,这极大影响系统的性能。那么我们就可以在算法的精确度和系统效率之间寻找一个平衡点。我们可以事先设定一个可以接收的结果范围,当算法进行X次进化后,一旦发现了当前的结果已经在误差范围之内了,那么就终止算法。

但这种方式也有个缺点,有些情况下可能稍微进化几次就进入了误差允许范围,但有些情况下需要进化很多很多很多很多次才能进入误差允许范围。这种不确定性导致算法的执行效率不可控。

所以,究竟选择何种方式来控制算法的迭代次数,这需要你根据具体的业务场景合理地选择。这里无法给出普世的方式,需要你自己在真实的实践中找到答案。

采用遗传算法解决负载均衡调度问题

算法都是用来解决实际问题的,到此为止,我想你对遗传是算法已经有了个全面的认识,下面我们就用遗传算法来解决一个实际问题——负载均衡调度问题。

假设有N个任务,需要负载均衡器分配给M个服务器节点去处理。每个任务的任务长度、每台服务器节点(下面简称“节点”)的处理速度已知,请给出一种任务分配方式,使得所有任务的总处理时间最短。

数学建模

拿到这个问题后,我们首先需要将这个实际问题映射成遗传算法的数学模型。

任务长度矩阵(简称:任务矩阵)

我们将所有任务的任务长度用矩阵tasks表示,如:

Tasks={2,4,6,8}

那么,tasks[i]中的i表示任务的编号,而tasks[i]表示任务i的任务长度。

节点处理速度矩阵(简称:节点矩阵)

我们将所有服务器节点的处理速度用矩阵nodes表示,如:

Nodes={2,1}

那么,nodes[j]中的j表示节点的编号,而nodes[j]表示节点j的处理速度。

任务处理时间矩阵

任务矩阵Tasks节点矩阵Nodes确定下来之后,那么所有任务分配给所有节点的任务处理时间都可以确定了,我们用矩阵timeMatrix表示,它是一个二维数组:

1 2
2 4
3 6
4 8

timeMatrix[i][j]表示将任务i分配给节点j处理所需的时间,它通过如下公式计算:

timeMatrix[i][j] = tasks[i]/nodes[j]

染色体

通过上文我们知道,每次进化都会产生N条染色体,每一条染色体都是当前问题的一个可行解,可行解由多个元素构成,每个元素称为染色体的一个基因。下面我们就用一个染色体矩阵来记录算法每次进化过程中的可行解。

一条染色体的构成如下:

chromosome={1,2,3,4}

一条染色体就是一个一位数组,一位数组的下标表示任务的编号,数组的值表示节点的编号。那么chromosome[i]=j的含义就是:将任务i分配给了节点j。

上面的例子中,任务集合为Tasks={2,4,6,8},节点集合为Nodes={2,1},那么染色体chromosome={3,2,1,0}的含义是:

  • 将任务0分配给3号节点
  • 将任务1分配给2号节点
  • 将任务2分配给1号节点
  • 将任务3分配给0号节点

适应度矩阵

通过上文可知,在遗传算法中扮演者“上帝”角色的是适应度函数,它会评判每一条染色体的适应度,并保留适应度高的染色体、淘汰适应度差的染色体。那么在算法实现时,我们需要一个适应度矩阵,记录当前N条染色体的适应度,如下所示:

adaptability={0.6, 2, 3.2, 1.8}

adaptability数组的下标表示染色体的编号,而adaptability[i]则表示编号为i的染色体的适应度。

在负载均衡调度这个实例中,我们将N个任务执行总时长作为适应度评判的标准。当所有任务分配完后,如果总时长较长,那么适应度就越差;而总时长越短,则适应度越高。

选择概率矩阵

通过上文可知,每次进化过程中,都需要根据适应度矩阵计算每一条染色体在下一次进化中被选择的概率,这个矩阵如下所示:

selectionProbability={0.1, 0.4, 0.2, 0.3}

矩阵的下标表示染色体的编号,而矩阵中的值表示该染色体对应的选择概率。其计算公式如下:

selectionProbability[i] = adaptability[i] / 适应度之和

遗传算法的实现

上述一切知识点铺垫完成之后,接下来我们就可以上代码了,相信Talk is cheap, show you the code!

/**
 * 遗传算法
 * @param iteratorNum 迭代次数
 * @param chromosomeNum 染色体数量
 */
function gaSearch(iteratorNum, chromosomeNum) {
    // 初始化第一代染色体
    var chromosomeMatrix = createGeneration();

    // 迭代繁衍
    for (var itIndex=1; itIndex<iteratorNum; itIndex++) {
        // 计算上一代各条染色体的适应度
        calAdaptability(chromosomeMatrix);

        // 计算自然选择概率
        calSelectionProbability(adaptability);

        // 生成新一代染色体
        chromosomeMatrix = createGeneration(chromosomeMatrix);

    }
}

代码一来,一切都清晰了,似乎不需要过多的解释了。 上面是遗传算法最主要的框架,其中的一些细节封装在了一个个子函数中。在理解了遗传算法的原理后,我想代码不需要我作过多的解释了吧~完整的代码在我的Github上,欢迎Star。

结果展示

img

上述算法一共进行了100次进化,每次进化都会生成100条染色体。图中的横坐标表示进化次数,而纵坐标表示任务执行时间。 从图中我们可以看到,当进化约20次的时候,算法渐渐收敛于最优解。

写在最后

完整的代码在我的Github上,欢迎下载,欢迎Star!

代码中包含三个文件:

  • ga.html:展示的页面
  • GA.js:遗传算法的完整代码
  • common.js:通用的JS代码

各位大佬直接打开ga.html即可查看算法执行结果。也欢迎各位关注我的个人公众号,不定期分享不正经程序员的心路历程。

  • 7
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
粒子群算法PSO入门代码火经典案例求Ackley函数附-PSO.zip 本帖最后由 当当的花生 于 2016-7-30 20:09 编辑 回帖获得更多 粒子群算法 遗传算法前面有人讲了,我来讲讲PSO。 1)先看看百度百科解释: 粒子群算法,也称粒子群优化算法(Particle Swarm Optimization),缩写为 PSO, 是近年来由J. Kennedy和R. C. Eberhart等[1] 开发的一种新的进化算法。PSO 算法属于进化算法的一种,和模拟退火算法相似,它也是从随机解出发,通过迭代寻找最优解,它也是通过适应度来评价解的品质,但它比遗传算法规则更为简单,它没有遗传算法的“交叉” 和“变异” 操作,它通过追随当前搜索到的最优值来寻找全局最优。这种算法以其实现容易、精度高、收敛快等优点引起了学术界的重视,并且在解决实际问题中展示了其优越性。粒子群算法是一种并行算法。 2) 什么? 看不懂? 我来通俗解释: 粒子群算法是生物学家研究鸟类捕食创造的,把一只鸟比作成一个粒子,设想一个有20只秃鹫(粒子)的群体吧,秃鹫相互独立具有个体特征但又相互协助体现群体特征,现在我就是这20只中的一只好了(人丑),我现在和小伙伴去 觅食(找非洲野牛的尸体),假设我是一只老婆在家里孵蛋所以我得很认真找食物的秃鹫,每时每刻我都在记录我周围中最可能有猎物的地方,并以这个依据(设为依据一)在下一刻立即调整速度矢量(有大小方向)来趋向我上一时刻发现的最有可能有尸肉的地方。 假如我飞了一小时,上面说到我每时每刻都在记录最有当刻最有可能有肉的地方,那么在这一个小时的记录中肯定有一个最可能有肉的地方,这个地方就是依据二了,我有种往这个移动的趋势。 前面都是我的个鸟行为,我是一只善于观察和沟通并成功让酋长漂亮女儿当我老婆的鸟,所以群里其他秃鹫找到了一个小尸体就会立即告诉我,那我就看谁发现的小尸肉最有分量了,并有一个往这个地方移动的趋势,这是依据三。 再加一些假设以尽可能地模仿一个群体,1)每时每刻有随机从3只傻鸟中抽出一只鸟让他分心,在下一刻瞎移动位置,假设其他秃鹫和我一样机智并且有才华(不太可能),好了有了这些,经过2个小时的觅食,我们这十只鸟最终飞到了同一地点:一头最大野牛尸体旁边。这就是粒子群算法啦,显然是一个找大尸肉的优化算法。 3)来举个经典栗子,求Ackley函数的最小值。 什么很好求?可不是,这个函数的局部最小值太多了,一不小心就掉坑了,函数是这样的: ackley函数.png MATLAB画图如下: 图像.png 画图代码: %经典函数 Ackley clear,clc,close all; x1=-5:0.01:5; x2=-5:0.01:5; for i=1:1001     for j=1:1001         %目标函数         z=-20*exp^2 x2^2)/2))-exp)) cos))/2 20 2.71289;     end end [x,y]=meshgrid; figure mesh xlabel ylabel zlabel复制代码 这么多局部最小值,那么怎么用PSO求最小值呢? 少说废话,先上MATLAB代码: 1)定义函数: function y = fun y=-20*exp^2 x^2)/2))-exp)) cos))/2 20 2.71289;   复制代码 2)PSO求解: 要先定义函数哦 clear,clc,close all; %参数初始化 %粒子群算法中的俩个参数 c1 = 0.1; %惯量因子 c2 = 0.1; maxg=400; %进化次数 移动400回啊 sizepop=20; %总群规模 20只鸟啊 %初始速度和总群上下边界值 Vmax=1; Vmin=-1; %速度范围 上帝让我飞这么快啊 popmax=5; popmin=-5; %粒子范围 我不能飞到老王的领地啊 %%产生初始粒子和速度,上帝说要有鸟,就有了鸟 for i= 1:sizepop pop=popmax*rands ; %初始种群 V=rand; %初始速度 fitness=fun); %染色体的适应度 end %%找最好的染色体,最大的小肉块啊 [bestfitness,bestindex]=min; zbest=pop; %全局最佳 gbest=pop; %个体最佳 fitnessgbest=fitness; %个体最佳适应度 fitnesszbest=bestfitness; %全局最佳适应度 %%迭代寻优,我要找俩百回啊,老婆在家孵蛋啊 for i=1:maxg for j = 1:sizepop %速度更新,要找最好吃的肉啊 V = V c1*rand*-pop) c2*rand*); V>popmax))=popmax; V0.99 k=ceil; pop=rand; end %适应度值,函数最小的点(鸟) fitness=fun); %个体最优更新,我找到最好吃的肉要实时追踪啊 if fitness < fitnessgbest gebest = pop; fitnessgbest = fitness; end %群体最优更新,我是一只善于观察和沟通并成功让酋长漂亮女儿当我老婆的鸟 if fitness < fitnesszbest zbest = pop; fitnesszbest = fitness; end end yy = fitnesszbest; end %结果分析,20个鸟到了同一个地方,来啊互相伤害啊 plot% title]); grid on xlabel; ylabel; %结果输出 zbest %最佳个体,(点的位置) fitnesszbest %最优值(函数最小值)复制代码 说明:且把一个点(x,y)当成我这只鸟的位置,把这个点代到Ackley函数里得到一个值并记录下来。第一,我移动十次以后十次里有一个位置函数值最小(函数最小就是我们要的尸肉啊),那么我总有种往这个位置移动的趋势,第二,20个点带到函数我是不是得有个最小的,那我就又有种往这个点移动的趋势。 V = V c1*rand*-pop) c2*rand*); 而上面的速度更新公式巧妙的结合了上面俩点啊,精彩绝伦啊 c1,c2是个巧妙的常数,选大了搜索范围广,选小了局部搜索强啊 砖家现在在研究动态调整这俩个惯性因子啊 3)运行结果: 适应度.png 这里的适应度其实就是 把20个点带进函数的那个最小值的变化呀 最小值最后是接近真实最小值0啊,好吃的肉啊 结果: 360反馈意见截图16751106316927.png 每次运行结果都不是相同的哦,pso最终得到函数在(0.1070,0.0199)得到最小值0.0694.成功避开所有菊部最优值十分接近真实的在(0,0),最小值0 的结果。 啊我是一只神奇的秃鹫。第一次认真的回答啊,来啊点赞啊,来啊智能算法有什么: 算法.png ----Mr.Dang 转载注明出处,侵删 回复 支持 获得更多 M文件如下:

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小熊coder

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值