遺傳算法就是在一定的自變量有限的取值範圍內,隨機取若干個個體,每個個體相當於自變量範圍內的一個取值,若干個個體共同組成一個種羣,個體對於環境的適應能力體現爲該個體對應的因變量,不同的個體得到的結果不同,對於結果較好的個體,其下一代在種羣中的佔比更高,對於結果不好的個體,其下一代在種羣中的佔比會更少,簡單來說,就是好的個體被保留,壞的個體被淘汰。經過不斷的更新換代,最後結果會不斷逼近最優的結果。
同時參考自然界種羣的進化過程,除了物競天擇這一思想外,還有基因的突變和基因的交叉互換來探索更多的優秀的形狀,從而提高種羣的適應性。所以在遺傳算法中也參考了這種思想,在遺傳算法中,一個個體由能代表該個體屬性的多個二進制編碼構成。具有相同屬性的個體二進制編碼相同。在每一輪的種羣更新中,都會進行一次交叉互換和一次基因變異。
在進行交叉互換時,根據交叉互換的概率,對隨機的個體與另外一個隨機的個體進行一次基因的交叉互換,即隨機取基因的某一處斷開,將斷開後的基因片段進行交換,再組合成新的片段。
在進行變異的時候,根據變異的概率,對隨機的個體的基因中的隨機某個基因進行取反,得到新的基因。
那麼一個遺傳算法的過程就是以上的流程。下面具體談談怎麼實現。
一個個體的性狀的表達,是根據個體的基因屬性所對應的環境的適應度。以求某函數的最大值爲例,個體的不同基因屬性代表了不同的自變量取值,該自變量對應的函數結果就是該個體在整個環境中的性狀的表達。環境對該性狀的選擇就是通過數學方法來將結果與最理想的結果進行一個評估,例如當求最大值時,就可以讓函數結果大的自變量取值儘可能的保留,結果小的取值儘可能拋棄,這就是自然選擇的過程。
這裏在進行自然選擇的時候,用到了輪盤賭的數學模型。輪盤賭是一個有着不同面積區塊的圓形轉盤,每次轉動停到的位置都是隨機的,那麼最後的結果就是根據轉盤中的區塊的面積佔比來決定每次轉動出現的各個結果的概率。這是一個很簡單的數學模型。在自然選擇過程中,就可以通過這樣的方式來淘汰形狀較差的個體。具體方法就是,每個個體都有一個數值作爲其對環境的適應能力的體現,所有個體的適應力數值加在一起就是一整個轉盤的面積1,各個個體的適應力比上所有的適應力之和就是該個體區域面積佔整個轉盤面積的大小。數值越大,對應的就是轉盤上的面積越大,隨機取一個數,落在該區域的概率就越大,因此適應性強的將更有概率出現在下一代。這就是輪盤賭。
以上一個遺傳算法的基本原理過程就搞清楚了。
然後就是代碼部分
主程序:
clc;clear;close all;
%%
% f= @(a,b)(1/2*a .* sin(a) .* cos(1/2 * a) ).*(1/2*b .* sin(b) .* cos(1/2 * b) ); % 函數表達式
f= @(a,b)(10*sin(a)+7*abs(a-5)+10).*(10*sin(b)+7*abs(b-5)+10); % 函數表達式
figure(1);
[x0_1, x0_2]=meshgrid(0:.2:20);
y0=f(x0_1,x0_2);
mesh(x0_1, x0_2, y0);
hold on;
N = 50; % 初始種羣個數
chromosome = 2; % 空間維數
ger = 50; % 最大迭代次數
chromlength =[16,16];
limit = [0, 20;0, 20];
pc = 0.3; % 交叉概率
pm = 0.008; % 變異概率
all_best=0;
all_x=0;
%%
% for i=1:chromosome
% eval(['POP.chrom',num2str(i),'=round(rand(N,chromlength(i)));']);
% for j=1:N
% eval(['POP.x',num2str(i),'(j)=(binary2decimal(POP.chrom1(j,:)))/(2^chromlength(i)-1)*(limit(i,2)-limit(i,1)) ;'])
% % POP.x'i'(j)=(binary2decimal(POP.chrom1(j,:)))/(2^chromlength(i)-1)*(limit(i,2)-limit(i,1)) ;
% end
% end
POP.chrom1=round(rand(N,chromlength(1)));
POP.chrom2=round(rand(N,chromlength(2)));
%%
% 開始迭代
for n=1:ger
%%
% 交叉互換
for i = 1:2:N-1
if(rand<pc)
cpoint = round(rand*chromlength);
POP.NEWchrom1(i,:) = [POP.chrom1(i,1:cpoint(1)),POP.chrom1(i+1,cpoint(1)+1:chromlength(1))];
POP.NEWchrom1(i+1,:) = [POP.chrom1(i+1,1:cpoint(1)),POP.chrom1(i,cpoint(1)+1:chromlength(1))];
POP.NEWchrom2(i,:) = [POP.chrom2(i,1:cpoint(2)),POP.chrom2(i+1,cpoint(2)+1:chromlength(2))];
POP.NEWchrom2(i+1,:) = [POP.chrom2(i+1,1:cpoint(2)),POP.chrom2(i,cpoint(2)+1:chromlength(2))];
else
POP.NEWchrom1(i,:) = POP.chrom1(i,:);
POP.NEWchrom1(i+1,:) = POP.chrom1(i+1,:);
POP.NEWchrom2(i,:) = POP.chrom2(i,:);
POP.NEWchrom2(i+1,:) = POP.chrom2(i+1,:);
end
end
% 根據交叉互換的結果 更新種羣的基因
POP.chrom1=POP.NEWchrom1;
POP.chrom2=POP.NEWchrom2;
%%
% 基因變異並更新種羣
for i=1:N
for j1=1:chromlength(1)
if(rand<pm)
POP.chrom1(i,j1)=~POP.chrom1(i,j1);
end
end
for j2=1:chromlength(2)
if(rand<pm)
POP.chrom2(i,j2)=~POP.chrom2(i,j2);
end
end
end
%%
% 形狀表達與選擇
% 將基因(二進制編碼)轉化爲自變量的取值(10進制的數)
for i=1:N
POP.x1(i)=(binary2decimal(POP.chrom1(i,:)))/(2^chromlength(1)-1)*(limit(1,2)-limit(1,1)) ;
end
for i=1:N
POP.x2(i)=(binary2decimal(POP.chrom2(i,:)))/(2^chromlength(2)-1)*(limit(2,2)-limit(2,1)) ;
end
% 根據自變量的取值得到函數的輸出
for i=1:N
POP.y(i)=f(POP.x1(i),POP.x2(i));
end
%將輸出的結果單位化,轉化爲0-1之間的數值長度(相當於輪盤賭的各個區域的面積)
a=min(POP.y);
b=sum(POP.y)+N*(-a);
for i=1:N
POP.adapt(i)=(POP.y(i)-a)/b;
end
%數值長度轉換爲0-1之間的區間的節點(相當於把面積轉化爲了輪盤賭上各個區域的邊界線)
POP.NWEadapt(1)=POP.adapt(1);
for i=2:N
POP.NWEadapt(i)=POP.adapt(i)+POP.NWEadapt(i-1);
end
%進行輪盤賭,任取一個隨機數cs,求這個隨機數在輪盤賭中的位置區域
%到達某個區域,就代表下一個種羣在第i個個體就擁有該區域所表示的基因,從而得到新種羣
for i=1:N
cs=rand;
[a,b]=POP_erfen(1,N,POP.NWEadapt,cs);
POP.NEWchrom1(i,:)=POP.chrom1(b,:);
POP.NEWchrom2(i,:)=POP.chrom2(b,:);
end
%更新種羣
POP.chrom1=POP.NEWchrom1;
POP.chrom2=POP.NEWchrom2;
%%
% 獲得最優位置
[this_x this_best]=best(POP)
if all_best<this_best
all_best=this_best;
all_x=this_x;
end
cla;
mesh(x0_1, x0_2, y0);
plot3(POP.x1,POP.x2,POP.y, 'ro');title('狀態位置變化');
pause(0.1);
end
figure();
mesh(x0_1, x0_2, y0);
hold on;
plot3(all_x(1),all_x(2),all_best, 'ro');title('最優位置圖');
disp(['最大值:',num2str(all_best)]);
disp(['變量取值:',num2str(all_x)]);
另外在代碼中用了兩個自己寫的函數,一個是二分法求隨機數在輪盤賭中的位置,一個是求種羣中的最優個體:
二分法:
function [Nmin,Nmax]=POP_erfen(min,max,fun,tal)
if tal<fun(1)
Nmin=0;
Nmax=1;
else
a=min;
b=max;
if (b-a)<=1
Nmin=a;
Nmax=b;
else
c=round((a+b)/2);
if tal>=fun(c)
a=c;
b=b;
[Nmin,Nmax]=POP_erfen(a,b,fun,tal);
else
a=a;
b=c;
[Nmin,Nmax]=POP_erfen(a,b,fun,tal);
end
end
end
end
最優解:
function [bestindividual bestfit] = best(POP)
[px,py] = size(POP.y);
bestindividual = [POP.x1(1) , POP.x2(1)];
bestfit = POP.y(1);
for i = 2:px
if POP.y(i)>bestfit
bestindividual = [POP.x1(i) , POP.x2(i)];
bestfit = POP.y(i);
end
end
二進制轉十進制函數:
%二進制轉化成十進制函數
%輸入變量:
%二進制種羣
%輸出變量
%十進制數值
function pop2 = binary2decimal(pop)
[px,py]=size(pop);
for i = 1:py
pop1(:,i) = 2.^(py-i).*pop(:,i);
end
%sum(.,2)對行求和,得到列向量
pop2 = sum(pop1,2);
(後面打算把整個過程用GIF圖表現出來,未完待續)