技术报告遗传算法

技术报告遗传算法
技术报告遗传算法

遗传算法

1.概述

遗传算法是从代表问题可能潜在的解集的一个种群(population)开始的,而一个种群则由经过基因(gene)编码的一定数目的个体(individual)组成。每个个体实际上是染色体(chromosome)带有特征的实体。染色体作为遗传物质的主要载体,即多个基因的集合,其内部表现(即基因型)是某种基因组合,它决定了个体的形状的外部表现,如黑头发的特征是由染色体中控制这一特征的某种基因组合决定的。因此,在一开始需要实现从表现型到基因型的映射即编码工作。由于仿照基因编码的工作很复杂,我们往往进行简化,如二进制编码,初代种群产生之后,按照适者生存和优胜劣汰的原理,逐代(generation)演化产生出越来越好的近似解,在每一代,根据问题域中个体的适应度(fitness)大小选择(selection)个体,并借助于自然遗传学的遗传算子(genetic operators)进行组合交叉(crossover)和变异(mutation),产生出代表新的解集的种群。这个过程将导致种群像自然进化一样的后生代种群比前代更加适应于环境,末代种群中的最优个体经过解码(decoding),可以作为问题近似最优解。

1.1产生与背景

生物只有经过许多世代的不断进化(evolution,演化),才能更好地完成生存与繁衍的任务。遗传算法也遵循同样的方式,需要随着时间的推移不断成长、演化,最后才能收敛,得到针对某类特定问题的一个或多个解。了解一些有关有生命的机体如何演化的知识,对理解遗传算法的演化机制是是有帮助的。

从本质上说,任何生物机体不过就是一大堆细胞的集合。每个细胞都包含若干组相同的DNA链,人们一般称之为染色体(chromosome)。染色体中包含的DNA分为两股,这两股DNA 链以螺旋状绞合在一起,这就是我们所熟悉的DNA双螺旋结构模型。

图 1.1 DNA双螺旋结构

单个染色体是由称作基因(gene)的更小结构模块组成,而基因则又由称作核苷酸(nucleotide)的物质组成。核苷酸一共只有四种类型,即:腺嘌呤(thymine)、鸟嘌呤(adenine)、胞嘧啶(cytocine)、胸腺嘧啶(guanine)。它们常简写为T、A、C、G。这些核

苷酸相互连接起来,形成若干很长的基因链,而每个基因编码了生物机体的某种特征,如头发的颜色,耳朵的样子,等。一个基因可能具有的不同设置(如头发的黑色、棕色或金黄色),称为等位基因(allele),它们沿染色体纵向所处的物理部位称为基因的座位(locus)。

一个细胞中的染色体组(collection)包含了复制该机体所需的全部信息。这就是克隆怎样实行的秘密。你可以从被克隆施主(donor)身上,哪怕是一个血细胞中包含的信息,复制出整个生物机体,例如一只羊。新的羊将会在每一个方面和施主羊完全相同。染色体的这一集合就称为生物机体的基因组(genome)。在特殊基因组中等位基因的一种状态称为该机体的遗传类型(genotype)。这些就是用来生成实际的生物机体-所谓表现型(phenotype)-本身的硬编码指令。你和我都是表现型。我们的DNA携带了我们的遗传类型。如将这些术语用到其他领域中,则,设计汽车用的成套蓝图就是一个遗传类型;在生产线上隆隆作响的成品汽车就是一个表现型;只有设计被定型之前的,那些完全阵旧的设计,才勉强称得上是一个基因组。

对于千千万万的动物和植物-小到只有在显微镜下才能看到的单细胞生物,大到从空间卫星上也能见到的巨大珊瑚礁-地球是它们共同的家,不管它们的大小怎样、形状或颜色又怎样。一个生物机体被认为取得了成功,如果它得到了配偶并生下了一个子机体,而后者完全有希望来继续进一步复制自己。

为了做到这一点,生物机体必须善长许多工作。例如,能寻找食物和水、能面对掠食者来保卫自己、能使自己吸引潜在的配偶,等。所有这些特长在某种程度上都和生物机体的遗传类型-生命的蓝图有关。生物机体的某些基因将会产生有助于它走向成功的属性,而另一些基因则可能要妨碍它取得成功。一个生物的成功的量度就是它的适应性。生物机体愈能适应,它的子孙后代也就愈多。

当两个生物机体配对和复制时,它们的染色体相互混合,产生一个由双方基因组成的全新的染色体组。这一过程就叫重组(recombination)或交叠(crossover,又译杂交,交叉,交换)。这样就意味,后代继承的可能大部分是上一代的优良基因,也可能继承了它们不少的不良基因。如果是前一种情况,后代就可能变得比它的父母更能成功(例如,它对掠食者有更强的自卫机制);如为后一种情况,后代甚至就有可能不能再复制自己。这里要着重注意的是,愈能适应的子孙后代就愈有可能继续复制并将其基因传给下一个子孙后代。由此就会显示一种趋向,每一代总是比其父母一代生存和匹配得更完美。

作为它的一个很简捷的例子,我们设想,雌性动物仅仅吸引大眼睛的雄性。这样,在追求雌性配偶的雄性中,眼睛的尺寸愈大,其获得成功的可能性也愈大。你可以说,动物的适应性正比于它的眼睛的直径。因此,你就可以看到,从一个具有不同大小眼睛的雄性群体出发,当动物进化时,在同位基因中,能产生大眼睛雄性动物的基因,相对于产生小眼睛雄性动物的基因,就更有可能被复制到下一代。由此可以推出,当进化几代之后,大眼睛将会在雄性群体占据统治地位。过些时候,你就可以说,生物正在向一种特殊的遗传基因收敛。

达尔文的自然选择学说是一种被人们广泛接受的生物进化学说。这种学说认为,生物要生存下去,就必须进行生存斗争。生存斗争包括种内斗争、种间斗争以及生物跟无机环境之间的斗争三个方面。在生存斗争中,具有有利变异的个体容易存活下来,并且有更多的机会将有利变异传给后代;具有不利变异的个体就容易被淘汰,产生后代的机会也少的多。因此,凡是在生存斗争中获胜的个体都是对环境适应性比较强的。达尔文把这种在生存斗争中适者生存,不适者淘汰的过程叫做自然选择。它表明,遗传和变异是决定生物进化的内在因素。自然界中的多种生物之所以能够适应环境而得以生存进化,是和遗传和变异生命现象分不开的。正是生物的这种遗传特性,使生物界的物种能够保持相对的稳定;而生物的变异特性,使生物个体产生新的性状,以至于形成新的物种,推动了生物的进化和发展。遗传算法是模拟达尔文的遗传选择和自然淘汰的生物进化过程的计算模型。它的思想源于生物遗传学和

