目录
遗传算法(Genetic Algorithm,GA):模仿生物的遗传进化原理,通过选择、交叉、变异等操作,使得种群个体的适应性不断提高,物竞天择,适者生存。智能算法,全局搜索寻优。
遗传算法的特点如下:

遗传算法的应用领域比较广泛,具体涉及的应用领域如下:

我们看三个概念,即个体,种群,适应度。初始解组成的是个体,多个个体组成的称为种群,适应度是指衡量个体适应高低的,适应度函数一般就是指的目标函数。


下面看一下编码和解码的概念,本文中的编码和解码如下,编码是指将原来的个体改变成能进行选择,交叉,和变异等操作的染色体的过程。而解码就是将染色体转换成为原始的个体,即最优解。编码是遗传算法解决问题的先决条件和关键步骤,常用的编码方式有:二进制编码,浮点数编码等。一般来说,二进制编码比浮点型编码的搜索能力更强,浮点型编码在种群多样性的操作更具多样性。

下面看一下遗传操作的选择,交叉与变异。选择操作就是选择好的进行遗传,交叉就是随机配对,变异就是改变基因到其对应的基因。

遗传算法的基本过程如下:主要包括:收集问题参数并编码成染色体,初始化种族群体,通过选择交叉,变异等遗传操作更新群体,并计算群体的适应度,直到达到迭代次数或者适应度值不发生改变,则终止循环,并对染色体进行解码,即可得到问题的最优解。

下面看一下遗传算法的具体步骤:
首先,我们需要确定决策变量和约束条件,这个通常根据题意获得。然后建立模型,需要确定目标函数,然后需要找出个体,并对个体进行编码,编码是非常重要的,常用的编码方式是二进制编码。

标准的遗传算法基本是采用二进制编码,就是将个体(决策变量)用二进制串连在一起,构成染色体。编码完成,最后留下满足适应度函数的染色体后,还需要进行解码成个体,所以需要确定解码方法。其实就是还原成原来的决策变量。

然后,我们需要确定个体适应度的量化方法,即确定适应度函数,通过适应度函数去求解最优的决策变量,一般比较常用的就是直接将目标函数作为适应度函数,当然也有其它方法去确定适应度函数。


有了适应度函数后,我们就可以确定选择,交叉和变异的方式,个体选择常用的方法是按比例适应度分配,个体的适应度在总适应度的比例越大,即被选取的概率越大,遗产因子就会在种群中逐渐扩大。

计算出每个个体的选择概率后,我们就使用轮盘赌法进行选择,其实就是概率越大,轮盘所占比例越大,被选中的可能越大。
遗传的相关操作除了选择之外,还有交叉和变异,我们下面看一下交叉操作,交叉其实就是序列的交换,交叉率不宜过大,也不宜过小,一般取0.4-0.9。
下面看一下变异操作,对于二进制编码的变异,就是将1变成0,或者将0变成1,也应该选取合适的变异率,变异率过大和过小都不好。


遗传算法的具体步骤如下:首先对个体编码成染色体,初始化群体并计算解码后的适应值,然后进行遗传操作形成下一代群体, 判断是否满足要求,不满足则继续迭代,直到满足要求或者达到迭代次数。


用标准遗传算法求函数f(x)=x+10sin(5x)+7cos(4x) 的最大值,其中 x 的取值范围为[0,10]。这是一个有多个局部极值的函数。
下面是使用遗传算法求解该函数模型最大值的具体步骤:
(1)初始化种群数目为 NP=50,染色体的编码方式选择二级制编码,二进制编码长度为 L=20,遗传算法的最大进化迭代数为 G=100,交叉概率 取Pc=0.8,变异概率取Pm=0.1。
(2)产生初始种群,将二进制编码转换成十进制,计算个体适应度值,并进行归一化;采用基于轮盘赌的选择操作、基于概率的交叉和变异操作,产生新的种群,并把历代的最优个体保留在新种群中,进行下一步遗传操作。
(3)判断是否满足终止条件:若满足,则结束搜索过程,输出优化值;若不满足,则继续进行迭代优化。
优化结束后,其适应度进化曲线如下图所示,最优个体 x= 7.8569,函数 f(x)的最大值为 24.8554。这个值不是固定的,每次执行都会有些许波动。
遗传算法的具体流程图如下:

