Matlab实验作业.docx
- 文档编号:29860569
- 上传时间:2023-07-27
- 格式:DOCX
- 页数:17
- 大小:108.02KB
Matlab实验作业.docx
《Matlab实验作业.docx》由会员分享,可在线阅读,更多相关《Matlab实验作业.docx(17页珍藏版)》请在冰豆网上搜索。
Matlab实验作业
MATLAB实验报告
姓名:
学号:
学院:
专业:
任课老师:
日期:
一、选择题目
所选题目为MATLAB试题中的第二题,题目内容如下:
B、设计遗传算法求解f(x)极小值,具体表达式如下:
要求必须使用m函数方式设计程序。
二、问题分析
本题目是一个很简单的数学问题,很显然f(x)的最小值为0。
但是本次实验要求为必须以matlab为实验平台,设计遗传算法对问题进行求解。
遗传算法是一种现代智能算法,实际上它的功能十分强大,能够用于求解一些难以用常规数学手段进行求解的问题,尤其适用于求解多目标、多约束,且目标函数形式非常复杂的优化问题。
但是遗传算法也有一些缺点,最为关键的一点,即没有任何理论能够证明遗传算法一定能够找到最优解,算法主要是根据概率论的思想来寻找最优解。
因此,遗传算法所得到的解只是一个近似解,而不一定是最优解。
鉴于遗传算法在求解上的巨大优势,现在对于遗传算法的研究也越来越多。
Matlab中有专门的遗传算法工具箱,我们只需要按照一定的格式将目标函数、约束条件进行输入,同时根据问题的需要对其中的一些参数进行设置,即可以运行遗传算法对问题进行求解。
在本实验中,要求我们自行设计遗传算法,因此,首先我们必须要弄懂遗传算法的基本原理,之后才能将问题转化成遗传算法能够处理的形式。
例如,我们需要知道怎么表达自变量,怎么表达目标函数,怎么对目标函数进行优化,等等。
在此基础上,我们才能够运用matlab编写正确的程序,对问题进行求解。
三、遗传算法简介
遗传算法是美国Michigan大学的Holland教授在20世纪60年代所提出来的。
遗传算法是模仿自然界生物进化机制发展起来的随机全局搜索和优化算法,它借鉴了达尔文的进化论和孟德尔的遗传学说。
遗传算法操作借鉴适者生存的原则,在每一代的进化中,得到的新个体比原个体更能适应环境,就像自然界中的改造一样。
遗传算法的基本思想为,通过某种编码机制产生一个初始种群,在每一代进化的过程中通过选择操作产生优良父辈,并运用交叉和变异操作产生子代,经过选择、交叉和变异,使群体中每一个个体的品质得到提高。
当种群进化达到预先设定的进化代数或者一定条件时,算法结束,并将最后得到的最优个体进行解码,以得到问题的解。
基本遗传算法(也称简单遗传算法)可以表示为下式:
式中
—个体的编码方法;
—个体适应度评价函数;
—初始种群;
—种群大小;
—选择算子;
—交叉算子;
—变异算子;
—遗传算法终止条件。
在遗传算法中,每个个体就代表问题的一个解,它是通过编码将解变成相应的染色体来表达的;适应度函数可以评价个体对于环境的适应程度,根据适应度值的大小,选择算子选择更能适应环境的个体作为下一代种群的父辈;再运用交叉算子对这些父辈进行交叉,从而产生下一代子代;考虑到自然界还存在基因突变,因此变异算子主要是使某个个体的基因产生突变,变异算子的存在使得遗传算法在理论上总可能找到最优解;当满足一定的条件时,结束遗传算法。
在遗传算法中,交叉算子是最为重要的,直接决定遗传算法的寻优能力。
根据上面的理论,我们在设计遗传算法时,需要考虑如下问题:
怎么编码使得可行解变成染色体;怎么将染色体转化成相应的解(解码);如何选择下一代父辈;如何对个体进行交叉和变异操作;算法何时终止。
这些都是我们需要考虑的问题。
在下面的内容中,将对这些部分分别进行说明。
四、算法设计
1编码与解码方式
本实验中,编码采用最为常用的二进制编码方式。
编码时必须考虑染色体的长度,即采用多少位数字进行编码。
题目中共有三个变量x1,x2,x3,取值范围都为[-5.12,5.12]。
根据精度要求,至少要求我们最终的解保留到第二位小数。
假设我们对每个变量采用N位数进行编码,则算法精度为:
要使上式的值小于
,要求我们至少对每个变量采用11位进行编码。
不过,由于当采用10位数进行编码时,精度也接近于
,因此也可以采用10位长度对变量进行编码。
在本实验中,编码位数是可以进行选择的。
为了表述方便,在本部分及下面的部分中都以每个变量为10位编码对算法进行说明。
每个变量均采用10位编码,则三个变量共需要30位数字,每个染色体的长度为30位。
染色体的前10位代表变量x1,中间10位代表x2,后10位代表x3。
例如下面的染色体:
101100101011001101001110110001
1011001010、1100110100和1110110001分别代表x1,x2和x3。
有了编码方式自然还需要有解码方式,即将相应的染色体解码成实际的值,以上面的染色体为例,x1的值分别为:
x2和x3的值也可以依据同样的公式算出来。
2.适应度函数
在本实验中,根据每个个体的染色体,我们可以将其解码,算出相应的每一个变量的值,继而我们可以算出此个体所代表的函数值。
题干中要求函数的最小值,则f(x)的值越小,相应的个体越适应环境,因此其适应度值也就越高。
在本例中,选择算子不是采用通常的轮盘赌方式,而是采用了一种改进的选择算子。
在选择时,我们只需要将种群中的每个体由好到劣进行排序。
要实现这一点,只需知道每个个体的目标函数值即可,因此本实验没有提出具体的适应度函数,个体的适应程度是通过相应的函数值来表征的。
3.遗传算子
遗传算子包括选择算子,交叉算子和变异算子,通过这三个算子,产生下一代种群。
在本实验中,不同于通常的遗传算子,这三个算子是紧紧联系在一起的,三者共同协作产生下一代种群。
下面对这个过程进行一个说明。
首先,假设我们有上一代的初始种群,设其规模为n,用farm来表示。
我们将这个种群的所有个体按照适应度值的大小由大到小进行排序(也就是目标函数值由小到大),然后进行复制操作,复制的方法是排前n/4的个体复制两份,排在中间n/2的个体复制一份,排在后面n/4的个体删除。
这样经过复制操作后,我们可以得到一个种群数量仍然保持为n的种群,不妨设其为farm1。
采用这种复制方法的好处是,初期可以淘汰适应度值较低的个体,同时又避免初期适应度值高的个体迅速占据种群,使得算法陷入局部最优解。
算法后期仍然可以保持种群的多样性。
有了farm1之后,我们对其进行交叉操作,操作过程为:
(1)产生两个随机数,根据这两个随机数从farm1中选择两个个体进行交叉,得到两个子代,将其存入farm2;
(2)重复上述过程,直到farm2中的个体仍然保持为n。
这样,farm2中的个体全部是通过交叉得到的,且其交叉父辈都来自于上一代的优良个体(farm1)。
在交叉操作中,我们没有选择一般的两点交叉,而是选择了混合交叉方式。
具体做法是,首先产生两个交叉点位,D1和D2,且D1 再产生一个随机数,其取值范围为{0,1,2}。 如果随机数为1,则将两个染色体在D1之前的部分进行交换;如果随机数为2,则将两个染色体在位于D1与D2之间的基因进行交换;如果随机数为3,则将两个染色体在D2之后的部分进行交换。 这样做是为了保证交叉的充分性,尽可能的产生新个体、优异的个体,以保持种群的多样性,保证种群不断进化。 得到farm2后,我们再对farm2进行变异操作。 变异是为了保持种群的多样性,但是变异有可能使得变异后的个体适应值降低,使得种群退化,因此一定要控制变异率。 在本实验中,变异率是可以进行更改的,方便我们进行选择。 假设变异率为p,每个个体的染色体长度为30,则在每一代中平均共有n*30*p个基因要产生变异。 变异即是将相应的基因位由0变成1或由1变成0。 变异操作的过程为,对于farm2中的每个基因,产生一个随机数,若其小于p,则将相应的基因进行更改。 进行完变异操作后,我们可以得到变异后的种群farm3,它是在farm2的基础上经过变异得到的。 经过上述三个步骤后,可以得到farm3,我们将farm3和初始种群farm合并在一起,共有2*n个个体。 然后将这2*n个个体按照函数值由小到大进行排序,取排在前n位的个体作为最终的下一代种群。 经过上述操作,我们产生的新种群既保持上一代中的优良个体,而且由于我们进行了十分充分的交叉操作,因此也产生许多新的优良个体。 这可以大大加快算法的收敛速度,同时仍然保持了种群的多样性,保证可以搜寻到最优解。 4.算法终止条件 算法的终止条件一般可以分为最大代数终止或者满足收敛条件终止。 当运行代数设置的较大时,可能在较早的时候算法已经收敛了,后面的代数种群维持原状不再进化,这时候可以考虑采用一定的收敛条件来终止算法。 在本例中对收敛条件不做探讨,终止条件选择最大代数终止。 本实验中,最大代数是可以进行更改的。 五、实验过程及相关记录 1.实验操作步骤 本实验主要通过编写matlab程序,在matlab中运行得以实现。 程序运行的步骤如下流程图所示: 2.程序代码 本实验中的程序代码如下所示,由一个主程序和五个子程序组成,程序中对重要部分进行了注释说明。 程序1: 主程序 %n表示种群规模 %N表示每个变量的基因长度,染色体长度则为3*N,最好不小于10 %C为最大进化代数,即终止条件 %basevalue即为变量取值的下界,本例中为-5.12 %mylength为变量取值的区间长度,即最大值减最小值,本例为10.24 %p表示变异概率,最好不超过0.01 %Y存储最终种群中所得到最优个体的目标函数值,X存储相应的变量值 function[YX]=mygenitic(n,N,C,basevalue,mylength,p) farm=round(rand(n,3*N));%farm存储初始种群 counter=0;%counter记录进化代数 whilecounter obfunc_value1=myobfunctionvalue(farm,n,N,mylength,basevalue); %计算种群中各染色体的目标函数值,调用程序2 FARM1=mycopy(farm,obfunc_value1,n); %上一代种群经复制操作产生FARM1,调用程序3 FARM2=mycross(FARM1,n,N); %FARM1经过交叉操作得到FARM2,调用程序4 FARM3=mymutation(FARM2,n,N,p); %FARM2经过变异操作得到FARM3,调用程序5 FARM4=[farm;FARM3];%上一代种群farm和FARM3连接组合成FARM4 obfunc_value2=myobfunctionvalue(FARM4,2*n,N,mylength,basevalue); %计算FARM4中所有染色体的目标函数值,调用程序2 [pxz,pxindex]=sort(obfunc_value2);%对FARM4中的目标函数值排序 %将FARM4中排在前n为的个体传给farm,得到下一代新种群 fori=1: n farm(i,: )=FARM4(pxindex(i),: ); end counter=counter+1;%进行下一次进化 end %计算最终种群所有染色体的目标函数值 obfunc_value=myobfunctionvalue(farm,n,N,mylength,basevalue); [Ymincite]=min(obfunc_value);%Y存储最小的目标函数值 X=mydecode(farm(mincite,: ),N,basevalue,mylength); %X存储相应的变量值,调用程序6 end 程序2: 计算染色体的目标函数值 %输入一个种群,及相应的n,N,mylength和basevalue值 %输出该种群每个染色体的目标函数值,为列向量 functionfunctionvalue=myobfunctionvalue(farm,n,N,mylength,basevalue) functionvalue=zeros(n,1); fori=1: n x=mydecode(farm(i,: ),N,basevalue,mylength);%解码计算自变量值,调用程序6 functionvalue(i,1)=x (1)^2+x (2)^2+x(3)^2; end end 程序3: 复制产生新种群 %farm为输入种群,functionvalue为其对应的目标函数值 %n为种群规模 %copyfarm为输入种群经复制操作后所得到新种群 functioncopyfarm=mycopy(farm,functionvalue,n) [sortfunc,funcindex]=sort(functionvalue); %对输入种群的个体按目标函数值由小到大进行排序,并记录相应的索引号 c=fix(n/4);%将种群规模分为四等份 copyfarm=farm; fori=1: 3*c%排在前3/4的个体复制一份 copyfarm(i,: )=farm(funcindex(i,1),: ); end fori=1: c%排在前1/4的个体再复制一根,相当于复制两份 copyfarm(3*c+i,: )=farm(funcindex(i,1),: ); end if4*c jsq1=4*c; whilejsq1 jsq1=jsq1+1; copyfarm(jsq1,: )=farm(funcindex(jsq1-4*c,1),: ); end end end 程序4: 交叉产生新种群 %farm为输入种群,n为其种群规模,N为每个变量的基因长度 %crossfarm输出farm经交叉后所得到的新种群 functioncrossfarm=mycross(farm,n,N) crossfarm=zeros(2*(fix(n/2)+1),3*N);%crossfarm用于存储输出种群 fori=1: fix(n/2)+1 nper=randperm(n); a=farm(nper (1),: );%随机的从farm中取出一个染色体 b=farm(nper (1),: );%随机的从farm中再取出一个染色体 jxd1=fix(rand*(3*N-2))+2;jxd2=fix(rand*(3*N-2))+2;%产生两个交叉点 whilejxd1==jxd2 jxd2=fix(rand*(3*N-2))+2;%确保两个交叉点不一样 end ifjxd1>jxd2%确保第一个交叉点位置小于第二个 term1=jxd1; jxd1=jxd2; jxd2=term1; end pbs1=fix(rand*3);%随机选择交叉方式 ifpbs1==0%单点交叉,前部分交换 term2=a; forj=1: jxd1-1 a(j)=b(j); b(j)=term2(j); end elseifpbs1==1%两点交叉,中间部分交换 termp=a; forj=jxd1: jxd2 a(j)=b(j); b(j)=termp(j); end elseifpbs1==2%单点交叉,后部分交换 termp=a; forj=jxd2+1: 3*N a(j)=b(j); b(j)=termp(j); end end crossfarm(2*i-1,: )=a;%将交叉后的a存入crossform crossfarm(2*i,: )=b;%将交叉后的b存入crossform end crossfarm=crossfarm(1: n,: );%确保crossform行数为n end 程序5: 变异产生新种群 %farm为输入种群 %n为输入的种群规模,N代表每个变量的基因长度 %p为所选择的变异率 %输出为进过变异之后的种群 functionmutationfarm=mymutation(farm,n,N,p) mutationfarm=farm;%存储变异种群 fori=1: 3*n*N%对所有基因进行判断 ifrand gt=fix(i/(3*N))+1;cite=mod(i,3*N);%记录该基因的行和列 ifcite==0%列为0时需进行变换 gt=gt-1; cite=3*N; end %进行变异操作 ifmutationfarm(gt,cite)==0 mutationfarm(gt,cite)=1; else mutationfarm(gt,cite)=0; end end end end 程序6: 对染色体进行解码 %输入为一个染色体,及每个变量基因长度 %decodex输出该染色体所代表的三个自变量值,即进行解码操作 functiondecodex=mydecode(rst,N,basevalue,mylength) decodex=zeros(1,3);%decodex用于存储三个变量值 forj=1: N decodex (1)=decodex (1)+rst(j)*2^(j-1); end forj=N+1: 2*N decodex (2)=decodex (2)+rst(j)*2^(j-N-1); end forj=2*N+1: 3*N decodex(3)=decodex(3)+rst(j)*2^(j-2*N-1); end decodex (1)=basevalue+decodex (1)*mylength/(2^N-1);%得到x1的值 decodex (2)=basevalue+decodex (2)*mylength/(2^N-1);%得到x2的值 decodex(3)=basevalue+decodex(3)*mylength/(2^N-1);%得到x3的值 end 3.异常情况记录及处理 在运用matlab编写程序进行运算时,遇到了一些问题,如下: (1)由于本实验包括6个程序,每个程序当中都有许多的变量名。 由于程序中有些变量的名字特别长,因此在引用时总会因为输出其中某一个字母而在运行时出错。 这当然可以通过修改得以更正,不过也提醒我以后在对变量名进行命名时不要使用过长的变量名。 (2)本实验在编写过程中遇到的最大一个问题是,在编写完程序,进行运算时,matlab很久都没有反应,然后在下方的窗口中一直显示“busy”。 刚开始我以为是由于遗传代数过多、染色体长度太长导致运行时间过久。 于是我等了一会,可是过了很久结果还是没有运行出来。 我强制关掉程序,仔细的检查了代码,并没有发现错误,可再次运行时还是出现同样的问题,运算不出结果。 最后我设置了一些断点,也就是去掉一些分号,让中间结果输出到命令窗口。 运行后发现命令窗口中一直在不停的输出中间结果。 按“Ctrl+c”暂停之后,我知道程序中可能出现了死循环。 于是在有循环的语句中去找错误,结果发现主程序中的counter=counter+1这一条语句没有写进去,致使循环无法结束。 问题最终得以解决。 由于粗心我花了好大功夫去寻找错误,这是一个教训,也是一次收获,下次再遇到这种情况我不会再犯类似的错误了。 六、实验结果与总结 1.程序运行结果展示 在matlab运行程序的语句为: function[YX]=mygenitic(n,N,C,basevalue,mylength,p) 上述参数的含义前面已经有过解释。 在本实验中,我们将种群规模n设为20,变异率p设为0.01,其它值根据题目的要求,basevalue设为-5.12,mylength设为10.24。 因此在实验中我们只对个体基因长度N和遗传代数进行更改。 将基因长度设置为10,遗传代数分别设置为10,20,30,40,50,60,70,80,90,100,运行结果如下表所示: 编号 最大代数 Y x1 x2 x3 1 10 0.365200000 -0.36536 0.425415 0.22522 2 20 0.085000000 0.115112 0.22522 -0.14514 3 30 0.034700000 0.165161 -0.01501 0.085083 4 40 0.000075147 -0.005 -0.005 -0.005 5 50 0.000075147 -0.005 0.005 0.005 6 60 0.000075147 -0.005 -0.005 0.005 7 70 0.000075147 0.005 -0.005 -0.005 8 80 0.000075147 0.005 0.005 0.005 9 90 0.000075147 -0.005 0.005 -0.005 10 100 0.000075147 0.005 -0.005 0.005 从上表可以看出,在进行到第40代左右时,算法已经收敛,这说明本算法的收敛速度是十分迅速的,这主要得益于本实验中所设计的三个遗传算子。 从结果上看,到第40代时,函数值已经稳定在0.000075147,三个变量取值的绝对值都为0.005,这已经非常接近我们的实际结果。 然后,我们将最大遗传代数固定为100,对个体基因长度进行修改,分别设其为10,11,12,13,14,15,运行程序,得到的结果如下表所示: 编号 变量基因长度 Y x1 x2 x3 1 10 0.000075147 -0.00500 -0.00500 0.00500 2 11 0.000018768 -0.00250 -0.00250 -0.00250 3 12 0.000004690 0.00125 0.00125 0.00125 4 13 0.000001172 -0.00063 0.00063 0.00063 5 14 0.000000293 -0.00031 0.00031 -0.00031 从上表可以看到,随着染色体长度的增加,Y的值从0.000075147下降到0.000000293,单变量取值的绝对值由0.005下降到0.00031,越来越逼近于我们的实际结果,这说明基因长度的增加可以提高遗传算法的运算精度。 不过,过长的编码会增加程序的运行时间。 这也是我们在选择参数时需要综合考虑的问题。 2.总结 本实验主要是要求以matlab为实验平台,采用遗传算法求解一个简单的目标最小化函数,程序运行的最终结果表明,遗传算法能够无限的接近于计算出一个最优解。 本实验中所设计的程序采用了改进的遗传算法,不仅加快了算法的收敛速度,同时依旧保持了种群的多样性,保证算法能够找到最优解。 遗传算法只是一种求解手段,本实验的主要目的是为了让我们通过设计一个遗传算法程序,以达到比较熟练使用matlab撰写程序的效果。 在编写程序的过程中,我体会到了matlab是一种很好的数值计算软件。 它的语言简洁易懂,十分容易掌握;Matlab有比较完善的纠错机制,方便我们对程序进行调试;另外,matlab的计算能力十分强大,运算速度非常快。 当然,matlab也有十分强大的绘图本领,同时有许多
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Matlab 实验 作业
![提示](https://static.bdocx.com/images/bang_tan.gif)