适者生存的自然规律,是具有“生存+检测”的迭代过程的搜索算法。遗传算法以一种群体中的所有个体为对象,并利用随机化技术指导对一个被编码的参数空间进行高效搜索。其中,选择、交叉和变异构成了遗传算法的遗传操作;参数编码、初始群体的设定、适应度函数的设计、遗传操作设计、控制参数设定五个要素组成了遗传算法的核心内容。作为一种新的全局优化搜索算法,遗传算法以其简单通用、鲁棒性强、适于并行处理以及高效、实用等显著特点,在各个领域得到了广泛应用,取得了良好效果,并逐渐成为重要的智能算法之一。

生物的进化是一个奇妙的优化过程,它通过选择淘汰,突然变异,基因遗传等规律产生适应环境变化的优良物种。遗传算法是根据生物进化思想而启发得出的一种全局优化算法。

1.2研究思想

遗传算法受到达尔文进化论的影响很大。在遗传算法中,问题的解是逐渐进化得到的。

遗传算法的计算从一组可能解开始,这组解被称作种群(Population),在算法中被表示成基因。我们又把种群中的解拿出来去构成新的一个种群,这是因为我们期望新的种群要比旧的种群要好!当然,新的种群中的解要有这样的性质,就必须按照它的适应度去选择,适应度越高,它参与构造新的种群的机会就越大。这个过程一次又一次的重复,一直到我们所给的约束条件满足为止,比如说种群中解得个数或者种群的良好程度。

生物的进化是一个奇妙的优化过程,它通过选择淘汰,突然变异,基因遗传等规律产生适应环境变化的优良物种。遗传算法是根据生物进化思想而启发得出的一种全局优化算法。

遗传算法的基本思想是基于Darwin进化论和Mendel的遗传学说的。

Darwin进化论最重要的是适者生存原理。它认为每一物种在发展中越来越适应环境。物种每个个体的基本特征由后代所继承,但后代又会产生一些异于父代的新变化。在环境变化时,只有那些熊适应环境的个体特征方能保留下来。

Mendel遗传学说最重要的是基因遗传原理。它认为遗传以密码方式存在细胞中,并以基因形式包含在染色体内。每个基因有特殊的位置并控制某种特殊性质;所以,每个基因产生的个体对环境具有某种适应性。基因突变和基因杂交可产生更适应于环境的后代。经过存优去劣的自然淘汰,适应性高的基因结构得以保存下来。

由于遗传算法是由进化论和遗传学机理而产生的直接搜索优化方法;故而在这个算法中要用到各种进化和遗传学的概念。

2.基本工作原理

遗传算法的基本原理正是基于模仿生物界遗传学的遗传过程。它把问题的参数用基因代表,把问题的解用染色体代表(在计算机里用二进制码表示),从而得到一个由具有不同染色体的个体组成的群体。这个群体在问题特定的环境里生存竞争,适者有最好的机会生存和产生后代。后代随机化地继承了父代的最好特征,并也在生存环境的控制支配下继续这一过程。群体的染色体都将逐渐适应环境,不断进化,最后收敛到一族最适应环境的类似个体,即得到问题最优的解.值得注意的一点是,现在的遗传算法是受生物进化论学说的启发提出的,这种学说对我们用计算机解决复杂问题很有用。

2.1生物遗传学概念

由于遗传算法是由进化论和遗传学机理而产生的直接搜索优化方法;故而在这个算法中要用到各种进化和遗传学的概念。首先给出遗传学概念、遗传算法概念和相应的数学概念三者之间的对应关系.这些概念如下:

表2.1 概念对照表

2.2步骤和实现方法

遗传算法计算优化的操作过程就如同生物学上生物遗传进化的过程,主要有三个基本操作(或称为算子):选择(Selection)、交叉(Crossover)、变异(Mutation)。

遗传算法基本步骤主要是:先把问题的解表示成“染色体”,在算法中也就是以二进制编码的串,在执行遗传算法之前,给出一群“染色体”,也就是假设的可行解。然后,把这些假设的可行解置于问题的“环境”中,并按适者生存的原则,从中选择出较适应环境的“染色体”进行复制,再通过交叉、变异过程产生更适应环境的新一代“染色体”群。经过这样的一代一代地进化,最后就会收敛到最适应环境的一个“染色体”上,它就是问题的最优解。

遗传算法的一般算法:

创建一个随机的初始状态初始种群是从解中随机选择出来的,将这些解比喻为染色体或基因,该种群被称为第一代,这和符号人工智能系统的情况不一样,在那里问题的初始状态已经给定了。

评估适应度对每一个解(染色体)指定一个适应度的值,根据问题求解的实际接近程度来指定(以便逼近求解问题的答案)。不要把这些“解”与问题的“答案”混为一谈,可以把它理解成为要得到答案,系统可能需要利用的那些特性。

繁殖(包括子代突变) 带有较高适应度值的那些染色体更可能产生后代(后代产生后也将发生突变)。后代是父母的产物,他们由来自父母的基因结合而成,这个过程被称为“杂交”。

下一代如果新的一代包含一个解,能产生一个充分接近或等于期望答案的输出,那么问题就已经解决了。如果情况并非如此,新的一代将重复他们父母所进行的繁衍过程,一代一代演化下去,直到达到期望的解为止。

并行计算非常容易将遗传算法用到并行计算和群集环境中。一种方法是直接把每个节点当成一个并行的种群看待。然后有机体根据不同的繁殖方法从一个节点迁移到另一个节点。另一种方法是“农场主/劳工”体系结构,指定一个节点为“农场主”节点,负责选择有机体和分派适应度的值,另外的节点作为“劳工”节点,负责重新组合、变异和适应度函数的评估。