完整的matlab代码如下,注释很全:
- %遗传算法求函数极值
- clear
- clc
- NP = 50; %种群数量
- L = 20; %二进制位串长度
- Pc = 0.8; %交叉率
- Pm = 0.1; %变异率
- G = 100; %最大遗传迭代次数
- Xs = 10; %个体最小值
- Xx = 0; %个体最大值
- f = randi([0,1],NP,L); %随机获得初始种群
- %遗传算法
- for k = 1:G
- for i = 1:NP
- U = f(i,:); %遍历每个种群
- m = 0;
- for j = 1:L %遍历种群的每个二进制位
- m = U(j)*2^(j-1)+m ; %将二进制转换成十进制
- end
- x(i) = Xx+m*(Xs-Xx)/(2^L-1); %对染色体进行解码得到个体
- Fit(i) = func1(x(i)); %通过目标函数计算个体的适应度
- end
- maxFit = max(Fit); %适应度最大值
- minFit = min(Fit); %适应度最小值
- rr = find(Fit==maxFit); %找到适应度最大值的的位置
- fBest = f(rr(1,1),:); %最适应的种群
- xBest = x(rr(1,1)); %最适应的染色体,即个体
- Fit = (Fit-minFit)/(maxFit-minFit); %归一化适应度值
-
- %基于轮盘赌法的选择操作
- sum_Fit = sum(Fit) ; %对适应度进行求和
- fitvalue = Fit./sum_Fit; %求每个适应度被选中的概率
- fitvalue = cumsum(fitvalue) ; %得到累加后的概率
- ms = sort(rand(NP,1)) ; %随机生成概率值,并由小到大排序
- fiti = 1;
- newi = 1;
- while newi <= NP
- if ms(newi) < fitvalue(fiti) %如果随机生成的概率值小于被选中的概率
- nf(newi,:) = f(fiti,:); %找到选择出来的染色体
- newi = newi+1;
- else %不满足条件,继寻找合适的染色体
- fiti = fiti+1;
- end
- end
- %交叉操作,就是二进制对应0和1的互换
- for i = 1:2:NP %遍历种群
- p = rand ; %随机产生的一个概率
- if p > Pc %要保证随机产生的概率大于交叉率
- q = rand(1,L);
- for j = 1:L
- if q(j)==1 %只要随机产生的位置等于1,就进行交叉
- %将被选择的二级制进行交换,即交叉
- temp = nf(i+1,j);
- nf(i+1,j) = nf(i,j);
- nf(i,j) = temp;
- end
- end
- end
- end
- %变异操作,就是二进制取反操作
- i = 1;
- while i <= round(NP*Pm)
- h = randi([1,NP],1,1); %随机选取一个需要变异的染色体
- for j = 1:round(L*Pm)
- g = randi([1,L],1,1); %随机选取需要变异的基因数
- nf(h,g) =~ nf(h,g); %取反变异
- end
- i = i+1;
- end
- f = nf; %更新种群
- f(1,:) = fBest; %保留最优个体在新种群中
- trace(k) = maxFit; %保存最优的适应度
- end
- disp('最优个体如下:') ;
- disp(xBest) ;
- disp('函数最大值:') ;
- disp(func1(xBest)) ;
- figure
- plot(trace)
- xlabel('迭代次数')
- ylabel('目标函数值')
- title('适应度进化曲线')
适应度函数即目标函数如下:
- function result = func1(x)
- fit = x+10*sin(5*x)+7*cos(4*x);
- result = fit;
- end
迭代过程的适应度进行如下,最终的值在24.85左右,即函数的最值为24.85左右。

