一、简介
1 遗传算法概述
遗传算法(Genetic Algorithm,GA)是进化计算的一部分,是模拟达尔文的遗传选择和自然淘汰的生物进化过程的计算模型,是一种通过模拟自然进化过程搜索最优解的方法。该算法简单、通用,鲁棒性强,适于并行处理。
2 遗传算法的特点和应用
遗传算法是一类可用于复杂系统优化的具有鲁棒性的搜索算法,与传统的优化算法相比,具有以下特点:
(1)以决策变量的编码作为运算对象。传统的优化算法往往直接利用决策变量的实际值本身来进行优化计算,但遗传算法是使用决策变量的某种形式的编码作为运算对象。这种对决策变量的编码处理方式,使得我们在优化计算中可借鉴生物学中染色体和基因等概念,可以模仿自然界中生物的遗传和进化激励,也可以很方便地应用遗传操作算子。
(2)直接以适应度作为搜索信息。传统的优化算法不仅需要利用目标函数值,而且搜索过程往往受目标函数的连续性约束,有可能还需要满足“目标函数的导数必须存在”的要求以确定搜索方向。遗传算法仅使用由目标函数值变换来的适应度函数值就可确定进一步的搜索范围,无需目标函数的导数值等其他辅助信息。直接利用目标函数值或个体适应度值也可以将搜索范围集中到适应度较高部分的搜索空间中,从而提高搜索效率。
(3)使用多个点的搜索信息,具有隐含并行性。传统的优化算法往往是从解空间的一个初始点开始最优解的迭代搜索过程。单个点所提供的搜索信息不多,所以搜索效率不高,还有可能陷入局部最优解而停滞;遗传算法从由很多个体组成的初始种群开始最优解的搜索过程,而不是从单个个体开始搜索。对初始群体进行的、选择、交叉、变异等运算,产生出新一代群体,其中包括了许多群体信息。这些信息可以避免搜索一些不必要的点,从而避免陷入局部最优,逐步逼近全局最优解。
(4) 使用概率搜索而非确定性规则。传统的优化算法往往使用确定性的搜索方法,一个搜索点到另一个搜索点的转移有确定的转移方向和转移关系,这种确定性可能使得搜索达不到最优店,限制了算法的应用范围。遗传算法是一种自适应搜索技术,其选择、交叉、变异等运算都是以一种概率方式进行的,增加了搜索过程的灵活性,而且能以较大概率收敛于最优解,具有较好的全局优化求解能力。但,交叉概率、变异概率等参数也会影响算法的搜索结果和搜索效率,所以如何选择遗传算法的参数在其应用中是一个比较重要的问题。
综上,由于遗传算法的整体搜索策略和优化搜索方式在计算时不依赖于梯度信息或其他辅助知识,只需要求解影响搜索方向的目标函数和相应的适应度函数,所以遗传算法提供了一种求解复杂系统问题的通用框架。它不依赖于问题的具体领域,对问题的种类有很强的鲁棒性,所以广泛应用于各种领域,包括:函数优化、组合优化生产调度问题、自动控制
、机器人学、图像处理(图像恢复、图像边缘特征提取…)、人工生命、遗传编程、机器学习。
3 遗传算法的基本流程及实现技术
基本遗传算法(Simple Genetic Algorithms,SGA)只使用选择算子、交叉算子和变异算子这三种遗传算子,进化过程简单,是其他遗传算法的基础。
3.1 遗传算法的基本流程
通过随机方式产生若干由确定长度(长度与待求解问题的精度有关)编码的初始群体;
通过适应度函数对每个个体进行评价,选择适应度值高的个体参与遗传操作,适应度低的个体被淘汰;
经遗传操作(复制、交叉、变异)的个体集合形成新一代种群,直到满足停止准则(进化代数GEN>=?);
将后代中变现最好的个体作为遗传算法的执行结果。
其中,GEN是当前代数;M是种群规模,i代表种群数量。
3.2 遗传算法的实现技术
基本遗传算法(SGA)由编码、适应度函数、遗传算子(选择、交叉、变异)及运行参数组成。
3.2.1 编码
(1)二进制编码
二进制编码的字符串长度与问题所求解的精度有关。需要保证所求解空间内的每一个个体都可以被编码。
优点:编、解码操作简单,遗传、交叉便于实现
缺点:长度大
(2)其他编码方法
格雷码、浮点数编码、符号编码、多参数编码等
3.2.2 适应度函数
适应度函数要有效反映每一个染色体与问题的最优解染色体之间的差距。
3.2.3选择算子
3.2.4 交叉算子
交叉运算是指对两个相互配对的染色体按某种方式相互交换其部分基因,从而形成两个新的个体;交叉运算是遗传算法区别于其他进化算法的重要特征,是产生新个体的主要方法。在交叉之前需要将群体中的个体进行配对,一般采取随机配对原则。
常用的交叉方式:
单点交叉
双点交叉(多点交叉,交叉点数越多,个体的结构被破坏的可能性越大,一般不采用多点交叉的方式)
均匀交叉
算术交叉
3.2.5 变异算子
遗传算法中的变异运算是指将个体染色体编码串中的某些基因座上的基因值用该基因座的其他等位基因来替换,从而形成一个新的个体。
就遗传算法运算过程中产生新个体的能力方面来说,交叉运算是产生新个体的主要方法,它决定了遗传算法的全局搜索能力;而变异运算只是产生新个体的辅助方法,但也是必不可少的一个运算步骤,它决定了遗传算法的局部搜索能力。交叉算子与变异算子的共同配合完成了其对搜索空间的全局搜索和局部搜索,从而使遗传算法能以良好的搜索性能完成最优化问题的寻优过程。
3.2.6 运行参数
4 遗传算法的基本原理
4.1 模式定理
4.2 积木块假设
具有低阶、定义长度短,且适应度值高于群体平均适应度值的模式称为基因块或积木块。
积木块假设:个体的基因块通过选择、交叉、变异等遗传算子的作用,能够相互拼接在一起,形成适应度更高的个体编码串。
积木块假设说明了用遗传算法求解各类问题的基本思想,即通过积木块直接相互拼接在一起能够产生更好的解。
二、源代码
function GaTSPChen
% mainly amended by Chen Zhen, 2012~2016
CityNum = 30; % 城市数目,可以选 10, 30, 50, 75
[dislist, Clist] = tsp(CityNum); % dislist 为城市之间相互的距离,Clist 为各城市的坐标
inn = 30; % 初始种群大小
gnMax = 500; % 最大代数
crossProb = 0.8; % 交叉概率
muteProb = 0.8; % 变异概率
% 随机产生初始种群
population = zeros(inn, CityNum); % population 为初始种群,包括多条染色体
for i = 1 : inn
population(i,:) = randperm(CityNum);
end
[~, cumulativeProbs] = calPopulationValue(population, dislist); % 计算种群每条染色体的累计概率
generationNum = 1;
generationMeanValue = zeros(generationNum, 1); % 每一代的平均距离
generationMaxValue = zeros(generationNum, 1); % 每一代的最短距离
bestRoute = zeros(inn, CityNum); % 最佳路径
newPopulation = zeros(inn, CityNum); % 新的种群
while generationNum < gnMax + 1
for j = 1 : 2 : inn
selectedChromos = select(cumulativeProbs); % 选择操作,选出两条需要交叉编译的染色体,即父亲母亲
crossedChromos = cross(population, selectedChromos, crossProb); % 交叉操作,返回交叉后的染色体
newPopulation(j, :) = mut(crossedChromos(1, :),muteProb); % 对交叉后的染色体进行变异操作
newPopulation(j + 1, :) = mut(crossedChromos(2, :), muteProb); % 对交叉后的染色体进行变异操作
end
population = newPopulation; %产生了新的种群
[populationValue, cumulativeProbs] = calPopulationValue(population, dislist); % 计算新种群的适应度
% 记录当前代最好和平均的适应度
[fmax, nmax] = max(populationValue); % 因为计算适应度时取距离的倒数,这里面取最大的倒数,即最短的距离
generationMeanValue(generationNum) = 1 / mean(populationValue);
generationMaxValue(generationNum) = 1 / fmax;
bestChromo = population(nmax, :); % 前代最佳染色体,即对应的路径
bestRoute(generationNum, :) = bestChromo; % 记录每一代的最佳染色体
drawTSP(Clist, bestChromo, generationMaxValue(generationNum), generationNum, 0);
generationNum = generationNum + 1;
end
[bestValue,index] = min(generationMaxValue);
drawTSP(Clist, bestRoute(index, :), bestValue, index,1);
figure(2);
plot(generationMaxValue, 'r');
hold on;
plot(generationMeanValue, 'b');
grid;
title('搜索过程');
legend('最优解', '平均解');
fprintf('遗传算法得到的最短距离: %.2f\n', bestValue);
fprintf('遗传算法得到的最短路线');
disp(bestRoute(index, :));
end
%------------------------------------------------
% 计算所有染色体的适应度
function [chromoValues, cumulativeProbs] = calPopulationValue(s, dislist)
inn = size(s, 1); % 读取种群大小
chromoValues = zeros(inn, 1);
for i = 1 : inn
chromoValues(i) = CalDist(dislist, s(i, :)); % 计算每条染色体的适应度
end
chromoValues = 1./chromoValues'; % 因为让距离越小,选取的概率越高,所以取距离倒数
% 根据个体的适应度计算其被选择的概率
fsum = 0;
for i = 1 : inn
% 乘以15次方的原因是让好的个体被选取的概率更大(因为适应度取距离的倒数,若不乘次方,则个体相互之间的适应度差别不大),换成一个较大的数也行
fsum = fsum + chromoValues(i)^15;
end
% 计算单个概率
probs = zeros(inn, 1);
for i = 1: inn
probs(i) = chromoValues(i)^15 / fsum;
end
% 计算累积概率
cumulativeProbs = zeros(inn,1);
cumulativeProbs(1) = probs(1);
for i = 2 : inn
cumulativeProbs(i) = cumulativeProbs(i - 1) + probs(i);
end
cumulativeProbs = cumulativeProbs';
end
%--------------------------------------------------
%“选择”操作,返回所选择染色体在种群中对应的位置
% cumulatedPro 所有染色体的累计概率
function selectedChromoNums = select(cumulatedPro)
selectedChromoNums = zeros(2, 1);
% 从种群中选择两个个体,最好不要两次选择同一个个体
for i = 1 : 2
r = rand; % 产生一个随机数
prand = cumulatedPro - r;
j = 1;
while prand(j) < 0
j = j + 1;
end
selectedChromoNums(i) = j; % 选中个体的序号
if i == 2 && j == selectedChromoNums(i - 1) % 若相同就再选一次
r = rand; % 产生一个随机数
prand = cumulatedPro - r;
j = 1;
while prand(j) < 0
j = j + 1;
end
end
end
end
%------------------------------------------------
% “交叉”操作
function crossedChromos = cross(population, selectedChromoNums, crossProb)
length = size(population, 2); % 染色体的长度
crossProbc = crossMuteOrNot(crossProb); %根据交叉概率决定是否进行交叉操作,1则是,0则否
crossedChromos(1,:) = population(selectedChromoNums(1), :);
crossedChromos(2,:) = population(selectedChromoNums(2), :);
if crossProbc == 1
c1 = round(rand * (length - 2)) + 1; %在[1,bn - 1]范围内随机产生一个交叉位 c1
c2 = round(rand * (length - 2)) + 1; %在[1,bn - 1]范围内随机产生一个交叉位 c2
chb1 = min(c1, c2);
chb2 = max(c1,c2);
middle = crossedChromos(1,chb1+1:chb2); % 两条染色体 chb1 到 chb2 之间互换位置
crossedChromos(1,chb1 + 1 : chb2)= crossedChromos(2, chb1 + 1 : chb2);
crossedChromos(2,chb1 + 1 : chb2)= middle;
for i = 1 : chb1 % 看交叉后,染色体上是否有相同编码的情况(路径上重复出现两个城市)。若有,则该编码不参与交叉
while find(crossedChromos(1,chb1 + 1: chb2) == crossedChromos(1, i))
location = find(crossedChromos(1,chb1 + 1: chb2) == crossedChromos(1, i));
y = crossedChromos(2,chb1 + location);
crossedChromos(1, i) = y;
end
while find(crossedChromos(2,chb1 + 1 : chb2) == crossedChromos(2, i))
location = find(crossedChromos(2, chb1 + 1 : chb2) == crossedChromos(2, i));
y = crossedChromos(1, chb1 + location);
crossedChromos(2, i) = y;
end
end
for i = chb2 + 1 : length
while find(crossedChromos(1, 1 : chb2) == crossedChromos(1, i))
location = logical(crossedChromos(1, 1 : chb2) == crossedChromos(1, i));
y = crossedChromos(2, location);
crossedChromos(1, i) = y;
end
while find(crossedChromos(2, 1 : chb2) == crossedChromos(2, i))
location = logical(crossedChromos(2, 1 : chb2) == crossedChromos(2, i));
y = crossedChromos(1, location);
crossedChromos(2, i) = y;
end
end
end
end
%--------------------------------------------------
%“变异”操作
% choromo 为一条染色体
function snnew = mut(chromo,muteProb)
length = size(chromo, 2); % 染色体的的长度
snnew = chromo;
muteProbm = crossMuteOrNot(muteProb); % 根据变异概率决定是否进行变异操作,1则是,0则否
if muteProbm == 1
c1 = round(rand*(length - 2)) + 1; % 在 [1, bn - 1]范围内随机产生一个变异位
c2 = round(rand*(length - 2)) + 1; % 在 [1, bn - 1]范围内随机产生一个变异位
chb1 = min(c1, c2);
chb2 = max(c1, c2);
x = chromo(chb1 + 1 : chb2);
snnew(chb1 + 1 : chb2) = fliplr(x); % 变异,则将两个变异位置的染色体倒转
end
end
% 根据变异或交叉概率,返回一个 0 或 1 的数
function crossProbc = crossMuteOrNot(crossMuteProb)
test(1: 100) = 0;
l = round(100 * crossMuteProb);
test(1 : l) = 1;
n = round(rand * 99) + 1;
crossProbc = test(n);
end
%------------------------------------------------
% 计算一条染色体的适应度
% dislist 为所有城市相互之间的距离矩阵
% chromo 为一条染色体,即一条路径
function chromoValue = CalDist(dislist, chromo)
DistanV = 0;
n = size(chromo, 2); % 染色体的长度
for i = 1 : (n - 1)
DistanV = DistanV + dislist(chromo(i), chromo(i + 1));
end
DistanV = DistanV + dislist(chromo(n), chromo(1));
chromoValue = DistanV;
end
%------------------------------------------------
% 画图
% Clist 为城市坐标
% route 为一条路径
function drawTSP(Clist, route, generationValue, generationNum,isBestGeneration)
CityNum = size(Clist, 1);
for i = 1 : CityNum - 1
plot([Clist(route(i), 1),Clist(route(i + 1), 1)], [Clist(route(i),2),Clist(route(i+1),2)],'ms-','LineWidth',2,'MarkerEdgeColor','k','MarkerFaceColor','g');
text(Clist(route(i), 1),Clist(route(i), 2), [' ', int2str(route(i))]);
text(Clist(route(i+1), 1),Clist(route(i + 1), 2), [' ', int2str(route(i+1))]);
hold on;
end
plot([Clist(route(CityNum), 1), Clist(route(1), 1)], [Clist(route(CityNum), 2), Clist(route(1), 2)],'ms-','LineWidth',2,'MarkerEdgeColor','k','MarkerFaceColor','g');
title([num2str(CityNum),'城市TSP']);
if isBestGeneration == 0 && CityNum ~= 10
text(5, 5, ['第 ',int2str(generationNum),' 代',' 最短距离为 ', num2str(generationValue)]);
else
text(5, 5, ['最终搜索结果:最短距离 ',num2str(generationValue),', 在第 ',num2str(generationNum),' 代达到']);
end
if CityNum == 10 % 因为文字显示位置不一样,所以将城市数目为 10 时单独编写
if isBestGeneration == 0
text(0, 0, ['第 ',int2str(generationNum),' 代',' 最短距离为 ', num2str(generationValue)]);
else
text(0, 0, ['最终搜索结果:最短距离 ',num2str(generationValue),', 在第 ', num2str(generationNum),' 代达到']);
end
end
hold off;
pause(0.005);
end
%------------------------------------------------
三、运行结果
四、备注
版本:2014a