版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、人工智能 Java 坦克机器人系列: 遗传算法Robo cool (robocool), 编程游戏爱好者, 自由撰稿人2006 年 6 月 30 日遗传算法(Genetic Algorithm, GA)是近几年发展起来的一种崭新的全局优化算法。本文将讲解这种算法,并介绍如何 Robocode Java 坦克机器人中采用此算法以实现机器人进化。遗传算法遗传算法(Genetic Algorithm, GA)是近几年发展起来的一种崭新的全局优化算法。1962年霍兰德(Holland)教授首次提出了GA算法的思想,它借用了仿真生物遗传学和自然选择机理,通过自然选择、遗传、变异等作用机制,实现各个个体的
2、适应性的提高。从某种程度上说遗传算法是对生物进化过程进行的数学方式仿真。这一点体现了自然界中物竞天择、适者生存进化过程。与自然界相似,遗传算法对求解问题的本身一无所知,它所需要的仅是对算法所产生的每个染色体进行评价,把问题的解表示成染色体,并基于适应值来选择染色体,使适应性好的染色体有更多的繁殖机会。在算法中也即是以二进制编码的串。并且,在执行遗传算法之前,给出一群染色体,也即是假设解。然后,把这些假设解置于问题的“环境”中,也即一个适应度函数中来评价。并按适者生存的原则,从中选择出较适应环境的染色体进行复制, 淘汰低适应度的个体,再通过交叉,变异过程产生更适应环境的新一代染色体群。对这个新种
3、群进行下一轮进化,至到最适合环境的值。遗传算法已用于求解带有应用前景的一些问题,例如遗传程序设计、函数优化、排序问题、人工神经网络、分类系统、计算机图像处理和机器人运动规划等。术语说明由于遗传算法是由进化论和遗传学机理而产生的搜索算法,所以在这个算法中会用到很多生物遗传学知识,下面是我们将会用来的一些术语说明:一、染色体(Chronmosome)染色体又可以叫做基因型个体(individuals),一定数量的个体组成了群体(population),群体中个体的数量叫做群体大小。二、基因(Gene)基因是串中的元素,基因用于表示个体的特征。例如有一个串S1011,则其中的1,0,1,1这4个元素
4、分别称为基因。它们的值称为等位基因(Alletes)。三、基因地点(Locus)基因地点在算法中表示一个基因在串中的位置称为基因位置(Gene Position),有时也简称基因位。基因位置由串的左向右计算,例如在串 S1101 中,0的基因位置是3。四、基因特征值(Gene Feature)在用串表示整数时,基因的特征值与二进制数的权一致;例如在串 S=1011 中,基因位置3中的1,它的基因特征值为2;基因位置1中的1,它的基因特征值为8。五、适应度(Fitness)各个个体对环境的适应程度叫做适应度(fitness)。为了体现染色体的适应能力,引入了对问题中的每一个染色体都能进行度量的函
5、数,叫适应度函数. 这个函数是计算个体在群体中被使用的概率。操作算法霍兰德(Holland)教授最初提出的算法也叫简单遗传算法,简单遗传算法的遗传操作主要有三种:选择(selection)、交叉(crossover)、变异(mutation)这也是遗传算法中最常用的三种算法:1选择(selection)选择操作也叫复制操作,从群体中按个体的适应度函数值选择出较适应环境的个体。一般地说,选择将使适应度高的个体繁殖下一代的数目较多,而适应度较小的个体,繁殖下一代的数目较少,甚至被淘汰。最通常的实现方法是轮盘赌(roulette wheel)模型。令fi表示群体的适应度值之总和,fi表示种群中第i个
6、染色体的适应度值,它被选择的概率正好为其适应度值所占份额fifi。如下图表中的数据适应值总和fi=6650,适应度为2200变选择的可能为fifi=2200/6650=0.394.图1. 轮盘赌模型Fitness 值:220018001200950400100选择概率:33310.2710.180.1430.060.0152交叉(Crossover)交叉算子将被选中的两个个体的基因链按一定概率pc进行交叉,从而生成两个新的个体,交叉位置pc是随机的。其中Pc是一个系统参数。根据问题的不同,交叉又为了单点交叉算子(Single Point Crossover)、双点交叉算子(Two Point
7、Crossover)、均匀交叉算子 (Uniform Crossover),在此我们只讨论单点交叉的情况。单点交叉操作的简单方式是将被选择出的两个个体S1和S2作为父母个体,将两者的部分基因码值进行交换。假设如下两个8位的个体:S11000 1111S21110 1100产生一个在1到7之间的随机数c,假如现在产生的是2,将S1和S2的低二位交换:S1的高六位与S2的低六位组成数串10001100,这就是S1和S2的一个后代P1个体;S2的高六位与S1的低二位组成数串11101111,这就是S1和S2的一个后代P2个体。其交换过程如下图所示:Crossover11110000Crossover
8、11110000S11000 1111S21110 1100P11000 1100P21110 11113变异(Mutation)这是在选中的个体中,将新个体的基因链的各位按概率pm进行异向转化,最简单方式是改变串上某个位置数值。对二进制编码来说将0与1互换:0变异为1,1变异为0。如下8位二进制编码:11101100随机产生一个1至8之间的数i,假如现在k=6,对从右往左的第6位进行变异操作,将原来的1变为0,得到如下串:11001100整个交叉变异过程如下图:图2. 交叉变异过程4精英主义 (Elitism)仅仅从产生的子代中选择基因去构造新的种群可能会丢失掉上一代种群中的很多信息。也就是
9、说当利用交叉和变异产生新的一代时,我们有很大的可能把在某个中间步骤中得到的最优解丢失。在此我们使用精英主义(Elitism)方法,在每一次产生新的一代时,我们首先把当前最优解原封不动的复制到新的一代中,其他步骤不变。这样任何时刻产生的一个最优解都可以存活到遗传算法结束。上述各种算子的实现是多种多样的,而且许多新的算子正在不断地提出,以改进GA某些性能。比如选择算法还有分级均衡选择等等。遗传算法的所需参数说简单点遗传算法就是遍历搜索空间或连接池,从中找出最优的解。搜索空间中全部都是个体,而群体为搜索空间的一个子集。并不是所有被选择了的染色体都要进行交叉操作和变异操作,而是以一定的概率进行,一般在
10、程序设计中交叉发生的概率要比变异发生的概率选取得大若干个数量级。大部分遗传算法的步骤都很类似,常使用如下参数:Fitness函数:见上文介绍。Fitnessthreshold(适应度阀值):适合度中的设定的阀值,当最优个体的适应度达到给定的阀值,或者最优个体的适应度和群体适应度不再上升时(变化率为零),则算法的迭代过程收敛、算法结束。否则,用经过选择、交叉、变异所得到的新一代群体取代上一代群体,并返回到选择操作处继续循环执行。P:种群的染色体总数叫种群规模,它对算法的效率有明显的影响,其长度等于它包含的个体数量。太小时难以求出最优解,太大则增长收敛时间导致程序运行时间长。对不同的问题可能有各自
11、适合的种群规模,通常种群规模为 30 至 160。pc:在循环中进行交叉操作所用到的概率。交叉概率(Pc)一般取0.6至0.95之间的值,Pc太小时难以向前搜索,太大则容易破坏高适应值的结构。Pm:变异概率,从个体群中产生变异的概率,变异概率一般取0.01至0.03之间的值变异概率Pm太小时难以产生新的基因结构,太大使遗传算法成了单纯的随机搜索。另一个系统参数是个体的长度,有定长和变长两种。它对算法的性能也有影响。由于GA是一个概率过程,所以每次迭代的情况是不一样的,系统参数不同,迭代情况也不同。遗传步骤了解了上面的基本参数,下面我们来看看遗传算法的基本步骤。基本过程为:对待解决问题进行编码,
12、我们将问题结构变换为位串形式编码表示的过程叫编码;而相反将位串形式编码表示变换为原问题结构的过程叫译码。随机初始化群体P(0):=(p1, p2, pn);计算群体上每个个体的适应度值(Fitness)评估适应度,对当前群体P(t)中每个个体Pi计算其适应度F(Pi),适应度表示了该个体的性能好坏按由个体适应度值所决定的某个规则应用选择算子产生中间代Pr(t)依照Pc选择个体进行交叉操作仿照Pm对繁殖个体进行变异操作没有满足某种停止条件,则转第3步,否则进入9输出种群中适应度值最优的个体程序的停止条件最简单的有如下二种:完成了预先给定的进化代数则停止;种群中的最优个体在连续若干代没有改进或平均
13、适应度在连续若干代基本没有改进时停止。根据遗传算法思想可以画出如右图所示的简单遗传算法框图:图3. 简单遗传算法框图下面伪代码简单说明了遗传算法操作过程:choose an intial populationFor each h in population,compute Fitness(h)While(max Fitness(h) Fitnessthreshold)do selection do crossoverdo mutation update populationFor each h in population,compute Fitness(h)Return best Fitnes
14、sRobocode 说明能有效实现遗传算法的应用例子有很多,像西洋双陆棋、国际名模等等都是遗传程序设计学习的工具,但是 Robocode 有着其他几个无可比拟的优势:它是基于面向对象语言 Java 开发,而遗传算法本身的思想也是存在继承等面向对象概念;Robocode 是一种基于游戏与编程语言之间的平台,有一个充满竞技与乐趣的坦克战斗平台,你能很快的通过与其他坦克机器比赛而测试自己的遗传算法;Robocode 社群有 4000 个左右各种策略的例子机器人可供你选择,这些机器人足以让我们模拟真实的遗传环境。而且很多代码可直接开放源代码供我们借鉴 ;Robocode 是一个开源软件,你可直接上Ro
15、bocode控制器上加入自己的遗传特点,而加快遗传过程的收敛时间;Robocoe是一个很容易使用的机器人战斗仿真器,您在此平台上创建自己的坦克机器人,并与其它开发者开发的机器人竞技。以得分排名的方式判定优胜者。每个 Robocode 参加者都要利用 Java 语言元素创建他或她的机器人,这样就使从初学者到高级黑客的广大开发者都可以参与这一娱乐活动。如果您对Robocode不是很了解,请参考 developerWorks 网站 Java 技术专区文章:“重锤痛击 Robocode”;在 Robocode 中其实有很多种遗传算法方法来实现进化机器人,从全世界的 Robocode 流派中也发展几种比
16、较成熟的方法,比如预设策略遗传、自开发解释语言遗传、遗传移动我们就这几种方法分别加以介绍。由于遗传算法操作过程都类似,所以前面二部分都是一些方法的介绍和部分例子讲解,后面部分会给出使用了遗传算法的移动机器人人例子。 在附录中,也提供了机器人仓库中有关遗传算法机器人的下载,大家可参考。预设策略进化机器人Robocode 坦克机器人所有行为都离不开如移动、射击、扫描等基本操作。所以在此把这些基本操作所用到的策略分别进化如下编码:移动策略move-strategy (MS), 子弹能量bullet-power-strategy (BPS), 雷达扫描radar-strategy (RS), 和瞄准选
17、择策略target- strategy (TS)。由于Robocode爱好者社群的发展,每一种基本操作都发展了很多比较成熟的策略,所有在此我们直接在下面预先定义的这些策略如下表:MSBPSRSTSrandomdistance-basedalways-turnHeadOnLinearlight-fasttarget-focusLinearcircularPowerful-slowtarget-scope-focusCircularPerpendicularMediumnearest robotarbitaryhit-rate basedLoganti gravityStatisticStopAn
18、gularBullet avoidwavewall avoidtrackOscillators下面是基本移动策略的说明:Random:随机移动主要用来混乱敌人的预测,其最大的一个缺点是有可能撞击到其他机器人Linear:直线移动,机器人做单一的直线行走circular:圆周移动,这种移动是以某一点为圆心,不停的绕圈Perpendicular:正对敌人移动,这是很多人采用的一种移动方式,这在敌人右边, 以随时调整与敌人的相对角Arbitrary:任意移动AntiGravity:假设场地有很多力点的反重力移动,本方法是模拟在重力场作用下,物体总是远离重力势高的点,滑向重力势低的点,开始战场是一个平
19、面然后生成一些势点重力势大的势点的作用就像是一个山(起排斥作用),其衰减系数与山的坡度对应。重力势小的势点的作用就像是一个低谷(起吸引作用),其衰减系数与谷的坡度对应。这样使本来的平面变得不平了,从来物体沿着最陡的方向向下滑动Track:跟踪敌人,敌人移动到哪,机器人也移动到哪,但是总与敌人保持一定最佳躲避子弹距离和角度Oscillators:重复做一振荡移动Bullet avoid:每当雷达觉察到敌人时有所动作。机器人保持与敌人成30度倾向角。自身成 90 度角静止并逐渐接近目标。如果机器人觉察到能量下降介于 0.1 和 3.0 之间(火力范围),那么机器人就立即切换方向,向左或向右移动。w
20、all avoid:这是一种使自己的机器人不会被困在角落里或者不会撞墙的移动方式瞄准策略说明如下:Headon:正对瞄准Linear:直线瞄准circular:圆周瞄准nearest robot:接近机器人瞄准Log:保存每次开火记录Statistic:统计学瞄准,分析所有打中及未打中的次数,以其中找出最高打中敌人的概率为准则Angular:找到最佳角度瞄准Wave:波形瞄准,子弹以波的方式进行探测Robocode 行为事件坦克的主要都定义在一个主循环中,我们在程序中定义为上面四个策略定义四种战略如Move,Radar,Power,Target,当某一事件发生,基于这个事件而定的行为就会触发。
21、而每个战略中都有不同的行为处理方式。这些行为通过遗传算法触发,遗传算法将调用这些基本动作并搜索这些策略的最佳组合。基于这些基本动作将有4224 (=4*11*4*3*8)种可能发生。在Robocode AdvancedRobot 类下有如下的移动函数:setAhead和ahead:让机器人向前移动一定距离.setBack和back:让机器人向后移动一定距离setMaxTurnRate:设置机器人最大的旋转速度setMaxVelocity:设置机器人最大的运动速度setStop和stop:停止移动或暂停机器人,并记住停止的位置setResume和resume:重新开始移动停止的机器人setTur
22、nLeft和turnLeft:向左旋转机器人setTurnRight和turnRight:向右旋转机器人下面是 doMove 移动方法中使用部分程序代码:Random:switch(Math.random()*2) case 0:setTurnRight(Math.random()*90);break;case 1:setTurnLeft(Math.random()*90);break;execute();Linear:ahead(200);setBack(200);Circular:setTurnRight(1000);setMaxVelocity(4);ahead(1000);anti g
23、ravity:double forceX = 0;double forceY = 0;for (int i=0; itargetInfo.size(); i+)TargetInformation ti = (TargetInformation)targetInfo.get(i);double targetToMeX = getX()-ti.x;double targetToMeY = getY()-ti.y;double targetDistance = Math.sqrt(ti.x * ti.x + ti.y * ti.y);forceX += (targetToMeX/(ti.distan
24、ce * ti.distance);forceY += (targetToMeY/(ti.distance * ti.distance);forceX += 1/(getX();forceY += 1/(getY();forceX += 1/(getX()-getBattleFieldWidth();forceY += 1/(getY()-getBattleFieldHeight();double forceMagnitude = Math.sqrt(forceX*forceX+forceY*forceY);forceX*=8/forceMagnitude;forceY*=8/forceMag
25、nitude;desiredX = getX() + forceX;desiredY = getY() + forceY;这里我们用遗传算法来控制机器人移动位置。这些策略是基于下面几点:机器人人自己的位置、速度和方位;对手的位置(x,y坐标)、速度、方位以及相对角;所有机器人和子弹位置,方位及速度;场地大小等参数。当上面的信息在下一回移动中使用时,出输出一对坐标值,根据这对坐标在Robocode就能得到距离和角度。要想让移动实现遗传必须要让它实现在线学习:所以我们的代码必须做下面几件事:要有一个函数收集适应度值,在Robocode运行过程中要运用到遗传操作,遗传后代要在Robocode运行中产
26、生,而不是事后由手写入代码。遗传操作本例中遗传算法为实现移动用到两个类GA和MovePattern。此处的GA比较简单主要完成数据和群体的定义,以及这些定义的读写文件操作。基中包括如下参数:群体大小、交叉概率、变异概率、精英概率(既告诉从当前群体到下一代中有多少移动不需要改变)、方程式中使用的加权系数大小,它通过一个主循环完成MovePattern的封装。MovePattern类中实现交叉、变异方法等方法,完成移动模式操作。而所有的输出保存在一个vector函数当中。Vector函数拥有一对实数数组,一个用于计算x坐标,另一个用于计算y坐标。通过对x,y坐标的计算,从而得到距离、角度等值,并产
27、生相就在移动策略。如下,MovePattern包含三个参数,grad表示vector函数排列顺序,input即表示算法给出的输入编号,rang是加权的范围。public class MovePatteren implements Comparable private int grad, input;private double range;protected double fitness=0;protected double weightsX, weightsY; 交叉操作:每一个交叉操作执行如下步骤,先在交叉操作中产生一个特征码。这个特征码是个0到1之间的变量数组。有关交叉的基本原理可参考上
28、面部分。最后通过遍历vector函数,把相应的加权值进行交叉操作。protected MovePatteren crossOver(MovePatteren mate, boolean maskx, boolean masky) double wx= new doubleweightsX.length;double wy= new doubleweightsX.length;for(int mask=0; maskmaskx.length; mask+) for(int g=0; g=grad; g+) int i=mask*(grad+1)+g;wxi=(maskxmask?this:mat
29、e).weightsXi;wyi=(maskyg?this:mate).weightsYi;return new MovePatteren(grad, input, range, wx, wy);这里的变异操作比较简单。把加权范围内的随机数值去代替0到数组长之间的随机数并保存到移动模式中。则完成整个数组的变异过程:protected void mutate() weightsX(int)(Math.random()*weightsX.length)=Math.random()*range*2-range;weightsY(int)(Math.random()*weightsX.length)=
30、Math.random()*range*2-range;从上面的例子我们知道了遗传算法的大概实现,但并没有告诉我们这些组件是如何一起工作的。当Robocode开始时,如果文件中没有数据,所以系统会依照输入的策略随机生成一个移动模式,如果文件中有数据,则加载这些数据。每一个移动模式在开始都会给出了一个适应度值。当所有的移动模式都接收到适应度值,并完成各自的编号后,下面的操作将开始执行:对所有的移动模式依据它们的适应度值进行分级处理执行精英操作执行交叉操作应用变异操作保存加权算法重新开始适应度值在进行运算过程中由机器人程序不断调整,以找到最优适应度。限于篇副其他的一些策略本文不与详细说明,上面所有
31、提到的策略和行为程序都可在网上或IBM的开发杂志上找到成熟的讲解和例子机器人。有兴趣的朋友可以把这些策略都加入到自己的遗传算法中来。我们取群体大小为50,选择概率为0.7,交叉概率为0.6,变异概率为0.3,与Robocode部分例子机器人测试,经过150代后你会发现系统产生了很多有趣的策略。比如撞击策略,这些策略都不在我们定义的策略之中。中间解释程序进化机器人遗传算法可被看做任意基因组字符串。但是你必须决定这些字符所代表的意义,也就是说如何解释每一个基因组。最简单的方法是把每一个基因组视为java代码,编译并运行它们。但是这些程序编译都很困难,所以也就有可能不能工作。Jacob Eisens
32、tein设计了一种机器翻译语言TableRex来解决这个问题。在java中,TableRex被用于进化和解释动行时的Robocode机器人。通过测试,只要我把TableRex解释程序作为文件放入Robocode控制器目录中,这些控制器就会读取文件并开始战斗。TableRex是一些最适合遗传算法的二进制编程。只要符合TableRex程序文法,每个程序都能被解释。编码下表中显示了TableRex编码结构,它由一个行动作函数,二个输入和一个输出组成。如行6的值 ,这是个布尔型的表达式“值 line4 小于 90”,这个结果会在最后一行输出布尔为1的值。FunctionInput 1Input 2Ou
33、tput1. Randomignoreignore0,872. DivideConst_1Const_20,53. Greater ThanLine 1Line 214. Normalize AngleEnemy bearingignore-505. Absolute ValueLine 4ignore506. Less ThanLine 4Const_9017. AndLine 6Line 318. MultiplyConst_10Const_101009. Less ThanEnemy distanceLine 8010. AndLine 9Line 7011. MultiplyLine
34、10Line 4012 OutputTurn gun leftLine 110输入的函数我们依照Robocode定义而定。如下表:velocityenergyheadinggunHeadinggunHeatdistance to west walldistance to north walldistance to east walldistance to south wallconstant: 1constant: 2constant: 10constant: 90enemyVelocityenemyEnergyenemyHeadingenemyBearingenemyDistanceTabl
35、eRex有三个设计标准:它是一种解释程序,能更快的进化程序,基于TableRex设计的机器人能有效的读写遗传数据;拥有一个容易编码的固定基因组,使遗传中更容易交叉操作;只要给TableRex一个简单的输入,它就很容易通过操作命令输出要的命令序列。如上表的最后输出左转炮管;而整个TableRex解释程序由三部分组成:SmallBrain:TableRex的实现部分,此部分直接写在例子机器人处,也即自己写的测试机器人;BrainWorld:这是实现遗传算法的主方法,直接写入Robocode控制器当中,在Robocode运行当中运行;GeneticAlgorithm:这是遗传算法的定义部分,里面直接
36、定义了所要用到的遗传操作函数;下面我们来分析一个机器人如何通过TableRex达到遗传。GeneticAlgorithm:主要是声明选择、交叉、变异的方法。GeneticAlgorithm是一个静态类,其中定义了遗传所要的基本参数,如下:public abstract class GeneticAlgorithm public int population_size; / 群体长度 public int genome_size; /基因个体长度 public GFPair population; /产生的群体 public int best_index; public double best_
37、fitness = Double.MIN_VALUE; /最优适应度 public double mutation = 0.03; /变异概率public double crossover = 0.9; /交叉概率public double copy = 0.1;public int tourney_size = 3;其中变异概率取0.03, 交叉概率取0.9,最优适应度为实型的最小。此部分是从保存的文件中读取各个基本参数遗传初始化群体。依照适应度值选择群体中个体:public String tourneySelect ()double best_fit = -1;int best_guy =
38、 -1;for (int i = 0; i best_fit)best_fit = populationcur_guy.fitness;best_guy = cur_guy; return populationbest_guy.genome;交叉操作:通过从字符串中取子串的方法达到交叉操作:public static String crossover (String g1, String g2)int num_points = (int) Math.round (Math.random() * 4f);int point = (int) (g1.length() * Math.random()
39、;return g1.substring (0, point) + g2.substring (point); 变异操作:此部分先把基因转换为字符串流,通过setCharAt函数从指定的位置取反字符而达到变异:public static String mutate (String genome, double p_mutation)StringBuffer genome_b = new StringBuffer (genome);if (genome_b.charAt (point) = 1) genome_b.setCharAt(point, 0);else genome_b.setChar
40、At (point, 1);return new String (genome_b); BrainWorld:BrainWorld直接嵌入到Robocode控制器中,通过实现RobocodeListener接口来完成遗传的实例化。其最重要的有两个方法,计算最优适应度和产生遗传后代。1. 实例化 GeneticAlgorithm:genome_size = num_events * num_boxes * (input_bits * 2 + func_bits);int population_size = Integer.parseInt (in.readLine();ga = new Gene
41、ticAlgorithm (population_size, genome_size);2. 通过文件读取操作从遗传保存文件中读取参数到遗传类中,文件格式如下所示: Robocode Location Storage_directory population_size elitism crossover copy base_mutation .3. 计算最优适应度:protected void evaluateAll ()ga.best_fitness = Double.MIN_VALUE;ga.worst_fitness = Double.MAX_VALUE; for (int i = 0;
42、 i ga.population_size; i+) ga.populationi.fitness = 0; for (int j = 0; j ga.best_fitness) ga.best_fitness = ga.populationi.fitness;ga.best_index = i; 通过三个循环遍历整个群体,对各个适应度进行比较后找出最优适应度。4. 产生遗传后代public void newGeneration ()String copy_pop = ga.copy (ga.population, ga.copy);String cross_pop = ga.crossove
43、r (ga.population, ga.crossover);copy_pop = ga.mutate (copy_pop, ga.mutation);cross_pop = ga.mutate (cross_pop, ga.mutation);for (int i = 0; i copy_pop.length; i+) ga.populationi+elite_pop.length.genome = copy_popi;for (int i = 0; i cross_pop.length; i+) ga.populationi+elite_pop.length+copy_pop.lengt
44、h.genome = cross_popi;current_generation+;evaluateAll(); 通过复制(ga.copy)、交叉(ga.crossover)、变异(ga.mutate)操作,产生出遗传后代。SmallBrain:SmallBrain也即我们写的利用遗传算法的例子机器人,它开始读取遗传文件genome.dat,产生新的编码,当扫描到敌人时把所有相关的信息写入数组robot_data,再通过循环操作进化写入输入运算,最后遍历输入运算决定输出机器人的动作。1.编码:public void parseGenome (String genome)functions =
45、new int num_boxesnum_events;inputs1 = new int num_boxesnum_events;inputs2 = new int num_boxesnum_events;robot_data = new double num_boxes + num_system_inputsnum_events;通过parseGenome方法,设置function,input1,input2等数组的参数,对要操作的机器人进行编码。这部分和最上面提来的TableRex编码表是一致的。2.写入状态信息public void onScannedRobot (ScannedRob
46、otEvent e) robot_data0kSCAN_EVENT = getVelocity(); robot_data1kSCAN_EVENT = getEnergy(); robot_data2kSCAN_EVENT = getHeading();.3.根据函数数组写入输入运算for (int i = 0; i robot_datainputs2ievent_numevent_num?1f:0f;break; case 2: /equal tobreak; case 15: /outputhandleOutput (inputs1ievent_num, robot_data inputs
47、2ievent_numevent_num);break;此处注意最后是根据写入的操作运算进行输出4.输出机器人动作命令 public void handleScanOutput (int outputType, double value) switch (outputType % 16)case 0: ahead (value); break;case 1: back (value); break;case 2: /maybe shouldnt use mod here fire (value % 3); break;最后我们可以看出TableRex程序中,smallBrain和BrainWo
48、rld之间以文件方式并行交互,smallBrain扫描信息,写入文件。BrainWorld根据文件数据进化机器人,并把进化结果写入文件,smallBrain根据进化后的数据产生机器人的动作。GPBot 小型遗传机器人Geep 的机器人GPBot系统正是采用了TableRex解释程序的简单例子。GPBot仅由四行代码组成(每行都以分号结束),它做了如下一些定制达到代码最优化:忽略雷达旋转,让它直接随着炮管而转动TurnGunRight(INFINITY);把行为做为常量来实现,让它们能显示在进化代码序列的任意点。OnScannedRobot() MoveTank();TurnTankRight(
49、); TurnGunRight();GPBot所有事件都写在ScannedRobotEvent事件。每个方法都利用到了遗传算法进化机器人。第一行代码移动系统进化,适应度值依照个体躲避子弹,避墙和敌人的能力而设置;第二行代码指示坦克旋转指定的方向角。第三行代码瞄准系统进化指示炮管旋转指定的方向角, 适应度值依照个体打中敌人概率来设置。GPBot群体大小为256个个体,变异、交叉概率分别为0.9,选择长度为5。在最附录中提供了例子机器人人下载。测试结果最后我们给出一些测试数据,看看我们的程序不同的测试结果。变异概率变化测试:群体大小: 25变异概率(Mutation): tested精英概率(El
50、itism): 0.1比赛回合: 20 rounds后代: 25我们选择变异概率分级从0.01到0.5这间。依照上面的遗传算法介绍,0.01的概率是比较合理的,所以我们以此为初始化值,下图中显示了所有的测试概率数据,从下图我们可以看出,开始一个很小的适应度值,从6代开始图形就增长很慢了,在13代的时候,有一个小的变化,到最后每个后代都相当逼近。图4. 变异测试图5. 6 到 9代放大特写测试说明:我们从上可以看出当我们给出初始的适应度在很小时,在第一代增长很多,经过一定数量的后代开始汇聚到一些最大的适应度值。由此我们得到证明,机器人在我们的学习环境中聪明的开始躲避子弹、墙、和其他机器人。而且它
51、自己找到一个很好的瞄准位置给与敌人打击。JGAP 的使用最后不能不说一下Jgap的使用, JGAP 是一款用Java编写的遗传算法包,由sourceforce上开发而来。它提供了基本的遗传算法,你可以使用它来解决一些适合用遗传算法解决的问题。而且它给出了很多例子程序,可用于我们一些遗传算法的测试工作。由于它来自于开源组织sourceforce,所以开源的遗传功能将是研究简单遗传算法很好工具。AI-CODE近来在研究人工智能过程和坦克机器人时,发现国内也开发出了一个类似于 Robocode 仿真器的平台 AI-CODE,其思想延用 Robocode,但在 Robocode 基础上做了很多的改进,
52、封装了一些函数模块,让开发者更侧重于算法和程序设计的学习。最有意思的这个平台能同时支持 Java,C,C+,C# 语言,从理论上看它支持任何语言。美中不足的是国内应用的例子还不是很多,远没有 Robocode 那么多可参考的例子。如果大家有兴趣可尝试在 AI-CODE 平台上用不同语言做一些遗传算法的测试。我想能帮助更多人工智能爱好者。其相关网站大家可到 http:/ 去了解。附录资料:如何处理Java异常及常见异常六种异常处理的陋习你觉得自己是一个Java专家吗?是否肯定自己已经全面掌握了Java的异常处理机制?在下面这段代码中,你能够迅速找出异常处理的六个问题吗? 1 OutputStre
53、amWriter out = . 2 java.sql.Connection conn = . 3 try / 4 Statement stat = conn.createStatement(); 5 ResultSet rs = stat.executeQuery( 6 select uid, name from user); 7 while (rs.next() 8 9 out.println(ID: + rs.getString(uid) / 10 ,姓名: + rs.getString(name); 11 12 conn.close(); / 13 out.close(); 14 15
54、 catch(Exception ex) / 16 17 ex.printStackTrace(); /, 18 作为一个Java程序员,你至少应该能够找出两个问题。但是,如果你不能找出全部六个问题,请继续阅读本文。 本文讨论的不是Java异常处理的一般性原则,因为这些原则已经被大多数人熟知。我们要做的是分析各种可称为“反例”(anti-pattern)的违背优秀编码规范的常见坏习惯,帮助读者熟悉这些典型的反面例子,从而能够在实际工作中敏锐地察觉和避免这些问题。 反例之一:丢弃异常 代码:15行-18行。 这段代码捕获了异常却不作任何处理,可以算得上Java编程中的杀手。从问题出现的频繁程度和
55、祸害程度来看,它也许可以和C/C+程序的一个恶名远播的问题相提并论?不检查缓冲区是否已满。如果你看到了这种丢弃(而不是抛出)异常的情况,可以百分之九十九地肯定代码存在问题(在极少数情况下,这段代码有存在的理由,但最好加上完整的注释,以免引起别人误解)。 这段代码的错误在于,异常(几乎)总是意味着某些事情不对劲了,或者说至少发生了某些不寻常的事情,我们不应该对程序发出的求救信号保持沉默和无动于衷。调用一下printStackTrace算不上“处理异常”。不错,调用printStackTrace对调试程序有帮助,但程序调试阶段结束之后, printStackTrace就不应再在异常处理模块中担负主
56、要责任了。 丢弃异常的情形非常普遍。打开JDK的ThreadDeath类的文档,可以看到下面这段说明:“特别地,虽然出现ThreadDeath是一种正常的情形,但ThreadDeath类是Error而不是Exception的子类,因为许多应用会捕获所有的Exception然后丢弃它不再理睬。”这段话的意思是,虽然ThreadDeath代表的是一种普通的问题,但鉴于许多应用会试图捕获所有异常然后不予以适当的处理,所以JDK把 ThreadDeath定义成了Error的子类,因为Error类代表的是一般的应用不应该去捕获的严重问题。可见,丢弃异常这一坏习惯是如此常见,它甚至已经影响到了Java本身
57、的设计。 那么,应该怎样改正呢?主要有四个选择: 1、处理异常。针对该异常采取一些行动,例如修正问题、提醒某个人或进行其他一些处理,要根据具体的情形确定应该采取的动作。再次说明,调用printStackTrace算不上已经“处理好了异常”。 2、重新抛出异常。处理异常的代码在分析异常之后,认为自己不能处理它,重新抛出异常也不失为一种选择。 3、把该异常转换成另一种异常。大多数情况下,这是指把一个低级的异常转换成应用级的异常(其含义更容易被用户了解的异常)。 4、不要捕获异常。 结论一:既然捕获了异常,就要对它进行适当的处理。不要捕获异常之后又把它丢弃,不予理睬。 反例之二:不指定具体的异常 代
58、码:15行。 许多时候人们会被这样一种“美妙的”想法吸引:用一个catch语句捕获所有的异常。最常见的情形就是使用catch(Exception ex)语句。但实际上,在绝大多数情况下,这种做法不值得提倡。为什么呢? 要理解其原因,我们必须回顾一下catch语句的用途。catch语句表示我们预期会出现某种异常,而且希望能够处理该异常。异常类的作用就是告诉 Java编译器我们想要处理的是哪一种异常。由于绝大多数异常都直接或间接从java.lang.Exception派生,catch (Exception ex)就相当于说我们想要处理几乎所有的异常。 再来看看前面的代码例子。我们真正想要捕获的异常
59、是什么呢?最明显的一个是SQLException,这是JDBC操作中常见的异常。另一个可能的异常是IOException,因为它要操作OutputStreamWriter。显然,在同一个catch块中处理这两种截然不同的异常是不合适的。如果用两个catch块分别捕获SQLException和IOException就要好多了。这就是说,catch语句应当尽量指定具体的异常类型,而不应该指定涵盖范围太广的Exception类。 另一方面,除了这两个特定的异常,还有其他许多异常也可能出现。例如,如果由于某种原因,executeQuery返回了null,该怎么办?答案是让它们继续抛出,即不必捕获也不必
60、处理。实际上,我们不能也不应该去捕获可能出现的所有异常,程序的其他地方还有捕获异常的机会?直至最后由JVM处理。 结论二:在catch语句中尽可能指定具体的异常类型,必要时使用多个catch。不要试图处理所有可能出现的异常。 反例之三:占用资源不释放 代码:3行-14行。 异常改变了程序正常的执行流程。这个道理虽然简单,却常常被人们忽视。如果程序用到了文件、Socket、JDBC连接之类的资源,即使遇到了异常,也要正确释放占用的资源。为此,Java提供了一个简化这类操作的关键词finally。 finally是样好东西:不管是否出现了异常,Finally保证在try/catch/finally
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 冲击试验机建设项目可行性分析报告(总投资4000万元)
- 卧式多级离心泵项目可行性分析报告范文(总投资7000万元)
- 公务员考试热点纪检办案流程解读
- 交通规划师招聘面试题目参考集
- 三角铁项目可行性分析报告范文
- 银行信贷审查员面试题集及解析
- 深度解析(2026)《GBT 18459-2001传感器主要静态性能指标计算方法》
- 生物科技公司研发部主任面试问题集
- 特发性肺纤维化长期管理个体化方案优化
- 酒店前台服务面试考核全解析
- 孔隙率测定方法
- 2025 初中中国历史一二九运动的爆发课件
- 上消化道出血疾病宣教
- 飞模施工方案
- QA矩阵培训课件
- 作文可爱的家乡教学课件
- 警犬搜救训练课件
- 耳尖放血疗法课件
- 知道智慧树医学伦理学(山东大学)满分测试答案
- 知道智慧树生命科学与健康满分测试答案
- QGDW11970.1-2023输变电工程水土保持技术规程第1部分水土保持方案
评论
0/150
提交评论