(1)初始化种群数目为 NP=100,染色体的编码方式选择二级制编码,二进制编码长度为 L=10,遗传算法的最大进化迭代数为 G=1000,交叉概率 取Pc=0.8,变异概率取Pm=0.1。
(2)产生初始种群,将二进制编码转换成十进制,计算个体适应度值;采用基于轮盘赌的选择操作、基于概率的交叉和变异操作,产生新的种群,并把历代的最优个体保留在新种群中,进行下一步遗传操作。
(3)判断是否满足终止条件:若满足,则结束搜索过程,输出优化值;若不满足,则继续进行迭代优化。
- %遗传算法求函数极值
- clear
- clc
- NP = 100; %种群数量
- L = 10; %二进制位串长度
- Pc = 0.8; %交叉率
- Pm = 0.1; %变异率
- G = 1000; %最大遗传迭代次数
- Xs = 20; %个体上限
- Xx = -20; %个体下线
- f = randi([0,1],NP,L); %随机获得初始种群
- %遗传算法
- for k = 1:G
- for i = 1:NP
- U = f(i,:); %遍历每个种群
- m = 0;
- for j = 1:L %遍历种群的每个二进制位
- m = U(j)*2^(j-1)+m ; %将二进制转换成十进制
- end
- x(i) = Xx+m*(Xs-Xx)/(2^L-1); %对染色体进行解码得到个体
- Fit(i) = -func2(x(i)); %通过目标函数计算个体的适应度
- end
- maxFit = max(Fit); %适应度最大值
- %minFit = min(Fit); %适应度最小值
- rr = find(Fit==maxFit); %找到适应度最小值的的位置
- fBest = f(rr(1,1),:); %最适应的种群
- xBest = x(rr(1,1)); %最适应的染色体,即个体
- % Fit = (Fit-minFit)/(maxFit-minFit); %归一化适应度值
-
- %基于轮盘赌法的选择操作
- sum_Fit = sum(Fit) ; %对适应度进行求和
- fitvalue = Fit./sum_Fit; %求每个适应度被选中的概率
- fitvalue = cumsum(fitvalue); %得到累加后的概率
- ms = sort(rand(NP,1)) ; %随机生成概率值,并由小到大排序
- fiti = 1;
- newi = 1;
- while newi <= NP
- if ms(newi) < fitvalue(fiti) %如果随机生成的概率值小于被选中的概率
- nf(newi,:) = f(fiti,:); %找到选择出来的染色体
- newi = newi+1;
- else %不满足条件,继寻找合适的染色体
- fiti = fiti+1;
- end
- end
- %交叉操作,就是二进制对应0和1的互换
- for i = 1:2:NP %遍历种群
- p = rand ; %随机产生的一个概率
- if p > Pc %要保证随机产生的概率大于交叉率
- q = rand(1,L);
- for j = 1:L
- if q(j)==1 %只要随机产生的位置等于1,就进行交叉
- %将被选择的二级制进行交换,即交叉
- temp = nf(i+1,j);
- nf(i+1,j) = nf(i,j);
- nf(i,j) = temp;
- end
- end
- end
- end
- %变异操作,就是二进制取反操作
- i = 1;
- while i <= round(NP*Pm)
- h = randi([1,NP],1,1); %随机选取一个需要变异的染色体
- for j = 1:round(L*Pm)
- g = randi([1,L],1,1); %随机选取需要变异的基因数
- nf(h,g) =~ nf(h,g); %取反变异
- end
- i = i+1;
- end
- f = nf; %更新种群
- f(1,:) = fBest; %保留最优个体在新种群中
- trace(k) = -maxFit; %保存最大的适应度
- end
- disp('最优个体如下:') ;
- disp(xBest) ;
- disp('函数最小值:') ;
- disp(func2(xBest)) ;
- figure
- plot(trace)
- xlabel('迭代次数')
- ylabel('目标函数值')
- title('适应度进化曲线')
适应度函数如下:
- function result=func2(x)
- summ=sum(x.^2);
- result=summ;
- end
运行结果如下:


