演化算法

1、简介

演化算法,又称为进化算法(Evolutionary Algorithm)、进化计算(EvolutionaryComputing)或遗传算法(Genetic Algorithm),是一种元启发式(metaheuristic)方法(定义见:http://en.wikipedia.org/wiki/Metaheuristic)。前面谈到的模拟退火算法也是一种元启发式算法。二者的主要区别也是演化算法的主要特点:虽然它们都在解空间内进行搜索,但模拟退火算法是一种轨迹式(trajectory)方法,而演化算法是一种基于种群(population-based)的方法。

具体地说,前者在搜索解空间时是沿着某一条轨迹前进的,最终收敛到某个解的局部。每次迭代时只对一个解操作。而后者的搜索过程是从一个初始解的集合(称为初始种群)开始的,种群中的每一个解都沿着一定的轨迹搜索,每前进一步称为种群的进化,得到的解集称为种群的一代(generation)。这样便增加了在庞大解空间中找到最优解的概率。在种群进化时,种群中的解会发生变异、杂交和选择等操作从而生成新的解,构成新一代种群。如下面两图所示。

这个过程与自然界生物的进化很类似,生物总群的每一代都会有或多或少的变异发生,而不用个体之间的杂交也是很普遍的现象。变异和杂交所产生的新个体不一定会适应生存环境,这就是自然选择的过程:适应的个体存活下来,不适应的个体遭到淘汰,最后存活下来的个体组成新一代的种群。

                                                                                                     

2. 伪代码

一个演化算法的基本结果如下:

P <- GenerateInitialPopulation()

Evaluate(P)

while

termination conditions not metdo

P’ <- Recombine(P)

P’’ <- Mutate(P’)

Evaluate(P’’)

P <- Select(P’’ U P)

endwhile

注意,在循环中的Recombine和Mutate操作即对应杂交算子和变异算子,有时重组(Recombine)也称作交叉(crossover)。这两种操作是必需的,但是顺序不是固定的,即也可以先进行变异然后再交叉。选择(select)操作是演化的关键一步,这里采用的方式是综合交叉和变异产生的后代和当代个体,选择优势个体(评估函数较好)组成下一代种群。选择的依据就是每一个解的评估函数值(通过Evaluate计算)。另一种方案是仅从新个体中选择适当数量的个体作为下一代种群。

3.设计要素

设计一个演化算法有以下几点需要考虑:

a.  表示方式

一个演化算法或是算法中的算子是否有效很大程度上依赖于解的表示方式。有些算子对一种表示方式的解有很好地效果,而对另一种表示方式的解却是无效的甚至是无法实现的。例如,求解SAT问题时经常使用的一种算子是反位操作,即对每一个变量的布尔值进行取反。这时表达所有变量真值情况(一个解)的很自然的方式就是使用一个长度为n的二进制位向量(n是变量的个数),以1表示变量取true值,以0表示变量取false值。

然而,同样的算子却不能用于TSP问题。TSP问题要求找到旅行商的一个城市环游路线,使得耗费最小。如果使用整数标记了每座城市,那么城市标号的一个排列就是一个解,这也是一种很自然的表示方式。对这样的解进行变换的常用算子就是2-交换算子:随机交换两个城市的位置形成新的排列。显然,这种算子是二进制位向量表示无法实现的。

总之,解的表示方式会对算子的设计甚至整个算法的效果产生决定性影响,在设计时要具体问题具体分析。

b.  评估函数

评估函数最常见的选择就是优化目标。显然,如果每次选择新一代个体都使用导致较好优化目标的个体,那么最终的结果是令人满意的。但有时候也会使用其他标准作为评估函数(考虑种群多样性等其他因素,详见第4节)。设计评估函数的常识性指导原则是:最优解应该被赋予最优的评估值

c.  变化算子

变化算子是演化算法的基础。种群能够不断地演化最后接近最优个体得益于变化算子的作用。把算子分成交叉和变异两类也是来自自然界的灵感。自然界中的生物的染色体(解)上的基因(部分解)发生变异时,生物的性状通常会发生显著变化。从长远来看,这种变异是有利于生物体生存的。不同生物体之间的杂交会使双亲个体的优良性状遗传到后代个体中,也同样有利于生物的生存。这就是交叉和变异算子会有助于产生近似最优解得原因。

变化算子的设计通常是问题相关的。某一种算子对特定问题有好的结果,却不一定是通用的。例如,一种常用的变异算子是反序(inverse),它使一个解的随机一段子序列中的元素反序。如下图,解s1中两个间隔点之间的部分反序后得到的新解为s2。

S1: 2 5 6 |3 4 8 9| 1 7

S2: 2 5 6 |9 8 4 3| 1 7

这个算子适用TSP问题的原因是,TSP的一个解中可能含有违反几何原理的子路径存在,如下图,1,2,3,4四个城市之间的距离(耗费)可能有如图所示的关系。路径1->3->4->2->1显然要比路径1->4->3->2->1要短,而反序算子就可以打破类似后者的子路径。

然而,这个算子在其他问题如生产调度问题中不一定有效。

d. 选择

选择算子的基本方法就是选择评估函数较优的个体进入下一代种群。选择的过程可以是确定的,也可以是随机的。但是要保证一点:评估值越好,被选中的概率应该越大。

选择的具体方式有很多。例如,可以定义如下选择概率:

Pi定义了第i个个体被选择的概率,Fi是其评估函数值,分母是所有个体的评估函数值得平均值。如果某个个体的评估函数值大于或等于平均值,则确定被选中;如果某个个体的评估函数值小于均值,则以这个概率被选中。显然,评估函数值越大接近均值,越容易被选中(这里定义评估函数值较大为优)。

另一种方式是,对当前种群进行多次随机取样,每次从样本中选取一个或多个最优解,直到选择了足够多的解为止。

e. 初始解

初始种群对算法的结果有一定的影响。在解空间中,如果初始解距离最优解很近,那么进过有限几次迭代,种群就可以进化到较优的状态;如果初始解选择的不好,那么算法效果就会受影响。

4. 解的强化(intensification)和多样化(diversification)

在种群进化的过程中,解的变化主要有两个方向:强化和多样化。强化是指解的质量有越变越好的趋势。然而,如果一直沿着某个解或某些解的领域范围搜索,容易陷入局部最优(local optimal)的情况,导致过早收敛(prematureconvergence)。如下图所示,

解S1就是一个局部最优解,而显然解S2(可能也是局部最优)比它要好,而接下来的搜索如果不跳出S1的邻域就无法获得较为精确的近似解。多样化方向策略就是为了解决这个问题。简单地说,多样化就是尽量使得搜索范围均匀分布在解空间中,而不只是局限于某个小的范围内。看起来,解的强化和多样化是相互矛盾的两个进化方向,但是二者对于得到较优的解都是不可或缺的。因此,考虑两个方向的平衡显得尤为重要,这也是设计演化算法的关键所在。

在演化算法的变化算子中,交叉算子就是为了实现解的多样化。通过使不同解的一部分按照一定的规则进行重组,得到一些与原来的解有一定距离的新解,消除了解的局部最优性。而变异算子就是为了实现解的强化。通过对解本身进行一定程度的扰动,进行局部邻域的搜索,找到局部领域中质量较好的解。这两种算子的设计与特定问题紧密相关,没有百试不爽的算子可用。

5.实现举例

这里借助TSP问题来举一例,看一看演化算法的具体实现。

首先定义一些算法必备的变量,具体功能详见注释。其中反转率是指在实现变异算子反转操作时,被反转的城市范围所占整个解维度(城市数量)的比例。问题解的表示方式:一个解就是城市编号的一个排列。

    //种群的大小private int pSize;//初始种群private ArrayList<ArrayList<Integer>> population;//交叉后的种群private ArrayList<ArrayList<Integer>> crossPop;//变异后的种群private ArrayList<ArrayList<Integer>> mutatePop;//最大迭代次数private int maxIter;//反转率private double mrate;

接下来是算法的主体部分。整个演化过程执行一定的迭代次数,可以多次执行取平均值。在演化之前要生成一个初始种群。这里采用随机生成一个排列的方法生成一个解。

public void run() {//首先生成初始种群long start,end;double sumTime = 0;double sumDist = 0;int times = 50;while(times-- > 0){start = System.currentTimeMillis();initialPopulation();int count = 0;while (++count <= maxIter) {// 进行重组操作recombinePop();// 进行变异操作mutatePop();// 进行选择操作selectNextGen();}shortestDist = getOptimal();end = System.currentTimeMillis();sumDist += shortestDist;sumTime += end - start;}System.out.println("50 次平均结果:" + sumDist / 50 + "\t" + sumTime / 50);}
private void initialPopulation() {int c = 0;ArrayList<Integer> solution;while(++c <= pSize){solution = randomSolution();population.add(solution);}}private ArrayList<Integer> randomSolution() {// 随机产生一个解ArrayList<Integer> res = new ArrayList<Integer>();Random rand = new Random(System.nanoTime());ArrayList<Integer> seed = new ArrayList<Integer>();int j,index;for(j = 0; j < number; j++)seed.add(j);while(!seed.isEmpty()){index = rand.nextInt(seed.size());res.add(seed.get(index));seed.remove(index);}return res;}

第一步是重组操作,算子的思想是:选取一个切入点,保留第一个个体的从第一个城市到切入点城市的部分,剩下部分由第二个个体的城市无重复填满。两个个体随机从种群中选取。

private void recombinePop() {// 交叉思想:选取一个切入点 保留第一个个体的从第一个城市到切入点城市的部分 剩下部分由第二个个体的城市无重复填满crossPop = new ArrayList<ArrayList<Integer>>();int i,j,cut,m,n,c = 0;Random rand = new Random(System.nanoTime());int[] isUsed;while(++c <= pSize){isUsed = new int[number];for(int index : isUsed)index = 0;i = rand.nextInt(pSize);do j = rand.nextInt(pSize);while(i == j);ArrayList<Integer> newSol = new ArrayList<Integer>();cut = rand.nextInt(number);for(n= 0; n <= cut; n++){newSol.add(population.get(i).get(n));isUsed[population.get(i).get(n)] = 1;}for(m = 0; m < number; m++){if(isUsed[population.get(j).get(m)] == 0){newSol.add(population.get(j).get(m));}}crossPop.add(newSol);}//population = (ArrayList<ArrayList<Integer>>)crossPop.clone();}

第二步是变异操作,算子的思想是:对每一个解简单执行inverse算子。

private void mutatePop() {// 变异思想:对每一个解简单地进行inverse算子 反转的部分解大小为mrate*numbermutatePop = new ArrayList<ArrayList<Integer>>();int size = (int)mrate * number;Random rand = new Random(System.nanoTime());int start,end;for(ArrayList<Integer> sol : crossPop){start = rand.nextInt(number - size + 1);end = start + size - 1;sol = inverseMove(sol, start, end);mutatePop.add(sol);}}

其中inverse算子的实现如下:

private ArrayList<Integer> inverseMove(ArrayList<Integer> sol, int cut1, int cut2) {// TODO Auto-generated method stubint i = cut1;int j = cut2;while(i < j){int job1 = sol.get(i);int job2 = sol.get(j);sol.remove(j);sol.add(j, job1);sol.remove(i);sol.add(i, job2);i++;j--;}return sol;}

最后一步是选择操作,算子的思想是:在原来种群和经过变异和交叉操作后的种群中选择较好的个体进入下一轮。这里的评估函数就是旅行商的旅行距离,也就是优化目标本身。

private void selectNextGen() {// 在原来种群和经过变异和交叉操作后的种群中选择较好的个体进入下一轮Candidate can = null;mutatePop.addAll(population);ArrayList<Candidate> canList = new ArrayList<Candidate>();for(ArrayList<Integer> sol : mutatePop){can =  new Candidate();can.setList(sol);can.setSortIndex(getTotalDist(sol));canList.add(can);}Collections.sort(canList, new MyComparator1());population = new ArrayList<ArrayList<Integer>>();int i,size = pSize;for(i = 0;  i < size; i++){//System.out.println(canList.get(i).getSortIndex());population.add(canList.get(i).getList());}}

另外,计算旅行距离的函数实现如下。

public static double getTotalDist(ArrayList<Integer> list, double[][] distances) {int number = list.size();double res = 0;int i = 0;for(; i < number - 1; i++)res += distances[list.get(i)][list.get(i + 1)];if(number > 1)res += distances[list.get(number - 1)][0];return res;}}

6.参考资料

a. 维基百科

b. 如何求解问题——现代启发式方法

c.Metaheuristic in Combinatorial Optimization:Overview and Conceptual Comparison

(2012年3月25日)

注:今后我仍将继续学习演化算法,尤其算子的设计,初始解的构造等方面,并更新有关内容。

演化算法(一) 基本概念相关推荐

  1. python 生成数组1:1:20_英特尔“演化算法”新框架:29个Python代码块,自动生成新算法...

    点击上方 "码农真经" 关注,星标或者置顶 22点24分准时推送,第一时间送达 来源:公众号 新智元 | 编辑:真经君 码农真经(ID:coder_experience)第 133 ...

  2. 数据结构之排序算法:基础概念

    排序算法:基础概念 思维导图: 排序的定义: 算法的稳定性: 内部排序和外部排序: 思维导图: 排序的定义: 算法的稳定性: 内部排序和外部排序:

  3. 数据结构之查找算法:基本概念

    查找算法:基本概念 思维导图: 查找的基本概念及基本操作: 思维导图: 查找的基本概念及基本操作: ps: Pi表示元素出现的概率,一般情况下,默认各个元素出现的概率相同 Ci表示元素出现的次数

  4. (王道408考研数据结构)第一章绪论-第二节1:算法的基本概念、算法的特性及设计要求

    文章目录 一:算法的基本概念 (1)数据结构和算法的关系 (2)算法(Algorithm)的定义 二:算法的特性 三:算法设计要求 程序=数据结构+算法,前面我们已经探讨了什么是数据结构,明白了如何用 ...

  5. 考研数据结构笔记--数据结构和算法的基本概念

    考研数据结构笔记--数据结构和算法的基本概念 数据结构的基本概念 算法的基本概念 数据结构的基本概念 数据 数据是对客观事物的符合表示,在计算机科学中是指所有能输入到计算机中并且被计算机程序处理的符合 ...

  6. 游戏编程中的人工智能技术-演化算法入门

    buckland大神的每一章都很经典,少了任何一章都会感到不完整.今天先介绍第十章-演化算法.因为这是人工生命的基础.大名鼎鼎的tierra就是采用了演化算法.(什么是Tierra?看这里点击打开链接 ...

  7. C语⾔程序设计——算法的基本概念

    C语⾔程序设计--算法的基本概念 算法的基本概念 算法的特性 有穷性:⼀个算法必须在⼜穷步之后结束,都有有穷性的时间完成,不能够⽆限的执⾏下去 确定性:算法的每⼀个步骤都是有确定意义的,每⼀个过程不能 ...

  8. 基于昇思MindSpore Quantum,实现量子虚时演化算法

    01.关于昇思MindSpore项目介绍 1.项目名称 基于昇思MindSpore Quantum,实现量子虚时演化算法 2.项目链接 https://summer-ospp.ac.cn/#/org/ ...

  9. 天勤计算机考研高分笔记(一)绪论4数据结构与算法的基本概念

    1.3.1数据结构的基本概念 1.数据 数据是一种对客观事物的符号表示,是可以输入计算机中,并被计算机程序处理的符号的总称.(整数,实数,字符串) 2.数据元素(数据项< <数据结构,数据 ...

最新文章

  1. 弹性网络_理论物理所建立解析模型研究凝胶网络弹性介导的液液相分离现象
  2. asp.net core系列 53 IdentityServer4 (IS4)介绍
  3. python redis_Python操作Redis大全
  4. codeblocks常用配置
  5. Scala数组:使用()代替java的[]
  6. 三面美团Java岗,java架构师线下培训
  7. 2021年Node.js开发人员学习路线图
  8. 2021最新学习路线,Java快速入门到精通(附Java教学视频)
  9. STM8L开发环境配置
  10. 金士顿优盘突然出现写保护,无法删除更改数据也没有办法格式化
  11. 如何用小米玩华为游戏华为账号
  12. 11年电赛综合测评——单运放三角波发生器
  13. Xiangqi UVa1589
  14. matlab 传函将s换为jw,2010MATLAB及控制系统仿真_总复习.ppt
  15. woocommerce 下单失败也会清空购物车
  16. 升级Windows11遇到VirtualBox兼容性问题(附解决办法)
  17. HCIE - Routing Switching v3.0 Outline
  18. SAM/BAM文件格式简介(一)
  19. apisix健康检查测试
  20. 数据仓库中各个常用英文简写的代表意义

热门文章

  1. 读书心得 | 软技能-代码之外的生存指南
  2. 计算机应用基础全册教案,[定稿]计算机应用基础全册教案
  3. 九章算法 | Google面试题:堆化
  4. Django框架的模型层之多表操作
  5. 截至2020年3月,所有与Android Auto兼容的汽车
  6. 基于Landsat的地表温度反演——单窗算法
  7. PSP播放ATRAC3 Plus格式的方法
  8. Python数据分析(一)
  9. 笔记本实体机安装FreeBSD操作系统(KDE-Plasma5桌面环境+NVIDIA显卡驱动)
  10. Ubuntu下安装拳皇97