遗传算法的具体步骤:

1)选择编码策略,把参数集合(可行解集合)转换染色体结构空间;

2)定义适应函数,便于计算适应值;

3)确定遗传策略,包括选择群体大小,选择、交叉、变异方法以及确定交叉概率、变异概率等遗传参数;

4)随机产生初始化群体;

5)计算群体中的个体或染色体解码后的适应值;

6)按照遗传策略,运用选择、交叉和变异算子作用于群体,形成下一代群体;

7)判断群体性能是否满足某一指标、或者是否已完成预定的迭代次数,不满足则返回第五步、或者修改遗传策略再返回第六步。

图2.1 一个遗传算法的具体步骤

遗传算法有很多种具体的不同实现过程,以上介绍的是标准遗传算法的主要步骤,此算法会一直运行直到找到满足条件的最优解为止.

1)群体规模

群体规模N影响到GA的最终性能和效率。当规模太小时,由于群体对大部分搜索空间只给出了不充分的样本量,所以得到的结果一般不佳,大的群体更有希望包含大量搜索空间的代表,从而可以防止过早收敛到局部最优解;然而群体越大,每一代需要的计算量也就越多,这可能会导致一个无法接受的慢收敛,在试验中群体规模的变化范围从10~160。

2)编码与解码

编码就是把问题的解用数字串表示,遗传算子也是直接对串进行操作的。这样,如何用适当的串来表示问题的解也就成了GA的一个重要问题。在GA的许多应用中都是用二进制的数字串来表示,目前也有用浮点数来表示的。浮点数表示精度会提高,但是增加迭代时间。下面只介绍用二进制串进行的编码,首先确定要优化的参数并确定每个参数的大致变化范围,这个范围选择得不好,也会影响遗传算法的性能。根据每个参数的精度和参数空间的大小来确定二进制的位数,然后把每个参数的二进制串一个一个地排列起来,就构成了GA的一个个体或者称作染色体。数字串中优化变量的排列方式也是一个比较重要的问题,合适的排列方式除了便于编码和解码之外,对GA的性能也有一定的影响。

3)遗传算法中个体的选择

选择就是根据适应值从群体中选择串进行拷贝的过程,一旦一个串被选择,则将这个串的拷贝放入在一个新的暂时群体交配池中,以便进行交叉和变异。选择的方法有确定性选择、轮盘赌选择、无退还随机选择、窗口选择方法,两两竞争法,线性标准化方法,还有退还和无退还的剩余随机选择方法等。

3.计算机内的进化

遗传算法的工作过程本质上就是模拟生物的进化过程。首先,要规定一种编码方法,使得你的问题的任何一个潜在可行解都能表示成为一个“数字”染色体。然后,创建一个由随机的染色体组成的初始群体(每个染色体代表了一个不同的候选解),并在一段时期中,以培育适应性最强的个体的办法,让它们进化,在此期间,染色体的某些位置上要加入少量的变异。经过许多世代后,运气好一点,遗传算法将会收敛到一个解。遗传算法不保证一定能得到解,如果有解也不保证找到的是最优解,但只要采用的方法正确,你通常都能为遗传算法编出一个能够很好运行的程序。遗传算法的最大优点就是,你不需要知道怎么去解决一个问题; 你需要知道的仅仅是,用什么的方式对可行解进行编码,使得它能被遗传算法机制所利用。

通常,代表可行解的染色体采用一系列的二进制位作为编码。在运行开始时,你创建一个染色体的群体,每个染色体都是一组随机的2进制位。2进制位(即染色体)的长度在整个群体中都是一样的。作为一个例子,长度为20的染色体的形状如下:

01010010100101001111

重要的事情就在于,每个染色体都用这样的方式编码成为由0和1组成的字符串,而它们通过译码就能用来表示你手头问题的一个解。这可能是一个很差的解,也可能是一个十分完美的解,但每一个单个的染色体都代表了一个可行解。初始群体通常都是很糟的,有

点像英国板球队或美国足球队。但不管怎样,正如前面说过的那样,一个初始的群体已经创建完成(对这一例子,不妨设共有100个成员),这样,你就可以开始做下面列出的一系列工作:

不断进行下列循环,直到寻找出一个解:

(1)检查每个染色体,看它解决问题的性能怎样,并相应地为它分配一个适应性分数。

(2)从当前群体中选出2个成员。被选出的概率正比于染色体的适应性,适应性分数愈高,被选中的可能性也就愈大。常用的方法就是采用所谓的轮盘赌选择法或赌轮选择法 (Roulette wheel selection )。

( 3)按照预先设定的杂交率(Crossover Rate ),从每个选中染色体的一个随机的点上进行杂交(crossover )。

( 4)按照预定的变异率(mutation rate ),通过对被选染色体的位的循环,把相应的位进行翻转(flip )。

(5)重复步骤(2),(3),(4),直到100个成员的新群体被创建出来。

结束循环

以上算法中步骤1 到步骤5 的一次循环称为一个代(或世代,generation)。而我把这整个的循环称作一个时代(epoch),在我的正文和代码中将始终都用这样方式来称呼。

3.1轮盘赌选择

轮盘赌选择是从染色体群体中选择一些成员的方法,被选中的机率和它们的适应性分数成比例,染色体的适应性分数愈高,被选中的概率也愈多。这不保证适应性分数最高的成员一定能选入下一代,仅仅说明它有最大的概率被选中。其工作过程是这样的:

设想群体全体成员的适当性分数由一张饼图来代表(见图3.4),这一饼图就和用于赌博的转轮形状一样。我们要为群体中每一染色体指定饼图中一个小块。块的大小与染色体的适应性分数成比例,适应性分数愈高,它在饼图中对应的小块所占面积也愈大。为了选取一个染色体,你要做的,就是旋转这个轮子,并把一个小球抛入其中,让它翻来翻去地跳动,直到轮盘停止时,看小球停止在哪一块上,就选中与它对应的那个染色体。本章后面我就会告诉你怎样来编写这种程序的准确算法。

图 3.1染色体的轮盘赌式选择

3.2杂交率

杂交率就是用来确定2个染色体进行局部的位(bit)的互换以产生2个新的子代的概率。实验表明这一数值通常取为0.7左右是理想的,尽管某些问题领域可能需要更高一些或较低一些的值。