[旅行商问题](TSP)。假设有一个旅行商人要拜访全国31个省会城市,他需要选择所要走的路径,路径的限制是每个城市只能拜访一次,而且最后要回到原来出发的城市。对路径选择的要求是:所选路径的路程为所有路径之中的最小值。全国31个省会城市的坐标为[1304 2312;3639 1315;4177 2244;37121399;3488 1535;3326 1556;3238 1229;4196 1004;4312 790;4386 570;3007 1970;2562 1756;2788 1491;2381 1676;1332695;3715 1678;3918 2179;4061 2370;3780 2212;3676 2578;4029 2838;4263 2931;3429 1908;3507 2367;3394 2643;34393201;2935 3240;3140 3550;2545 2357;2778 2826;2370 2975]。
(1)初始化种群数目为NP=200,最大遗传迭代数目为G=1000,染色体基因个数为N=31,即城市数目。
(2)产生初始种群,计算个体适应度值,即路径长度;采用基于概率的方式选择进行操作的个体;对选中的成对个体,随机交叉所选中的成对城市坐标,以确保交叉后路径每个城市只到访一次;对选中的单个个体,随机交换其一对城市坐标作为变异操作,产生新的种群,进行下一次遗传操作。
(3)判断是否满足终止条件:若满足,则结束搜索过程,输出优化值;若不满足,则继续进行迭代优化。
Matlab代码如下:
- %遗传算法解决TSP问题
- clear
- close all;
- clc;
- C=[1304 2312;3639 1315;4177 2244;3712 1399;3488 1535;3326 1556;...
- 3238 1229;4196 1044;4312 790;4386 570;3007 1970;2562 1756;...
- 2788 1491;2381 1676;1332 695;3715 1678;3918 2179;4061 2370;...
- 3780 2212;3676 2578;4029 2838;4263 2931;3429 1908;3507 2376;...
- 3394 2643;3439 3201;2935 3240;3140 3550;2545 2357;2778 2826;...
- 2370 2975];%省会城市坐标
- N=size(C,1);%C的行数,即城市数目
- D=zeros(N);%N*N的零矩阵,初始化任意两个城市距离间隔矩阵
- %求任意两个城市距离间隔矩阵
- for i=1:N
- for j=1:N
- D(i,j)=((C(i,1)-C(j,1))^2+(C(i,2)-C(j,2))^2)^0.5;
- end
- end
- NP=200;%种群数目
- G=1000;%最大遗传迭代数
- f=zeros(NP,N);%NP*N的零矩阵,用于存储种群
- F=[];%种群更新中间存储
- for i=1:NP
- f(i,:)=randperm(N);%1~N之间的随机数,随机生成初始种群
- end
- R=f(1,:);%用于存储最优种群
- len=zeros(NP,1);%用于存储路径长度
- fitness=zeros(NP,1);%用于存储归一化适应值
- gen=0;
- %遗传算法循环,小于最大遗传迭代次数,则继续更新下一代
- while gen<G
- %计算路径长度
- for i=1:NP
- len(i,1)=D(f(i,N),f(i,1));
- for j=1:(N-1)
- len(i,1)=len(i,1)+D(f(i,j),f(i,j+1));
- end
- end
- maxlen=max(len);%最长路径
- minlen=min(len);%最短路径
- %更新最短路径
- rr=find(len==minlen);
- R=f(rr(1,1),:);
- %计算归一化适应值
- for i=1:length(len)
- fitness(i,1)=(1-((len(i,1)-minlen)/(maxlen-minlen+0.001)));
- end
- %选择操作
- nn=0;
- for i=1:NP
- if fitness(i,1)>=rand
- nn=nn+1;
- F(nn,:)=f(i,:);
- end
- end
- [aa,bb]=size(F);
- while aa<NP
- nnper=randperm(nn);
- A=F(nnper(1),:);
- B=F(nnper(2),:);
- %交叉操作
- W=ceil(N/10);%交叉点个数
- p=unidrnd(N-W+1);%随机选择交叉范围,从p到p+W
- for i=1:W
- x=find(A==B(p+i-1));
- y=find(B==A(p+i-1));
- temp=A(p+i-1);
- A(p+i-1)=B(p+i-1);
- B(p+i-1)=temp;
- temp=A(x);
- A(x)=B(y);
- B(y)=temp;
- end
- %变异操作
- p1=floor(1+N*rand());
- p2=floor(1+N*rand());
- while p1==p2
- p1=floor(1+N*rand());
- p2=floor(1+N*rand());
- end
- tmp=A(p1);
- A(p1)=A(p2);
- A(p2)=tmp;
- tmp=B(p1);
- B(p1)=B(p2);
- B(p2)=tmp;
- F=[F;A;B];
- [aa,bb]=size(F);
- end
- if aa>NP
- F=F(1:NP,:);%保持种群规模为n
- end
- f=F;%更新种群
- f(1,:)=R;%保留每代最优个体
- clear F;
- gen=gen+1 ;
- Rlength(gen)=minlen;
- end
- figure
- for i=1:N-1
- plot([C(R(i),1),C(R(i+1),1)],[C(R(i),2),C(R(i+1),2)],'bo-');
- hold on;
- end
- title(['优化最短距离:',num2str(minlen)]);
- figure
- plot(Rlength)
- xlabel('迭代次数')
- ylabel('目标函数值')
- title('适应度进化曲线')
运行结果如下:


[0-1背包问题]。有N件物品和一个容量为V的背包。第i件物品的体积是c(i),价值是w(i)。求解将哪些物品放入背包可使物品的体积总和不超过背包的容量,且价值总和最大。假设物品数量为10,背包的容量为300。每件物品的体积为[95,75,23,73,50,22,6,57,89,98],价值为[89,59,19,43,100,72,44,16,7,64]。
(1)初始化种群数目为Np =50,染色体基因维数为L=10,最大进化代数为G=100。
(2)产生二进制初始种群,其中1表示选择该物品,0表示不选择该物品。取适应度值为选择物品的价值总和,计算个体适应度值,当物品体积总和大于背包容量时,对适应度值进行惩罚计算。
(3)对适应度进行归一化,采用基于轮盘赌的选择操作、基于概率的交叉和变异操作,产生新的种群,并把历代的最优个体保留在新种群中,进行下一步遗传操作。
(4)判断是否满足终止条件:若满足,则结束搜索过程,输出优化值;若不满足,则继续进行迭代优化。
matlab代码如下:
- %遗传算法解决0-1背包问题
- clear
- close all;
- clc;
- NP = 50;%种群规模
- L = 10;%物品件数
- Pc = 0.8;%交叉率
- Pm = 0.05;%变异率
- G = 100;%最大遗传代数
- V = 300;%背包容量
- C = [95,75,23,73,50,22,6,57,89,98]; %物品体积
- W = [89,59,19,43,100,72,44,16,7,64];%物品价值
- afa = 2; %惩罚函数系数
- f = randi([0,1],NP,L);%随机获得初始种群
- maxFit = 0 ;
- %遗传算法循环部分
- for k = 1:G
- %适应度计算,即计算总价值
- for i = 1:NP
- Fit(i) = func4(f(i,:),C,W,V,afa);
- end
- maxFit = max(Fit);%最大价值
- minFit = min(Fit);%最小价值
- rr = find(Fit==maxFit);
- fBest = f(rr(1,1),:);%历代最优个体
- Fit = (Fit - minFit)/(maxFit - minFit);%归一化适应度值
- %基于轮盘赌的选择
- sum_Fit = sum(Fit); %求适应度和
- fitvalue = Fit./sum_Fit;%求被选中的概率
- fitvalue = cumsum(fitvalue);%累加
- ms = sort(rand(NP,1)); %随机生成的概率,并按照由小到大排序
- fiti = 1;
- newi = 1;
- while newi <= NP
- if (ms(newi)) < fitvalue(fiti) %被选中的概率大于随机生成的
- nf(newi,:) = f(fiti,:); %选择复制
- newi = newi + 1;
- else
- fiti = fiti + 1;
- end
- end
- %基于概率的交叉操作
- for i = 1:2:NP
- p = rand;
- if p > Pc
- q = randi([0,1],1,L);
- for j = 1:L
- if q(j)==1;
- temp = nf(i + 1,j);
- nf(i + 1,j) = nf(i,j);
- nf(i,j) = temp;
- end
- end
- end
- end
- %基于概率的变异操作
- for m = 1:NP
- for n = 1:L
- r = rand(1,1);
- if r < Pm
- nf(m,n) = ~nf(m,n);
- end
- end
- end
- f = nf;
- f(1,:) = fBest; %保留最优个体在新种群中
- trace(k) = maxFit; %历代最优适应度
- end
- disp('最优个体如下:') ;
- disp(fBest) ;
- disp('最大价值如下:') ;
- disp(maxFit) ;
- figure
- plot(trace)
- xlabel('迭代次数')
- ylabel('目标函数值')
- title('适应度进化曲线')
适应度函数的代码如下:
- %适应度函数,求物品价值总和
- function result = func4(f,C,W,V,afa)
- fit = sum(f.*W); %物品价值总和
- TotalSize = sum(f.*C); %物品总体积
- if TotalSize <= V %物品总体积小于背包容量
- fit = fit;
- else %物品总体积大于背包容量
- fit = fit - afa * (TotalSize - V);
- end
- result = fit;
- end
运行结果如下:其实10个物品,1表示选择该物品,0表示不选择该物品,最后的最大总价值为388

