一、遗传算法
1.1 遗传算法的定义
遗传算法(Genetic Algorithm, GA)是模拟达尔文生物进化论的自然选择和遗传学机理的生物进化过程的计算模型,是一种通过模拟自然进化过程搜索最优解的方法。其主要特点是直接对结构对象进行操作,不存在求导和函数连续性的限定;具有内在的隐并行性和更好的全局寻优能力;采用概率化的寻优方法,不需要确定的规则就能自动获取和指导优化的搜索空间,自适应地调整搜索方向。
遗传算法以一种群体中的所有个体为对象,并利用随机化技术指导对一个被编码的参数空间进行高效搜索。其中,选择、交叉和变异构成了遗传算法的遗传操作;参数编码、初始群体的设定、适应度函数的设计、遗传操作设计、控制参数设定五个要素组成了遗传算法的核心内容。
1.2 遗传算法的执行过程
遗传算法是从代表问题可能潜在的解集的一个种群(population)开始的,而一个种群则由经过基因(gene)编码的一定数目的个体(individual)组成。每个个体实际上是染色体(chromosome)带有特征的实体。
染色体作为遗传物质的主要载体,即多个基因的集合,其内部表现(即基因型)是某种基因组合,它决定了个体的形状的外部表现,如黑头发的特征是由染色体中控制这一特征的某种基因组合决定的。因此,在一开始需要实现从表现型到基因型的映射即编码工作。由于仿照基因编码的工作很复杂,我们往往进行简化,如二进制编码。
初代种群产生之后,按照适者生存和优胜劣汰的原理,逐代(generation)演化产生出越来越好的近似解,在每一代,根据问题域中个体的适应度(fitness)大小选择(selection)个体,并借助于自然遗传学的遗传算子(genetic operators)进行组合交叉(crossover)和变异(mutation),产生出代表新的解集的种群。
这个过程将导致种群像自然进化一样的后生代种群比前代更加适应于环境,末代种群中的最优个体经过解码(decoding),可以作为问题近似最优解。
二、一元函数最大值的问题
2.1 最大值问题
我们要在既定的区间内找出函数的最大值。函数存在着很多的极大值和极小值。而最大值则是指定区间的极大值中的最大的那一个。从图像上具体表现为,极大值像是一座座山峰,极小值则是像一座座山谷。因此,我们也可以把遗传算法的过程看作是一个在多元函数里面求最优解的过程。
这些山峰对应着局部最优解,其中有一个山峰是海拔最高的,这个山峰则对应的是全局最优解。那么,遗传算法要做的就是尽量爬到最高峰,而不是困在较低的小山峰上。(如果问题求解是最小值,那么要做的就是尽量走到最低谷,道理是一样的)。
2.2 “袋鼠跳”解释几种常见的算法
既然我们把函数曲线理解成一个一个山峰和山谷组成的山脉。那么我们可以设想所得到的每一个解就是一只袋鼠,我们希望它们不断的向着更高处跳去,直到跳到最高的山峰。所以求最大值的过程就转化成一个“袋鼠跳”的过程。
爬山算法:一只袋鼠朝着比现在高的地方跳去。它找到了不远处的最高的山峰。但是这座山不一定是最高峰。这就是爬山算法,它不能保证局部最优值就是全局最优值。
模拟退火:袋鼠喝醉了。它随机地跳了很长时间。这期间,它可能走向高处,也可能踏入平地。但是,它渐渐清醒了并朝最高峰跳去。这就是模拟退火算法。
遗传算法:有很多袋鼠,它们降落到喜玛拉雅山脉的任意地方。这些袋鼠并不知道它们的任务是寻找珠穆朗玛峰。但每过几年,就在一些海拔高度较低的地方射杀一些袋鼠。于是,不断有袋鼠死于海拔较低的地方,而越是在海拔高的袋鼠越是能活得更久,也越有机会生儿育女。就这样经过许多年,这些袋鼠们竟然都不自觉地聚拢到了一个个的山峰上,可是在所有的袋鼠中,只有聚拢到珠穆朗玛峰的袋鼠被带回了美丽的澳洲。
遗传算法的步骤
-
随机产生种群。
-
根据策略判断个体的适应度,是否符合优化准则,若符合,输出最佳个体及其最优解,结束。否则,进行下一步。
-
依据适应度选择父母,适应度高的个体被选中的概率高,适应度低的个体被淘汰。
-
用父母的染色体按照一定的方法进行交叉,生成子代。
-
对子代染色体进行变异。
具体实现
package liu.ga;
//TSP数据类
/**
* TSP数据类
* 包含:
* disMap 各个城市间距离矩阵
*/
public class TSPData {
static int CITY_NUM; //城市数
static final int SPECIES_NUM=200; //种群数
static final int DEVELOP_NUM=1000; //进化代数
static final float pcl=0.6f,pch=0.95f;//交叉概率
static final float pm=0.4f;//变异概率
static final float[][] disMap; //地图数据
static
{
// int[][] cityPosition={
// {0,0},{12,32},{5,25},{8,45},{33,17},
// {25,7},{15,15},{15,25},{25,15},{41,12}};//10个城市(最优解:147)
// int[][] cityPosition={
// {60,200},{180,200},{80,180},{140,180},
// {20,160},{100,160},{200,160},{140,140},
// {40,120},{100,120},{180,100},{60,80},
// {120,80},{180,60},{20,40},{100,40},
// {200,40},{20,20},{60,20},{160,20}};//20个城市(最优解:870)
//
//城市坐标集合
int[][] cityPosition={
{1304, 2312},{3639, 1315},
{4177, 2244},{3712, 1399},
{3488, 1535},{3326, 1556},
{3238, 1229},{4196, 1004},
{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, 2367},
{3394, 2643},{3439, 3201},
{2935, 3240},{3140, 3550},
{2545, 2357},{2778, 2826},
{2370, 2975}};//31个城市(最优解:14700)
//路径集合
CITY_NUM=cityPosition.length;
disMap=new float[CITY_NUM][CITY_NUM];
//两城市之间距离计算赋值给disMap
for(int i=0;i<CITY_NUM;i++)
{
for(int j=i;j<CITY_NUM;j++)
{
//两点间距离公式:√(x1-x2)?+(y1-y2)?
float dis=(float)Math.sqrt(Math.pow((cityPosition[i][0] - cityPosition[j][0]),2) + Math.pow((cityPosition[i][1] - cityPosition[j][1]),2));
disMap[i][j]=dis;
disMap[j][i]=disMap[i][j];
}
}
}
}
package liu.ga;
//物种的个体类
import java.util.Random;
/**
* 个体类
* 包含:
* 1.createByRandomGenes 初始物种基因(随机) 基因直接用城市序列编码
* 2.calFitness 计算物种适应度
* 3.printRate 打印路径
*/
public class SpeciesIndividual {
String[] genes;//基因序列
float distance;//路程
float fitness;//适应度
SpeciesIndividual next;
float rate;//该物种被选中的概率
SpeciesIndividual() {
//初始化
this.genes = new String[TSPData.CITY_NUM];
this.fitness = 0.0f;
this.distance = 0.0f;
this.next = null;
rate = 0.0f;
}
//初始物种基因(随机)
void createByRandomGenes() {
//初始化基因为1-CITY_NUM序列——直接采用城市序列作为基因编码
for (int i = 0; i < genes.length; i++) {
genes[i] = Integer.toString(i + 1);
}
//获取随机种子
Random rand = new Random();
for (int j = 0; j < genes.length; j++) {
int num = j + rand.nextInt(genes.length - j);
//交换
String tmp;
tmp = genes[num];
genes[num] = genes[j];
genes[j] = tmp;
}
}
//初始物种基因(贪婪)
void createByGreedyGenes() {
Random rand = new Random();
int i = rand.nextInt(TSPData.CITY_NUM); //随机产生一个城市作为起点
genes[0] = Integer.toString(i + 1);
int j;//终点
int cityNum = 0;
do {
cityNum++;
//选出单源最短城市
float minDis = Integer.MAX_VALUE;
int minCity = 0;
for (j = 0; j < TSPData.CITY_NUM; j++) {
if (j != i) {
//判是否和已有重复
boolean repeat = false;
for (int n = 0; n < cityNum; n++) {
if (Integer.parseInt(genes[n]) == j + 1) {
repeat = true;//重了
break;
}
}
if (repeat == false)//没重
{
//判长度
if (TSPData.disMap[i][j] < minDis) {
minDis = TSPData.disMap[i][j];
minCity = j;
}
}
}
}
//加入到染色体
genes[cityNum] = Integer.toString(minCity + 1);
i = minCity;
} while (cityNum < TSPData.CITY_NUM - 1);
}
//计算物种适应度
void calFitness() {
float totalDis = 0.0f;
for (int i = 0; i < TSPData.CITY_NUM; i++) {
int curCity = Integer.parseInt(this.genes[i]) - 1;
int nextCity = Integer.parseInt(this.genes[(i + 1) % TSPData.CITY_NUM]) - 1;
totalDis += TSPData.disMap[curCity][nextCity];
}
//总距离的倒数作为物种适应度
this.distance = totalDis;
this.fitness = 1.0f / totalDis;
}
//深拷贝
public SpeciesIndividual clone() {
SpeciesIndividual species = new SpeciesIndividual();
//复制值
for (int i = 0; i < this.genes.length; i++)
species.genes[i] = this.genes[i];
species.distance = this.distance;
species.fitness = this.fitness;
return species;
}
//打印路径
void printRate() {
System.out.print("最短路线:");
for (int i = 0; i < genes.length; i++)
System.out.print(genes[i] + "->");
System.out.print(genes[0] + "\n");
System.out.print("最短长度:" + distance);
}
}
package liu.ga;
//物种的种群类
/**
* 种群类
* 包含:
* 1.add 添加物种
* 2.traverse 遍历
*/
public class SpeciesPopulation {
SpeciesIndividual head;//头结点
int speciesNum;//物种数量
SpeciesPopulation()
{
head=new SpeciesIndividual();
speciesNum=TSPData.SPECIES_NUM;
}
//添加物种
void add(SpeciesIndividual species)
{
SpeciesIndividual point=head;//游标
while(point.next != null)//寻找表尾结点
point=point.next;
point.next=species;
}
//遍历
void traverse()
{
SpeciesIndividual point=head.next;//游标
while(point != null)//寻找表尾结点
{
for(int i=0;i<TSPData.CITY_NUM;i++)
System.out.print(point.genes[i]+" ");
System.out.println(point.distance);
point=point.next;
}
System.out.println("_______________________");
}
}
package liu.ga;
import liu.ga.SpeciesIndividual;
import liu.ga.SpeciesPopulation;
import liu.ga.TSPData;
import java.util.Random;
/**
* 遗传算法类
* 包含:
* 1.run 开始跑算法
* 2.createBeginningSpecies 创建种群
* 3.calRate 计算每一种物种被选中的概率
* 4.select 轮盘策略 选择适应度高的物种
* 5.crossover 染色体交叉
* 6.mutate 染色体变异
* 7.getBest 获得适应度最大的物种
*/
public class GeneticAlgorithm {
//开始遗传
SpeciesIndividual run(SpeciesPopulation list) {
//创建初始种群
createBeginningSpecies(list);
for (int i = 1; i <= TSPData.DEVELOP_NUM; i++) {
//选择
select(list);
//交叉
crossover(list);
//变异
mutate(list);
}
return getBest(list);
}
//创建初始种群
void createBeginningSpecies(SpeciesPopulation list) {
//100%随机
int randomNum = (int) (TSPData.SPECIES_NUM);
for (int i = 1; i <= randomNum; i++) {
SpeciesIndividual species = new SpeciesIndividual();//创建结点
species.createByRandomGenes();//初始种群基因
list.add(species);//添加物种
}
// //40%贪婪
// int greedyNum=TSPData.SPECIES_NUM-randomNum;
// for(int i=1;i<=greedyNum;i++)
// {
// SpeciesIndividual species=new SpeciesIndividual();//创建结点
// species.createByGreedyGenes();//初始种群基因
//
// this.add(species);//添加物种
// }
}
//计算每一物种被选中的概率
void calRate(SpeciesPopulation list) {
//总适应度
float totalFitness = 0.0f;
//物种数量
list.speciesNum = 0;
SpeciesIndividual point = list.head.next;//游标
while (point != null)//寻找表尾结点
{
point.calFitness();//计算适应度
totalFitness += point.fitness;
list.speciesNum++;
point = point.next;
}
//计算选中概率
point = list.head.next;//游标
while (point != null)//寻找表尾结点
{
point.rate = point.fitness / totalFitness;
point = point.next;
}
}
//选择优秀物种(轮盘赌)
void select(SpeciesPopulation list) {
//计算适应度
calRate(list);
//找出最大适应度物种
float talentDis = Float.MAX_VALUE;
SpeciesIndividual talentSpecies = null;
SpeciesIndividual point = list.head.next;//游标
while (point != null) {
if (talentDis > point.distance) {
talentDis = point.distance;
talentSpecies = point;
}
point = point.next;
}
//将最大适应度物种复制talentNum个-我们先找出最大适应度的个体。然后复制该个体到新种群去,复制数量占原来种群的1/4。
SpeciesPopulation newSpeciesPopulation = new SpeciesPopulation();
int talentNum = (int) (list.speciesNum / 4);
for (int i = 1; i <= talentNum; i++) {
//复制物种至新表
SpeciesIndividual newSpecies = talentSpecies.clone();
newSpeciesPopulation.add(newSpecies);
}
//轮盘赌list.speciesNum-talentNum次
int roundNum = list.speciesNum - talentNum;
for (int i = 1; i <= roundNum; i++) {
//产生0-1的概率
float rate = (float) Math.random();
SpeciesIndividual oldPoint = list.head.next;//游标
while (oldPoint != null && oldPoint != talentSpecies)//寻找表尾结点
{
if (rate <= oldPoint.rate) {
SpeciesIndividual newSpecies = oldPoint.clone();
newSpeciesPopulation.add(newSpecies);
break;
} else {
rate = rate - oldPoint.rate;
}
oldPoint = oldPoint.next;
}
if (oldPoint == null || oldPoint == talentSpecies) {
//复制最后一个
point = list.head;//游标
while (point.next != null)//寻找表尾结点
point = point.next;
SpeciesIndividual newSpecies = point.clone();
newSpeciesPopulation.add(newSpecies);
}
}
list.head = newSpeciesPopulation.head;
}
//交叉操作
void crossover(SpeciesPopulation list) {
//以概率pcl~pch进行
float rate = (float) Math.random();
if (rate > TSPData.pcl && rate < TSPData.pch) {
SpeciesIndividual point = list.head.next;//游标
Random rand = new Random();
//list.speciesNum:物种数量
int find = rand.nextInt(list.speciesNum);
while (point != null && find != 0)//寻找表尾结点
{
point = point.next;
find--;
}
if (point.next != null) {
int begin = rand.nextInt(TSPData.CITY_NUM);
//取point和point.next进行交叉,形成新的两个染色体
for (int i = begin; i < TSPData.CITY_NUM; i++) {
//找出point.genes中与point.next.genes[i]相等的位置fir
//找出point.next.genes中与point.genes[i]相等的位置sec
int fir, sec;
for (fir = 0; !point.genes[fir].equals(point.next.genes[i]); fir++) ;
for (sec = 0; !point.next.genes[sec].equals(point.genes[i]); sec++) ;
//两个基因互换
String tmp;
tmp = point.genes[i];
point.genes[i] = point.next.genes[i];
point.next.genes[i] = tmp;
//消去互换后重复的那个基因
point.genes[fir] = point.next.genes[i];
point.next.genes[sec] = point.genes[i];
}
}
}
}
//变异操作
void mutate(SpeciesPopulation list) {
//每一物种均有变异的机会,以概率pm进行
SpeciesIndividual point = list.head.next;
while (point != null) {
float rate = (float) Math.random();
//TSPData.pm:变异概率
if (rate < TSPData.pm) {
//寻找逆转左右端点
Random rand = new Random();
int left = rand.nextInt(TSPData.CITY_NUM);
int right = rand.nextInt(TSPData.CITY_NUM);
if (left > right) {
int tmp;
tmp = left;
left = right;
right = tmp;
}
//逆转left-right下标元素
while (left < right) {
String tmp;
tmp = point.genes[left];
point.genes[left] = point.genes[right];
point.genes[right] = tmp;
left++;
right--;
}
}
point = point.next;
}
}
//获得适应度最大的物种
SpeciesIndividual getBest(SpeciesPopulation list) {
float distance = Float.MAX_VALUE;
SpeciesIndividual bestSpecies = null;
SpeciesIndividual point = list.head.next;//游标
while (point != null)//寻找表尾结点
{
if (distance > point.distance) {
bestSpecies = point;
distance = point.distance;
}
point = point.next;
}
return bestSpecies;
}
}
package liu.ga;
//主函数运行类
public class MainRun {
public static void main(String[] args) {
// TODO Auto-generated method stub
//整个例子的思想:
//这道实例的目的是为了找到一条回路最短的路径,而采用遗传算法来实现这个目的
//本实例,将基因直接使用城市序列编码
//通过选取两个城市之间最小的物种,最终产生最合适的路径
//创建遗传算法驱动对象
GeneticAlgorithm GA=new GeneticAlgorithm();
//创建初始种群
SpeciesPopulation speciesPopulation = new SpeciesPopulation();
//开始遗传算法(选择算子、交叉算子、变异算子)
SpeciesIndividual bestRate=GA.run(speciesPopulation);
//打印路径与最短距离
bestRate.printRate();
}
}