每一次,我们从群体中选择2个染色体,同时生成其值在0到1之间一个随机数,然后根据此数据的值来确定两个染色体是否要进行杂交。如果数值低于杂交率(O.7)就进行杂交,然后你就沿着染色体的长度随机选择一个位置,并把此位置后面所有的位进行互换。

例如,设给定的2个染色体为:

10001001110010010

01010001001000011

沿着它们的长度你随机选择一个位置,比如说10,然后互换第10位之后所有位。这样两个染色体就变成了(我已在开始互换的位置加了一个空格):

100010011 01000011

010100010 10010010

3.3变异率

变异率(突变率)就是在一个染色体中将位实行翻转(flip,即0 变1,1变0)的几率。这对于二进制编码的基因来说通常都是很低的值,比如0.001 。

因此,无论你从群体中怎样选择染色体,你首先是检查是否要杂交,然后再从头到尾检查子代染色体的各个位,并按所规定的几率对其中的某些位实行突变(翻转)。

4.实际应用

4.1 二维下料问题

遗传算法是一种优化算法,所以可以应用在很多地方。尤其是对于比较复杂或者难于求出精确解的问题,该方法给出了比较好的解决方案。

二维下料问题是说,在固定宽度的板材上切割下一些要求大小的目标物,使得消耗的板材长度最小。

对于这个问题,可以把他抽象为这样的数学模型:每一个目标物设置一个id号,这样一组id号就构成了一个切割顺序。显然,在这样的模型下,切割顺序和切割方式对于最终的结果有比较大的影响。

略去切割方式,本文在一种切割方式下,集中对切割顺序作一个小小的分析。

我选择的切割方法是:将进入的目标物从下到上,从左到右地在板材上找空闲位置,找到后将板材上的该区域标记;重复上述过程直到没有目标物进入为止。

在上述切割方式下,我的目标转化为找到一个合适的序列使得切割后使用的板材长度尽可能地小。

对于这个序列,我使用了遗传算法。遗传算法控制参数很多,尤其是初始种群的选取对于该算法有较大影响。我选择了从大到小排列目标物,然后象挤牙膏那样生成一组新的序列。用这样的序列进行进化。基本上对于较小数目的目标物,很快能给出次优序列。从本人实验的结果看,优化率((直接切割所需长度-优化后所需长度)/直接切割所需长度)平均为11%,有时甚至高达28%。可见,优化和不优化还是有很大差别的。

4.2排课问题

在排课问题中,我们的主要任务是将班级、教室、课程、教师安排在一周内且不发生时间冲突。据此,我们给出如下描述:

学校有R间教室,C个班,S门课程,T位教师,P个时间段。

1)教室集合R(R1,R2,…Rn),每间教室分别可容纳(X1,X2…Xr)人;

2)班级集合C(C1,C2,…Cn),每个班级分别有(K1,K2,…Kc)人,其中有x个班级上合班课;

3)课程集合S(S1,S2,…Sn),每门课对应Ci个班,1位教师,(1≤ Ci

(1≤ Sm

4)教师集合T(T1,T2,…Tn),每位教师对应Sm门课,Cn个班,

在初始设置时设定教师的排课要求。

5)时间集合P(P1,P2,…,Pn),假设一周上五天课,每天分为五个教学单元,每个单元为2个课时,即上午2个,下午2个,晚上1个,则时间集合包含25个时间段。如11代表周一第一个教学单元,即周一1、2节,12代表周一第二个教学单元,即周一3、4节,以此类推,这些时间段构成一个时间集合P(11,12,13,….55)。

一张正确的课表应至少满足以下硬约束条件:

1) 一个教师或者一个班级或者一个教室在同一时间段内只能安排一门课程;

2) 分配的教室可容纳人数应该大于学生数。

除了上述的硬性约束,还有些软约束,这些软约束有助于使得课表更加合理,更加人性

化。这些软约束条件可能是:

1) 尽量在早上安排必修课,而下午安排选修课,晚上尽量不排课;

2)尽可能满足个别教师的特殊上课时间要求;

3) 一门课尽量分散在一个星期中,即某天上完某一门课后,要隔一天以上再上这门课,以使教师有充足的时间备课和批改作业,而学生也有足够的时间复习消化;

4)一个教师的课不能排满一整天;

5)学生课表中的上课时间不能过分集中,应避免一天课程很满而另一天却一整天没课的情况。

这些软约束条件各院校有所不同,在我们的研究中,旨在我们定义的约束范围内给出一个遗传算法的解决方法,并对其进行优化操作。

遗传算法采用类似基因演化的循环过程,其演算过程如下:

1)随机产生一定数目的初始种群

2)对个体适应度进行评估,如果个体的适应度符合优化准则,则输出最佳个体及其代表的最优解,并结束计算,否则转向第3步。

3)依据适应度选择再生个体

4)按照一定的交叉概率和交叉方法生成新的个体

5)按照一定的变异概率和变异方法生成新的个体

6)由交叉和变异产生新一代的种群,然后返回第2步。

以下是遗传算法的伪代码。

BEGIN:

I = 0;

Initialize P(I);

Fitness P(I);

While(not Terminate-Condition)

{

I ++;

GA-Operation P(I);

Fitness P(I);

}

END.

染色体编码

GA中首要考虑的是如何表现其问题,即如何对染色体编码,使之适用于GA操作。在经典的遗传算法中,常采用浮点数或二进制的编码方法,而研究中,每条染色体代表每位教师的课表,其结构表示如下:

教师ID 班级ID 课程ID 教室上课时间安排

染色体在程序中可用十进制数编码,例如:某一教师编号为1247,要教授“数据库原理”这门课,“数据库原理”课程编号为8017,周学时为4,班级为01811、01812,随机产生上课时间,随机选择大于两班总人数的教室,则可生成染色体如:“124701811018128017024012241”其中02401,2241分别代表教室及上课时间星期二第二个教学单元(即上午3、4节)和星期四第一个教学单元(即上午1、2节)。

按如上编码,两条染色体对后9位作交叉操作,不会影响到每位教师所教授的课程,也不会造成教师课表内含其他教师的教授课程或每代演化后染色体结构不合理等问题。

