|
|
|
|
@ -0,0 +1,177 @@
|
|
|
|
|
import java.util.*;
|
|
|
|
|
public class TSP {
|
|
|
|
|
private static double mutate_rate=0.08;//变异概率
|
|
|
|
|
private static double cover_rate=0.8;//杂交概率
|
|
|
|
|
private static int sample_size=300;//每次迭代的种群个数
|
|
|
|
|
private static int city_num=30;//每个种群里的城市数量
|
|
|
|
|
private static int iterative=500;//迭代500代
|
|
|
|
|
private static double[]possible;
|
|
|
|
|
private static double[][]city={{ -0.07, 0.60 }, { 0.86, 0.17 }, { 0.94, -0.49 }, { 0.69, -0.54 },
|
|
|
|
|
{ 0.66, -0.15 }, { 0.39, 0.67 }, { 0.87, 0.08 }, { -0.97, 0.92 }, { 0.81, 0.03 }, { -0.41, 0.82 },
|
|
|
|
|
{ 0.10, -0.29 }, { -0.22, -0.66 }, { 0.74, -0.95 }, { -0.09, -0.24 }, { 0.93, 0.55 }, { 0.86, 0.66 },
|
|
|
|
|
{ -0.67, 0.80 }, { 0.14, -0.23 }, { -0.64, 0.34 }, { -0.38, -0.08 }, { -0.82, -0.75 }, { 0.40, 0.82 },
|
|
|
|
|
{ 0.56, 0.34 }, { -0.76, -0.08 }, { 0.78, -0.22 }, { -0.60, -0.81 }, { 0.41, -0.76 }, { 0.81, 0.52 },
|
|
|
|
|
{ 0.92, 0.97 }, { -0.13, -0.24 }};//城市坐标
|
|
|
|
|
|
|
|
|
|
//【*入口函数:TSP整体算法主体】
|
|
|
|
|
public static void main(String[] args) throws Exception {
|
|
|
|
|
CityPoint best=null;//定义最好城市点,算法刚启动时为空
|
|
|
|
|
|
|
|
|
|
//使用CityPoint()生成城市坐标、种群列表,要求生成300个种群,组成样本
|
|
|
|
|
ArrayList<CityPoint> sample=init_group(sample_size);
|
|
|
|
|
|
|
|
|
|
//生成样本后,使其迭代250代(iterative=250)
|
|
|
|
|
for(int i=0;i<iterative;i++){
|
|
|
|
|
|
|
|
|
|
//用cal_possible()方法计算种群中每个个体被选中的概率
|
|
|
|
|
possible=cal_possible(sample);
|
|
|
|
|
sample=cross(sample);
|
|
|
|
|
|
|
|
|
|
//用cal_possible()方法计算种群中每个个体被选中的概率
|
|
|
|
|
possible=cal_possible(sample);
|
|
|
|
|
sample=select(sample);
|
|
|
|
|
|
|
|
|
|
best=find_best(sample);
|
|
|
|
|
|
|
|
|
|
System.out.println();
|
|
|
|
|
System.out.println(String.format("【-----第%d代-----】", i+1));
|
|
|
|
|
System.out.println(String.format("【适应度最大值】%f", best.fitness));
|
|
|
|
|
System.out.println(String.format("【路程最小值】%f", 1/best.fitness));
|
|
|
|
|
System.out.println();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
//【*主方法:初始化种群】
|
|
|
|
|
static ArrayList<CityPoint>init_group(int sample_group_size){ //传入样本量,用CityPoint()生成新的种群,并用种群去创建样本列表
|
|
|
|
|
ArrayList<CityPoint> sample_group=new ArrayList<CityPoint>();
|
|
|
|
|
|
|
|
|
|
for(int i=0;i<sample_size;i++){//按样本量中要求的种群数量,生成对应数量的种群(300个种群为一个样本)
|
|
|
|
|
CityPoint group_current;//生成当前种群
|
|
|
|
|
do{
|
|
|
|
|
group_current=new CityPoint(city_num, city);
|
|
|
|
|
}while(group_current.fitness<=0);//判断当前种群的质量,如果生成的当下种群适应度符合基本要求
|
|
|
|
|
sample_group.add(group_current);//就把当前种群放到样本中
|
|
|
|
|
}
|
|
|
|
|
return sample_group;//样本里够300个及格的种群后,返回样本列表
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//【*主方法:计算个体被抽中的概率】
|
|
|
|
|
static double[]cal_possible(ArrayList<CityPoint>group){
|
|
|
|
|
double[]fitness_indi=new double[group.size()];//按传入种群的元素数量创建一个列表fitness_indi[]
|
|
|
|
|
double fitness_total=0;
|
|
|
|
|
for(int i=0;i<group.size();i++){//遍历传入的种群,i用于索引种群内的元素
|
|
|
|
|
fitness_total+=group.get(i).fitness;//适应度总和=传入种群中,当前个体的适应度相加起来
|
|
|
|
|
fitness_indi[i]=fitness_total;//把当前种群的总适应度记录进适应度列表(fitness[])
|
|
|
|
|
}
|
|
|
|
|
for(int i=0;i<group.size();i++){
|
|
|
|
|
fitness_indi[i]/=fitness_total;//将每个个体的适应度值除以总适应度,从而计算每个个体被选中的概率
|
|
|
|
|
}
|
|
|
|
|
return fitness_indi;//返回储存了每个个体被选概率的数组(fitness_indi[]),这个数组将被用于抽取个体进行交叉、变异
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//【*主方法:交叉】
|
|
|
|
|
static ArrayList<CityPoint> cross(ArrayList<CityPoint>father_group){//传入需要交叉的父种群
|
|
|
|
|
Random random=new Random();
|
|
|
|
|
for(int i=0;i<father_group.size()/2;i++){//对父种群的一半个体进行操作
|
|
|
|
|
if(random.nextDouble()<cover_rate){//每遍历一个个体,就对比一次交叉概率和随机抽取的数,如果交叉概率更大
|
|
|
|
|
father_group.add(get_child(father_group));//就启动用于求子代的get_child()方法,把求得子代加到传入的父种群列表里
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return father_group;//最后返回交叉完毕的父种群
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//【*辅助方法:挑选父个体】
|
|
|
|
|
static CityPoint sel_father(ArrayList<CityPoint>father_group){
|
|
|
|
|
Random random =new Random();
|
|
|
|
|
double t =random.nextDouble();//随机生成一个0到1之间的浮点数t,用来与个体被抽中的概率进行比较
|
|
|
|
|
int i;//这个i之后也被当成索引用
|
|
|
|
|
for(i=0;i<father_group.size()-1;i++){//遍历传入种群的元素(i<father_group.size()-1,因为i一开始为0,当i自加,自加到比father_group.size()大时,就会跳出循环,为了避免没遍历完就提前跳出循环,每次i自加完后father_group.size()都对应-1,就能保证不提前跳出循环)
|
|
|
|
|
if(possible[i]>t){//如果个体被选择的概率大于t
|
|
|
|
|
return father_group.get(i);//就把个体放进father_group,此时这个个体就是父个体
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return father_group.get(i);//返回抽中的父个体
|
|
|
|
|
}
|
|
|
|
|
//【*辅助方法:繁殖,获得子代】
|
|
|
|
|
static CityPoint get_child(ArrayList<CityPoint>father_group){
|
|
|
|
|
CityPoint parentA=sel_father(father_group);//从父种群中挑出一个父个体
|
|
|
|
|
CityPoint parentB=sel_father(father_group);//从父种群中挑出第二个父个体
|
|
|
|
|
int[]tmp=new int[city_num];//创建一个长度为个体元素数的数组(30)
|
|
|
|
|
Random random=new Random();
|
|
|
|
|
int indexA;//定义两个指数,作为两个父个体的交换点
|
|
|
|
|
int indexB;
|
|
|
|
|
do{
|
|
|
|
|
indexA=random.nextInt(city_num);//在个体中元素个数范围内,随机抽取一个指数
|
|
|
|
|
indexB=random.nextInt(city_num);//在个体中元素个数范围内,随机抽取第二个指数
|
|
|
|
|
}while(indexA==0||indexA>=indexB);//如果其中一个指数为0,而另一个又是负数
|
|
|
|
|
int[]tmp_gene=Arrays.copyOfRange(parentA.DNA, indexA, indexB);//创建一个临时数列,把父个体A中,处于指数A、指数B位置的片段存入数组
|
|
|
|
|
|
|
|
|
|
int k=0,i=0;//创建临时变量,i用于控制循环,k是数据的索引
|
|
|
|
|
for(i=0;i<city_num;i++){//进行30次循环
|
|
|
|
|
if(k==indexA){//当遍历到第indexA个数据
|
|
|
|
|
for(int j=0;j<tmp_gene.length;j++){//就开始遍历存有个体A两个元素的临时数列tmp_gene
|
|
|
|
|
tmp[k++]=tmp_gene[j];//将tmp_gene里的元素添加到tmp数列的k+1位置中(每次都放在下一个位置能防止基因重叠)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(!judge_contain(tmp_gene,parentB.DNA[i])){//判断个体B中,是否包含个体A存进tmp_gene中的元素
|
|
|
|
|
tmp[k++]=parentB.DNA[i];//如果不包含,就把个体B里的元素加到tmp数列中(把个体A里没有,但个体B里有的元素加进tmp数列)
|
|
|
|
|
}
|
|
|
|
|
if(k>city_num){//进行到30轮,即处理完一个个体内的所有元素了,就跳出循环
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CityPoint child=mutate_child(new CityPoint(tmp, city));//传入繁殖得出的子代个体,和城市坐标,去用mutate_child()方法进行变异操作,得出完整的子代个体
|
|
|
|
|
if(child.fitness<0||child.fitness<parentA.fitness||child.fitness<parentB.fitness){//如果子代的适应度及格、适应度又比两个父个体都好
|
|
|
|
|
child=get_child(father_group);//就用这个优秀的子代继续繁殖……
|
|
|
|
|
}
|
|
|
|
|
return child;//顺便返回一下这个优秀的子代(每繁殖一次就输出一个子代,然后又用子代继续繁殖……)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//【*辅助方法:突变】
|
|
|
|
|
static CityPoint mutate_child(CityPoint child_group){//传入子代种群
|
|
|
|
|
Random random =new Random();
|
|
|
|
|
if(random.nextDouble()<mutate_rate){
|
|
|
|
|
int k1,k2;//定义两个指数,用作交换位置
|
|
|
|
|
do{
|
|
|
|
|
k1=random.nextInt(city_num);//抽取第一个指数
|
|
|
|
|
k2=random.nextInt(city_num);//抽取第二个指数
|
|
|
|
|
}while(k1==k2);//如果抽的两个指数刚好相等
|
|
|
|
|
int t=child_group.DNA[k1];//交换两个个体相应索引位置的片段
|
|
|
|
|
child_group.DNA[k1]=child_group.DNA[k2];
|
|
|
|
|
child_group.DNA[k2]=t;
|
|
|
|
|
child_group.Fitness_cal();//计算适应度
|
|
|
|
|
}
|
|
|
|
|
return child_group;//返回完成突变操作的子代种群
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//【*辅助方法:判断某个体是否在数组中存在】
|
|
|
|
|
static boolean judge_contain(int list[],int indi){//传入一个数组,和一个个体
|
|
|
|
|
for(int i:list){//遍历数组,并把当前元素放进临时变量i
|
|
|
|
|
if(indi==i){//比较当前元素和个体,如果当前元素和个体相等
|
|
|
|
|
return true;//返回真,也就是传入数组中,有这个个体
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;//否则返回假
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//【*主方法:选择】
|
|
|
|
|
static ArrayList<CityPoint>select(ArrayList<CityPoint> father_group){//传入父种群
|
|
|
|
|
ArrayList<CityPoint>new_group=new ArrayList<CityPoint>();
|
|
|
|
|
for(int i=0;i<sample_size;i++){
|
|
|
|
|
new_group.add(sel_father(father_group));//把符合条件的父种群逐个添加到new_group中
|
|
|
|
|
}
|
|
|
|
|
return new_group;//返回由挑选个体组成的新种群
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//【*主方法:判断当前最优值】
|
|
|
|
|
static CityPoint find_best(ArrayList<CityPoint>group){
|
|
|
|
|
CityPoint best=group.get(0);//暂时将数组中的第一个元素视作最优解
|
|
|
|
|
for(CityPoint group_indi:group){//遍历传入的数组
|
|
|
|
|
if(group_indi.fitness>best.fitness)//每次遍历到的个体都和当前的最优解做比较,如果当前遍历的种群个体适应度>当前最优解的适应度
|
|
|
|
|
best=group_indi;//就把高适应度的个体换成最优解
|
|
|
|
|
}
|
|
|
|
|
return best;//返回重重比较下得出的真正最优解
|
|
|
|
|
}
|
|
|
|
|
}
|