刚刚接触遗传算法,主要学习的是以下几位老师的文章(抱拳),链接附上:
https://blog.csdn.net/u010451580/article/details/51178225
https://blog.csdn.net/wangqiuyun/article/details/12838903
写这篇文章主要是系统地整理一下我这几天学到了算法,所以内容不多。
遗传算法(Genetic Algorithm, GA)起源于对生物系统所进行的计算机模拟研究。它是模仿自然界生物进化机制发展起来的随机全局搜索和优化方法,借鉴了达尔文的进化论和孟德尔的遗传学说。
算法是将初始解构成初始种群,然后不断遗传、进化、变异,最终从中挑选出最优个体,应该算是基于大数据的。
有三种基本算子:选择,交叉,变异,
遗传算法的实施步骤如下(以目标函数求最小为例)。
第一步:初始化 t←0进化代数计数器;T是最大进化代数;随机生成M个个体作为初始群体P(t);
第二步:个体评价 计算P(t)中各个个体的适应度;(即评估个体好坏的一个指标)
第三步:选择运算 将选择算子作用于群体;(类似于物竞天择,适者生存)
第四步:交叉运算 将交叉算子作用于群体;(两个体交叉繁殖出新个体)斜体样式
第五步:变异运算 将变异算子作用于群体,并通过以上运算得到下一代群体P(t + 1);(自身变异形成新个体)
第六步:终止条件判断 t≦T:t← t+1 转到步骤2;t>T:终止 输出解。
常见的选择策略:
最常见的是赌轮选择方法,本质是个体被选中的概率与适应度成正比。这里选择的时候会用到累计概率,即计算出个体的适应度后,求出个体适应度占总适应度的一个比例,如个体1、个体2、个体3的适应度分别为f1,f2,f3,总适应度sumf = f1+f2+f3.则个体1、2、3的累计概率分别为f1/sumf, (f1+f2)/sumf, (f1+f2+f3)/sumf。最后一个数必然为1,则构成一个0-1的轮盘,随机数落在哪个范围,即选择对应个体作为新种群的一员。
那么具体如何选择,如何交叉?且看一下大神整理的:
https://www.cnblogs.com/legend1130/p/5333087.html
常见交叉策略:
https://blog.csdn.net/ztf312/article/details/82793295
其实感觉算法还是要结合一下具体事例(代码)一起看的,刚开始看算法似懂非懂,雾里看花的感觉,还是看了一遍别人写的具体代码才拨云见月。
TSP问题(Travelling Salesman Problem)即旅行商问题,又译为旅行推销员问题、货郎担问题,是数学领域中著名问题之一。假设有一个旅行商人要拜访n个城市,他必须选择所要走的路径,路径的限制是每个城市只能拜访一次,而且最后要回到原来出发的城市。路径的选择目标是要求得的路径路程为所有路径之中的最小值。
1、
基本思路:
1》初始化:随机生成scale条路径放入oldpopulation,(城市作为编号,编码表示路径。scale为种群的规模,每一条路径为一个个体)
2》算适应度:路径的长度越短,适应度(fitness)最高
3》挑选出适应度最高的路径放入newpopulation,若优于之前的best_tour,则更新best_tour
4》计算种群中个个体的累计概率,用赌轮选择法挑选出下一代放入newpopulation
5》对newpopulation进行交叉
6》对newpopulation进行变异
7》将newpopulation—>oldpopulation,
8》将重复进行2-7
代码附上:(和wangqiuyun博主不同的是我的代码所需数据是自己给的(也可自己文件读写),八个城市的距离矩阵(无向图,所以是对称矩阵)
private int[][] distance =
{ {0, 300, 360, 210, 590, 475 , 500, 690},
{300, 0 ,380, 270, 230, 285, 200, 390},
{360, 380, 0 ,510 ,230, 765, 580, 770},
{210, 270 , 510, 0, 470, 265, 450, 640},
{590, 230, 230, 470 ,0, 515 , 260, 450},
{475, 285, 765, 265, 515, 0 ,460, 650},
{500, 200 , 580 , 450 ,260 ,460 ,0 ,190},
{690, 390 ,770, 640 ,450, 650 ,190, 0}}; )
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Random;
public class GA {
private int scale;// 种群规模
private int cityNum; // 城市数量,染色体长度
private int MAX_GEN; // 运行代数
private int start = 0;
private int end = 7;
private int[][] distance =
{
{0, 300, 360, 210, 590, 475 , 500, 690},
{10000, 0 ,380, 270, 230, 285, 200, 390},
{10000, 380, 0 ,510 ,230, 765, 580, 770},
{10000, 270 , 510, 0, 470, 265, 450, 640},
{10000, 230, 230, 470 ,0, 515 , 260, 450},
{10000, 285, 765, 265, 515, 0 ,460, 650},
{10000, 200 , 580 , 450 ,260 ,460 ,0 ,190},
{10000, 10000 ,10000, 10000 ,10000, 10000 ,10000, 0}}; // 距离矩阵 */
private int bestT;// 最佳出现代数
private int bestLength; // 最佳长度
private int[] bestTour; // 最佳路径
// 初始种群,父代种群,行数表示种群规模,一行代表一个个体,即染色体,列表示染色体基因片段
private int[][] oldPopulation;
private int[][] newPopulation;// 新的种群,子代种群
private int[] fitness;// 种群适应度,表示种群中各个个体的适应度
private float[] Pi;// 种群中各个个体的累计概率
private float Pc;// 交叉概率
private float Pm;// 变异概率
private int t;// 当前代数
private Random random;
public GA() {
}
/**
* constructor of GA
*
* @param s
* 种群规模
* @param n
* 城市数量
* @param g
* 运行代数
* @param c
* 交叉率
* @param m
* 变异率
*
**/
public GA(int s, int n, int g, float c, float m) {
scale = s;
cityNum = n;
MAX_GEN = g;
Pc = c;
Pm = m;
}
// 给编译器一条指令,告诉它对被批注的代码元素内部的某些警告保持静默
@SuppressWarnings("resource")
/**
* 初始化GA算法类
* @param filename 数据文件名,该文件存储所有城市节点坐标数据
* @throws IOException
*/
private void init(String filename) throws IOException {
// 读取数据
/*int[] x;
int[] y;
String strbuff;
BufferedReader data = new BufferedReader(new InputStreamReader(
new FileInputStream(filename)));
distance = new int[cityNum][cityNum];
x = new int[cityNum];
y = new int[cityNum];
for (int i = 0; i < c ityNum; i++) {
// 读取一行数据,数据格式1 6734 1453
strbuff = data.readLine();
// 字符分割
String[] strcol = strbuff.split(" ");
x[i] = Integer.valueOf(strcol[1]);// x坐标
y[i] = Integer.valueOf(strcol[2]);// y坐标
}
// 计算距离矩阵
// ,针对具体问题,距离计算方法也不一样,此处用的是att48作为案例,它有48个城市,距离计算方法为伪欧氏距离,最优值为10628
for (int i = 0; i < cityNum - 1; i++) {
distance[i][i] = 0; // 对角线为0
for (int j = i + 1; j < cityNum; j++) {
double rij = Math
.sqrt(((x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j])
* (y[i] - y[j])) / 10.0);
// 四舍五入,取整
int tij = (int) Math.round(rij);
if (tij < rij) {
distance[i][j] = tij + 1;
distance[j][i] = distance[i][j];
} else {
distance[i][j] = tij;
distance[j][i] = distance[i][j];
}
}
}
distance[cityNum - 1][cityNum - 1] = 0; */
bestLength = Integer.MAX_VALUE;
bestTour = new int[cityNum + 1];
bestT = 0;
t = 0;
newPopulation = new int[scale][cityNum];
oldPopulation = new int[scale][cityNum];
fitness = new int[scale];
Pi = new float[scale];
random = new Random(System.currentTimeMillis());
/*
* for(int i=0;i fitness[k]) {
maxevaluation = fitness[k];
maxid = k;
}
}
if (bestLength > maxevaluation) {
bestLength = maxevaluation;
bestT = t;// 最好的染色体出现的代数;
for (i = 0; i < cityNum; i++) {
bestTour[i] = oldPopulation[maxid][i];
}
}
// System.out.println("代数 " + t + " " + maxevaluation);
// 复制染色体,k表示新染色体在种群中的位置,kk表示旧的染色体在种群中的位置
copyGh(0, maxid);// 将当代种群中适应度最高的染色体k复制到新种群中,排在第一位0
}
// 复制染色体,k表示新染色体在种群中的位置,kk表示旧的染色体在种群中的位置
public void copyGh(int k, int kk) {
int i;
for (i = 0; i < cityNum; i++) {
newPopulation[k][i] = oldPopulation[kk][i];
}
}
// 赌轮选择策略挑选
public void select() {
int k, i, selectId;
float ran1;
// Random random = new Random(System.currentTimeMillis());
for (k = 1; k < scale; k++) {
ran1 = (float) (random.nextInt(65535) % 1000 / 1000.0);
// System.out.println("概率"+ran1);
// 产生方式
for (i = 0; i < scale; i++) {
if (ran1 <= Pi[i]) {
break;
}
}
selectId = i;
// System.out.println("选中" + selectId);
copyGh(k, selectId);
}
}
//进化函数,正常交叉变异
public void evolution() {
int k;
// 挑选某代种群中适应度最高的个体
selectBestGh();
// 赌轮选择策略挑选scale-1个下一代个体
select();
// Random random = new Random(System.currentTimeMillis());
float r;
// 交叉方法
for (k = 0; k < scale; k = k + 2) {
r = random.nextFloat();// /产生概率
// System.out.println("交叉率..." + r);
if (r < Pc) {
// System.out.println(k + "与" + k + 1 + "进行交叉...");
//OXCross(k, k + 1);// 进行交叉
OXCross1(k, k + 1);
} else {
r = random.nextFloat();// /产生概率
// System.out.println("变异率1..." + r);
// 变异
if (r < Pm) {
// System.out.println(k + "变异...");
OnCVariation(k);
}
r = random.nextFloat();// /产生概率
// System.out.println("变异率2..." + r);
// 变异
if (r < Pm) {
// System.out.println(k + 1 + "变异...");
OnCVariation(k + 1);
}
}
}
}
//进化函数,保留最好染色体不进行交叉变异
public void evolution1() {
int k;
// 挑选某代种群中适应度最高的个体
selectBestGh();
// 赌轮选择策略挑选scale-1个下一代个体
select();
// Random random = new Random(System.currentTimeMillis());
float r;
for (k = 1; k + 1 < scale / 2; k = k + 2) {
r = random.nextFloat();// /产生概率
if (r < Pc) {
//OXCross1(k, k + 1);// 进行交叉
OXCross(k,k+1);//进行交叉
} else {
r = random.nextFloat();// /产生概率
// 变异
if (r < Pm) {
OnCVariation(k);
}
r = random.nextFloat();// /产生概率
// 变异
if (r < Pm) {
OnCVariation(k + 1);
}
}
}
if (k == scale / 2 - 1)// 剩最后一个染色体没有交叉L-1
{
r = random.nextFloat();// /产生概率
if (r < Pm) {
OnCVariation(k);
}
}
}
// 类OX交叉算子
void OXCross(int k1, int k2) {
int i, j, k, flag;
int ran1, ran2, temp;
int[] Gh1 = new int[cityNum];
int[] Gh2 = new int[cityNum];
// Random random = new Random(System.currentTimeMillis());
ran1 = random.nextInt(65535) % cityNum;
ran2 = random.nextInt(65535) % cityNum;
// System.out.println();
// System.out.println("-----------------------");
// System.out.println("----"+ran1+"----"+ran2);
while (ran1 == ran2) {
ran2 = random.nextInt(65535) % cityNum;
}
if (ran1 > ran2)// 确保ran1 ran2)// 确保ran1
2、TSP变式
如果出发点和终点固定,假设城市0位起点,城市7为终点,可将该路线图想象为有向图,所有城市均不能到达城市0,城市7不能不到达所有城市(都设为无穷大),经过迭代后即可求出答案
距离矩阵
private int[][] distance =
{ {0, 300, 360, 210, 590, 475 , 500, 690},
{10000, 0 ,380, 270, 230, 285, 200, 390},
{10000, 380, 0 ,510 ,230, 765, 580, 770},
{10000, 270 , 510, 0, 470, 265, 450, 640},
{10000, 230, 230, 470 ,0, 515 , 260, 450},
{10000, 285, 765, 265, 515, 0 ,460, 650},
{10000, 200 , 580 , 450 ,260 ,460 ,0 ,190},
{10000, 10000 ,10000, 10000 ,10000, 10000 ,10000, 0}}; // 距离矩阵 */
其余代码同上
TSP问题没有其他的约束条件,但对于求解一个多元函数,可能要求
0
我看过几篇论文,关于这个问题有修复不可行解法,改变遗传算子法,惩罚函数法,我没有深入研究过。不过我做过一个题,是多约束条件的,用了罚函数来体现违反约束的程度,并对适应度进行相应惩罚,也得出了结果。
罚函数是通过对不可行解的惩罚将约束问题转化为无约束问题,任何对约束的违法都要在目标函数中添加惩罚项。这个未完待续……
暂时掌握,后期更新。若文章有误,还请指正,谢谢!
2019年7月16日 周二