每一条染色体表示一种可能的排课结果,至于排课结果的优劣,则由适应度函数评估染色体的适应值来决定。

适应度函数

遗传算法在进化中是以每个个体的适应度值为依据来选取下一代种群的。适应度函数设定的好坏直接影响到遗传算法的收敛速度和能否找到最优解。在本系统中,适应度函数的设计思想是对每条染色体中存在的冲突类型进行加权求和,其中权值Wi代表的是第i条规则的重要程度,若某条染色体违反了某条规则i,则将其值Pi置为1(若没有违反规则i,则Pi值为0),其受到的惩罚值为Wi*Pi,对染色体中存在的冲突进行加权求和并加上1后,再求其倒数,如以下公式所示。染色体适应度函数值越大,则表示其拥有较好的授课时段和教室,其在下一代的演化中的生存概率就较大。

遗传操作

初始化[Initialize]

初始化的目的在于为后面的遗传操作提供初始种群。

在我们的算法中,由于每次对一位教师进行遗传操作,初始化时就需要考虑到教室及时间的设定,这其中包括教室可容人数的最优逼近(即避免一个30人的班级占用可容200人的教室这种情况),以及上课时间安排的合理性,这在排课问题描述中已有解释。

2)选择[Select]

选择运算用于模拟生物界去劣存优的自然选择现象。它从旧种群中选择出适应度高的某种染色体,放入配对集合中,为染色体交叉和变异运算产生新种群做准备。适应度越高的染色体被选择的可能性越大,

选择操作的方法有许多,如轮盘赌选择法(roulette wheel selection),局部选择法(local selection),锦标赛选择法(tournament selection)等。

研究中,我们选用了局部选择法中的一种:截断选择法(truncation selection)。

在截断选择法中,染色体按适应度函数值由高到低排序,只有最优秀的个体才能被选作父个体。其中,用于决定染色体被选作父个体的百分比的参数称为截断阀值Trunc,其取值范围为50%~10%。在该阀值之外的个体不能产生子个体。

选择强度与截断阀值的关系

截断阀值 1% 10% 20% 40% 50% 80%

选择强度 2.66 1.76 1.2 0.97 0.8 0.34

其中选择强度是将正规高斯分布应用于选择方法,期望平均适应度。

选择强度表示为:

SelIntTrunc(Trunc) =

式中fc为下列高斯分布的积分下限:

Trunc =

3)交叉[Crossover]

交叉是根据选择操作的结果,选取两条染色体作为父个体,再取一随机值(设为r)与系统预设的交叉率值(设为t)比较,若r

4)变异[Mutate]

变异是随机改变染色体中任一授课时段,将时段随机抽取一点在设定范围内改变。变异运算模仿了生物在自然遗传环境中由于各种偶然因素引起的基因突变,通过变异,染色体适应度有可能加强也有可能降低,但它确保了种群中遗传基因类型的多样性,使搜索能在尽可能大的空间中进行,获得最优解的可能性大大加强。

变异操作与交叉操作类似,即定义一个变异概率pm,在变异时先产生一个随机数r,当r

例如:有一染色体编码为:“0872’01211’1005’04201’ 2122”,它表示星期二的第

一、二教学单元节有编号为“1005”的课程,经变异,该染色体变成:“0872’01211’1005’04201’ 2152”,染色体的适应度大大提高。

冲突问题解决

排课问题是一个NP-Complete问题,无论采用哪种方法都无法避免各种冲突问题的出现,同一位教师在同一时段内排了两门课是冲突问题中最明显的一个。为了避免这种冲突产生,在本系统开发中引进了一个冲突检测函数fConflict(),当排完一位教师的所有课程之后,系统就会用该函数对此教师课程安排的冲突情况进行检测并作修正。

由适应值曲线图可以看出,该算法具有较好的收敛性,也说明了本文中提到的染色体编码方案和适应度函数能够较好地反映排课要求,染色体经过世代进化后可以得到令人满意的最优解。利用遗传算法排出的01811,01812两个班级某个学期的课表,从课表中可以看出该课表不存在教师、教室、班级冲突,同一门课程两次上课时间间隔都达到一天以上,并且没有课程被安排在晚上,因此不管是硬约束条件还是软约束条件都得到较好的满足。

4.3模拟退火遗传算法

在模拟退火算法的运行过程中溶入遗传算法,称为模拟退火遗传算法。

模拟退火算法是基于Monte Carlo迭代求解法后种启发式随机搜索算法,它模拟固体物质退火过程的热平衡问题与随机搜索寻优问题的相似性来达到寻找全局最优或近似全局最优的目的。在搜索最优解的过程中,模拟退火法除了可以接受优化解外,还有一个随机接受准则(Metropolis准则)有限度地接受恶化解,并且接受恶化解的概率慢慢趋向于0,这使得算法有可能从局部极值区域中跳出,即可能找到全局最优解,并保证了算法的收敛性。

模拟退火算法的求解过程如下:

1)随机产生初始解x0;

2)初始化退火温度T0;

3)在温度TK下执行如下操作:

产生新的可行解x',x'为x的邻解;

计算评价函数f(x')和f(x)的差值Δf=f(x')-f(x);

以min{1,exp(-Δf/Tk)}>random[0,1]的概率接收新解,其中random[0,1]是[0,1]之间的随机数。若达到温度Tk的平衡状态转(4),否则转(3);

4)按一定方式降低温度,可定义下降函数为Tk+1=αTk,k+1→,其中α∈[0,1];

5)若满足收敛判据,退火过程结束;否则转(3)。

通过以上分析可知,在模拟退火过程中,其退火温度控制着求解过程向最小值的优化方向进行,同时它又以概率exp(-Δf/Tk)接收劣质解,因此算法可以跳出局部极值点。只要初始温度足够高,退火过程足够慢,算法能够收敛到全局最优解。

4.4黑盒测试中的应用

在软件测试中,黑盒测试主要是针对模块进行的功能测试。最普遍的方法是以软件的功能说明书为基础将软件的输入划分为若干个等价类,多次运行该软件来检验软件对于不同的等价类是否能满足要求。但是在实际应用中,有的模块太大或输入参数太多,等价类划分后需要进行的测试工作可能是一个极大的任务。这时,如何选择最优的测试用例就成为测试人员的一个重要任务。

遗传算法是模仿生物遗传和进化机制的一种最优化方法,它把类似于遗传基因的一些行为,如交叉重组、变异、选择和淘汰等引入到算法求解的改进过程中。遗传算法的特点之一是,它同时保留着若干局部最优解,通过交叉重组或者解的变异来寻求更好的解。与贪婪算法相比,遗传算法更可能找到全局最优解,而贪婪算法则容易限于局部最优而达不到全局最优。

如果能够将遗传算法有效地运用于黑盒测试中,帮助测试人员选择最优的测试用例,那么将给测试工作带来极大的帮助。

应用方法

在设计具体的算法之前,我们先介绍遗传算法的基本算法,其算法框架如下:

第一步,初始化:选取p个候选解作为初始解,把其中最好的解作为暂定(最优)解。

第二步,解的改进:若满足终止条件,输出暂定解,算法终止。否则,进行以下步骤的运算:

1)解的交叉重组:从p个解中选出两个或两个以上的解进行交叉重组,得到新解,重复该运算若干次。

2)变异:在候选解中随机加进一些变异,产生新解。

3)搜索:对新产生的解用局部搜索法进行改良。若能得到比候选解更好的解,更新候选解。

4)全部解中按一定的准则选出p个解作为下一代的候选解,更新暂定解。

了解了遗传算法的算法框架后,进一步要做的就是在软件的黑盒测试中,如何将不同的等价类转变为遗传算法的候选解,如何设定解的优劣标准,如何设置合适的终止条件。

我们假定一个软件模块的输入参数有5个:A、B、C、D、E,经过合理的等价类划分后,每个参数又有5个不同的等价类:A1~A5,……,E1~E5。我们采用一个广义的遗传算法候选解概念,一般的遗传算法往往将候选解形式定为二进制的数据串,比如:111010、010001等等,而在不同等价类输入作为候选解时我们将候选解形式定为(按照上面假定为基础):A3B1C2D4E5、A2B2C4D1E3等等。这样我们解决了候选解的问题,在解的优劣标准以及终止条件的设定问题上,我们需要借助工具作为标准。

软件测试的目的是提高软件的可靠性,终止条件当然是软件达到了测试的目的及要求。而解的优劣标准正好与软件质量相反,即软件失效几率越大,这个测试用例(一个输入的解)越优。中结合北大的青鸟黑盒测试环境提出了一种基于测试执行的失效数据模型JBFDM (Jade Bird failure data model)。利用该模型我们可以做到:

1)提供一致的失效数据建模、收集及管理的可靠性度量过程,从而支持可靠性度量;

2)利用测试及软件现场收集的数据来评价测试计划、操作概图及测试方法的有效性。

软件测试的目的是发现错误,在黑盒测试中,错误表现的形式是软件失效。但是由于软件错误并不是软件失效的充分条件,换句话说,并不是所有错误都会在测试或运行时暴露,所以黑盒测试的目的就是尽可能的通过运行测试用例使软件失效而发现错误。在我们对测试用例的评价时,用以下的数据表示测试用例的优劣:

A=P+λ/M+μ×F

其中A表示遗传算法中的适应度Adaptaio,P表示该测试用例在实际中发生的几率Probability,M表示平均失效时间(MTTF),f表示失效等级。因为测试是针对使用的,所以发生几率高的测试用例适应度高就不难理解了;而M——平均失效时间越长,该测试用例应该不容易发现软件的错误,所以A越低;F则表示某些特殊情况发生使软件严重失效(比如造成死机、损坏仪器等等),此时该测试用例以及其后代必须被重点关注,所以此时A越大。λ、μ是相应于各个具体的被测试软件模块而定的系数。在实际应用中,由于软件失效的可能性不是特别大,所以遗传结果往往是发生几率高的测试用例后代较多。所以我们应该

针对具体被测试软件设计准确的发生概率产生算法。

对于该算法的说明如下:

1)每一个输入参数往往有一个几率(可以事先定义),可以简单相加来求得该测试用例的概率。但是在输入参数有较强相关性时,此方法并不能准确求得某个测试用例的发生概率,一个解决办法是设置输入参数的相关耦合度。在遗传算法的交叉、变异时其同时进行的几率与相关耦合度成正比,即对于相关耦合度高的输入参数,它们同时进行交叉、变异的几率高,反之则低。

2)检验是否满足测试要求时,需要先设置一个计数器。每运行一个新的测试用例,测试计数器加一。当发现第一次失效或故障时,计数器加二。若产生的遗传后代又使软件发生失效,则计数器加22。同理递推,当遗传算法产生的测试用例连续n次使软件失效,则计数器加2n。同时,记录所有的测试情况(此工作由外围的测试环境完成,比如北大的青鸟黑盒测试环境)。如果出现严重错误则终止测试,进行对程序的检查。如果连续k代测试用例的遗传后代都运行良好,计数器的值加2k。k的值由具体被测试软件的等价类数量、输入参数个数等决定。当测试计数器的值达到所有黑盒测试用例等价类的数值时(对于我们上面所举的例子,该值为55=3125),结束测试。当生成的孙子代、子代与父母代三代完全相同时,算法也必须结束,因为此时测试不会有新的结果。所以我们还要设置一个结束条件。而且该条件强于计数器条件。

3)每一组测试用例可以生成多个测试用例,根据适应度函数大小决定留下哪些测试用例组成新的测试用例组。

如果某测试用例使软件的运行发生了问题(即某个软件错误发作),它的后代也同样受困于该软件错误,算法很快能发现这些最佳测试用例并给出结果。测试人员就可以将它们交给开发人员解决这些问题。若软件本身确实质量优良,这些测试用例及其不同的后代无法发现失效,算法也能尽快结束,而不是完成所有测试用例(虽然从理论上,我们希望测试尽可能运行所有测试用例)。

上节的算法,相对于运行所有测试用例,并没有比较明显的优点。尤其对于测试来说,算法并没有加速运行测试用例,好象还降低了运行速度。其实算法本身的确不是用来加速运行测试用例的,其目的是找到一组最佳测试用例。因为实际上对于很多模块运行所有测试用例或哪怕是所有等价类都是几乎不可能的。

以上一节举的例子做说明,其输入等价类大致有55=3125。如果一个模块有10个输入、每个输入有10种等价类,那么输入等价类为1010。按运行一个等价类需要1分钟计算(很多循环运行模块可能不止1分钟),需要几个月才能运行一遍所有等价类。这时,运用遗传算法的优势就体现出来了。

综上所述,本文提出了一种利用遗传算法寻求最佳测试用例的测试方法原理。它能在较短时间内完成软件模块的黑盒测试并给出测试结果和好的测试用例。利用该算法原理,可以在测试集成环境中做一些设置或修改测试集成环境,这样可以大大提高测试工作的效率。

4.5求[0,31]范围内的y=(x-10)^2的最小值

1)编码算法选择为“将x转化为2进制的串”,串的长度为5位。(等位基因的值为0 or

1)。

2)计算适应度的方法是:先将个体串进行解码,转化为int型的x值,然后使用=(x-10)^2作为其适应度计算合适(由于是最小值,所以结果越小,适应度也越好)。

3)正式开始,先设置群体大小为4,然后初始化群体 => (在[0,31]范围内随机选取4个

整数就可以,编码)。

4)计算适应度Fi(由于是最小值,可以选取一个大的基准线1000,Fi = 1000 - (x-10)^2)

5)计算每个个体的选择概率。选择概率要能够反映个体的优秀程度。这里用一个很简单的方法来确定选择概率P=Fi / TOTAL(Fi)。

6)选择

根据所有个体的选择概率进行淘汰选择.这里使用的是一个赌轮的方式进行淘汰选择。先按照每个个体的选择概率创建一个赌轮,然后选取4次,每次先产生一个0-1的随机小数,然后判断该随机数落在那个段内就选取相对应的个体。这个过程中,选取概率P高的个体将可能被多次选择,而概率低的就可能被淘汰。

下面是一个简单的赌轮的例子

13% 35% 15% 37%

----------|------------------------|------------|-*-----------------------| 个体1 个体2 个体3 ^0.67 个体4 随机数为0.67落在了个体4的端内。本次选择了个体4,被选中的个体将进入配对库(mating pool,配对集团)准备开始繁殖。

7)简单交叉

先对配对库中的个体进行随机配对。然后在配对的2个个体中设置交叉点,交换2个个体的信息后产生下一代。

比如( | 代表简单串的交叉位置)

( 0110|1, 1100|0 ) --交叉--> (01100,11001)

( 01|000, 11|011 ) --交叉--> (01011,11000)

2个父代的个体在交叉后繁殖出了下一代的同样数量的个体。复杂的交叉在交叉的位置,交叉的方法,双亲的数量上都可以选择.其目的都在于尽可能的培育出更优秀的后代。

8)变异

变异操作时按照基因座来的。比如说没计算2万个基因座就发生一个变异(我们现在的每个个体有5个基因座。也就是说要进化1000代后才会在其中的某个基因座发生一次变异。)变异的结果是基因座上的等位基因发生了变化。我们这里的例子就是把0变成1或则1变成0。至此,我们已经产生了一个新的(下一代)集团.然后回到第4步,周而复始,生生不息下去) 伪代码实例:

//Init population

foreach individual in population

{

individual = Encode(Random(0,31));

}

while (App.IsRun)

{ //计算个体适应度

int TotalF = 0;

foreach individual in population

{

individual.F = 1000 - (Decode(individual)-10)^2;

TotalF += individual.F;

}

//------选择过程,计算个体选择概率-----------

foreach individual in population

{

individual.P = individual.F / TotalF;

}

//选择

for(int i=0;i<4;i++)

{

//SelectIndividual(float p)是根据随机数落在段落计算选取哪个个体的函数 MatingPool[i] = population[SelectIndividual(Random(0,1))];

} //简单交叉

//由于只有4个个体,配对2次

for(int i=0;i<2;i++)

{ MatingPool.Parents[i].Mother = MatingPool.RandomPop();

MatingPool.Parents[i].Father = MatingPool.RandomPop();

}

//交叉后创建新的集团

population.Clean();

foreach Parent in MatingPool.Parents

{

//注意在copy 双亲的染色体时在某个基因座上发生的变异未表现.

child1 = Parent.Mother.DivHeader + Parent.Father.DivEnd;

child2 = Parent.Father.DivHeader + Parent.Mother.DivEnd;

population.push(child1);

population.push(child2);

}

}

4.6手工模拟计算示例

为更好地理解遗传算法的运算过程,下面用手工计算来简单地模拟遗传算法的各个主要执行步骤。

例:求下述二元函数的最大值:

max f(x1,x2)=x1^2+x2^2

s.t. x1∈{1,2,3,4,5,6,7}

x2∈{1,2,3,4,5,6,7}

1)个体编码

遗传算法的运算对象是表示个体的符号串,所以必须把变量 x1, x2 编码为一种符号串。本题中,用无符号二进制整数来表示。因 x1, x2 为 0 ~ 7之间的整数,所以分别用3位无符号二进制整数。

2)变异运算

变异运算是对个体的某一个或某一些基因座上的基因值按某一较小的概率进行改变,它也是产生新个体的一种操作方法。我们采用基本位变异的方法来进行变异运算,其具体操作过程是:

首先确定出各个个体的基因变异位置,下表所示为随机产生的变异点位置,其中的数字表示变异点设置在该基因座处;

然后依照某一概率将变异点的原有基因值取反。

表2.2 变异点的原有基因值取反

表2.3新一代的群体p(t+1)

的改进。事实上,这里已经找到了最佳个体“111111”。需要说明的是,表中有些栏的数据是随机产生的。这里为了更好地说明问题,我们特意选择了一些较好的数值以便能够得到较好的结果,而在实际运算过程中有可能需要一定的循环次数才能达到这个最优结果。

例1:设地f(x)=-x^2+2x=0.5,求 max f(x),x ∈[-1,2]。

注:这是一个非常简单的二次函数求极值的问题,相信大家都会做。在此我们要研究的不是问题本身,而是借此来说明如何通过遗传算法分析和解决问题。

在此将细化地给出遗传算法的整个过程.编码和产生初始群体首先第一步要确定编码的策略,也就是说如何把 到2这个区间内的数用计算机语言表示出来。编码就是表现型到基因型的映射,编码时要注意以下三个原则:

完备性:问题空间中所有点(潜在解)都能成为GA 编码空间中的点(染色体位串)表现型;

健全性:GA 编码空间中的染色体位串必须对应问题空间中的某一潜在解;

非冗余性:染色体和潜在解必须一一对应。

我们通过采用二进制的形式来解决编码问题,将某个变量值代表的个体表示为一个{0,1}二进制串。当然,串长取决于求解的精度.如果要设定求解精度到六位小数,由于区间长度为2-(-1)=3,则必须将闭区间[-1,2]分为3*10^6等分。

因为097152=2^21<3*10^6<2^22=4104304 所以编码的二进制串至少需要22位。将一个二进制串(b21b20b19…b1b0)转化为区间[-1,2]内对应的实数值很简单,只需采取以下两步1)将一个二进制串(b21b20b19…b1b0)代表的二进制数化为10进制数:

21212019102100()(2)'

i i i b b b b b b x =?=?=∑

2)x'对应的区间[-1,2]内的实数:

1

2)1(2'122---?+-=x x 例如,一个二进制串a=<1000101110110101000111>表示实数0.637197。

X ′=(1000101110110101000111)2=2288967

637197.01

232288967122=-?+-=x 二进制串<0000000000000000000000>,<1111111111111111111111>,则分别表示区间的两个端点值-1和2。

利用这种方法我们就完成了遗传算法的第一步——编码,这种二进制编码的方法完全符合上述的编码的三个原则。

首先我们来随机的产生一个个体数为4个的初始群体如下:

pop(1)={

<1101011101001100011110>, %% a1

<1000011001010001000010>, %% a2

<0001100111010110000000>, %% a3

<0110101001101110010101>} %% a4

化成十进制的数分别为:

pop(1)={ 1.523032,0.574022 ,-0.697235 ,0.247238 }

接下来我们就要解决每个染色体个体的适应值问题了。

(2)定义适应函数和适应值

由于给定的目标函数f(x)=-x^2+2x+0.5在[-1,2]内的值有正有负,所以必须通过建立适应函数与目标函数的映射关系,保证映射后的适应值非负,而且目标函数的优化方向应对应于适应值增大的方向,也为以后计算各个体的入选概率打下基础。

对于本题中的最大化问题,定义适应函数g(x),采用下述方法:

min min (), ()0()0,f x F f x F g x -->?=??若其他

式中Fmin 既可以是特定的输入值,也可以是当前所有代或最近K 代中f(x)的最小值,这里为了便于计算,将采用了一个特定的输入值。

若取Fmin=-1,则当f(x)=1时适应函数g(x)=2当f(x)=-1时适应函数g(x)=0。 由上述所随机产生的初始群体,我们可以先计算出目标函数值分别如下:

f [pop(1)]={ 1.226437 , 1.318543 , -1.380607 , 0.933350 }

然后通过适应函数计算出适应值分别如下:

取Fmin=-1,g[pop(1)]= { 2.226437 , 2.318543 , 0 , 1.933350 }

3)确定选择标准

这里我们用到了适应值的比例来作为选择的标准,得到的每个个体的适应值比例叫作入选概率。其计算公式如下:对于给定的规模为n 的群体pop={

123,,,,n a a a a L },个体i a 的适应值为()i g a ,则其入选概率为

1()

(),1,2,3,,()i s i n i

i g a P a i n g a ===?∑

由上述给出的群体,我们可以计算出各个个体的入选概率。

首先可得 ∑==4

148330.6)(i i a g ,然后分别用四个个体的适应值去除以∑=41)(i i

a g ,

得:

P(a1)=2.226437 / 6.478330 = 0.343675 %% a1

P(a2)=2.318543 / 6.478330 = 0.357892 %% a2

P(a3)= 0 / 6.478330 = 0 %% a3

P(a4)=1.933350 / 6.478330 = 0.298433 %% a4

4)产生种群

计算完了入选概率后,就将入选概率大的个体选入种群,淘汰概率小的个体,并用入选概率最大的个体补入种群,得到与原群体大小同样的种群。

要说明的是:为保证收敛性,附录11的算法作了修正,采用了最佳个体保存方法(elitist model),具体内容将在后面给出介绍。

由初始群体的入选概率我们淘汰掉a3,再加入a2补足成与群体同样大小的种群得到newpop(1)如下:

newpop(1)={<1101011101001100011110>, %% a1

<1000011001010001000010>, %% a2

<1000011001010001000010>, %% a2

<0110101001101110010101>} %% a4

5)交叉

交叉也就是将一组染色体上对应基因段的交换得到新的染色体,然后得到新的染色体组,组成新的群体。

我们把之前得到的newpop(1)的四个个体两两组成一对,重复的不配对,进行交叉。(可以在任一位进行交叉)

<110101110 1001100011110>, <1101011101010001000010>

交叉得:

<100001100 1010001000010>, <1000011001001100011110>

10000110010100 01000010>, <1000011001010010010101>

交叉得:

<01101010011011 10010101>, <0110101001101101000010>

通过交叉得到了四个新个体,得到新的群体jchpop (1)如下:

jchpop(1)={

<1101011101010001000010>,

<1000011001001100011110>,

<1000011001010010010101>,

<0110101001101101000010>}

这里采用的是单点交叉的方法,当然还有多点交叉的方法,不过有些烦琐,这里就不着重介绍了。

6)变异

变异也就是通过一个小概率改变染色体位串上的某个基因。

现把刚得到的jchpop(1)中第3个个体中的第9位改变,就产生了变异,得到了新的群体pop(2)如下:

pop(2)= {<1101011101010001000010>,

<1000011001001100011110>,

<1000011011010010010101>,

<0110101001101101000010> }

然后重复上述的选择、交叉、变异直到满足终止条件为止。

7)终止条件

遗传算法的终止条件有两类常见条件:(1)采用设定最大(遗传)代数的方法,一般可设定为50代,此时就可能得出最优解.此种方法简单易行,但可能不是很精确;(2)根据个体的差异来判断,通过计算种群中基因多样性测度,即所有基因位相似程度来进行控制。

相关主题
相关文档
最新文档