遗传算法求解K图染色问题(java版)

先修知识

若你对遗传算法的思想不是很熟悉,可以先阅读以下文章,了解什么是遗传算法。
遗传算法图文详解

问题描述

对于文件给出的一个有 500 个节点的图形(n500),利用遗传算法,求最
少可用几种颜色能对其进行着色?并给出对应的着色方案。

问题分析:

题目的目标是用5种颜色,将图1中的每个节点上色,且保证相邻节点间颜色不同;
a) 首先我们依次将图1中的每个节点随机染色,得到一个解序列S;
b) 设置初始种群数量500,随机生成500个解序列,获得初始种群
c) 采取某种选择策略,对初始种群中的解进行选择,遗传到下一代
d) 采取某种交叉策略,对初始种群中的解随机选择进行交叉,遗传到下一代
e) 采取某种变异策略,对初始种群中的解随机选择进行变异,遗传到下一代
f) 结束条件,最优解冲突数为0,或者满足最大循环次数;否则执行c)至e)

伪代码

生成初始种群curGeneration,设下一代种群nextGeneration ← Ø, 并设置迭代次数 NUM,
初始种群数量10,NUM=100
curGeneration←initGeneration, g ←0
while(true){
foreach solution in curGeneration
conflict ←计算solution的冲突值
if conflict==0
成功退出
if g>NUM
失败退出
nextGeneration ← nextGeneration ∪ production(curGeneration)
nextGeneration ← nextGeneration ∪ crossover(curGeneration)
nextGeneration ← nextGeneration ∪ mutation(curGeneration)
curGeneration ← nextGeneration
nextGeneration ← Ø
g ← g+1

局限性

遗传算法作为启发式算法,收敛速度没有退火算法快;其主要是通过选择算子来遗传优良解,交叉算子增加种群的多样性,变异算子对种群中的解进行局部搜索。使用遗传算法求解问题的难点在于如何设定参数,某些情况下可以将遗传算法和退火算法结合,也许会获得较理想的结果。本文用遗传算法求解500个节点的图着色问题,很遗憾,只能用5种颜色染色才能得到优解;

图节点文本文件

K图着色500个节点文件
提取码:o177

具体实现

1.先定义一个存储节点的颜色集合类

package package_java;
import java.util.ArrayList;
public class ColorSet{
	public  ArrayList node;
	public ColorSet(){
		node = new ArrayList();
	}
	public void add(int newNode){
		node.add(newNode);
	}
	
	public void set(int index, int newNode){
		node.set(index,newNode);
	}
	
	public  int get(int index){
		return (int)(node.get(index));
	}
	
	public int remove(int index){
		return (int)(node.remove(index));
	}
	
	public boolean remove(Object o){
		return node.remove(o);
	}
	public int indexOf(int oldNode){
		return node.indexOf(oldNode);
	}
	public int length(){
		return node.size();
	}
}

2.主类

import java.io.File;
import java.io.FileReader;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;
import package_java.ColorSet;
public class KGraphColorGA{
	/*
	 *此程序使用遗传算法求解500个节点的图染色问题
	 *可以用5种颜色求解出最优解
	 *您可以对Seed:随机数产生用的种子,selectNum:选择数量 crossNum:交叉数量 mutateNum:变异数量进行调整
	 *看能否是程序收敛更快
	*/
	/*
	 *NUM:节点数量 K:颜色数量 P:种群数量 Seed:随机数产生用的种子
	 *bestConflict:最佳冲突 newSolutionIndex:记录新种群的索引 selectNum:选择数量 crossNum:交叉数量 mutateNum:变异数量
	*/
	final static int NUM=500,K=5,P=500,Seed=2010;
	public static int bestConflict=100, newSolutionIndex=0,selectNum=150,crossNum=100,mutateNum=250;
	// 最佳解
	public static int[] bestSolution=new int[NUM];
	// 存储种群中每个解的冲突
	public static int[] conflictsArr=new int[P];
	public static void main(String[] args){
		int[][] g=new int[NUM][NUM];
		// 存储原始种群
		int[][] allSolution=new int[P][NUM];
		// 存储新种群
		int[][] newAllSolution=new int[P][NUM];
		String path="D:\\YSA\\Algorithm\\n500.txt";
		if(!readNodeFile(g,path)){
			System.out.println("fail to read node file !");
		}
		else
		{
			// 迭代次数
			int steps=40000;
			int iteration=0;
			// 生成初始种群
			initSolution(allSolution,P,NUM,K,Seed);
			// 计算初始种群中每个解的冲突
			calculateConflictsArr(g,allSolution,NUM,P,K);
			// 获取最佳解
			getBestSolution(allSolution,NUM,P);
			while(iteration<steps&&bestConflict>0){
				// 每次循环前置新种群索引为0
				newSolutionIndex=0;
				// 对原始种群进行选择
				selectSolution(allSolution,newAllSolution,NUM,P,selectNum,Seed);
				// 对原始种群进行交叉
				crossSomeSolution(allSolution,newAllSolution,NUM,P,K,crossNum,Seed);
				// 对原始种群进行变异
				mutateSolution(allSolution,newAllSolution,NUM,P,K,mutateNum,Seed);	
				// 将新种群赋给原始种群
				newAllSolutionToAllSoliution(allSolution,newAllSolution,NUM,P);
				// 计算种群中每个解的冲突
				calculateConflictsArr(g,allSolution,NUM,P,K);
				// 获取最佳解 注意:如果在第i次种群最优解比第i+1次种群的所有解都要好,则将第i次种群最优解替换第i+1次种群最差解
				getBestSolution(allSolution,NUM,P);
				if((iteration%400==0)){
					System.out.println("the iteration "+iteration+" conflict:"+bestConflict);
				}
				++iteration;
				
			}
			System.out.println("the best solution:");
			//printArr(bestSolution,NUM);
			ArrayList<ColorSet> colorList=solutionToColorSet(bestSolution,NUM,K);
			printNodeSet(K,colorList);
			System.out.println();
			System.out.println("min conflict:"+bestConflict);
		}
	}
	//	读取节点文件方法
	public static boolean readNodeFile(int[][] g, String path){
		File file=new File(path);
		if(!file.exists()){
			return false;
		}
		ArrayList<String> stringList=new ArrayList<String>();
		try{
			FileReader fr=new FileReader(file);
			BufferedReader br=new BufferedReader(fr);
			String str;
			
			while((str=br.readLine())!=null){
				stringList.add(str);
			}
			br.close();
			fr.close();
		}catch(IOException e){
			e.printStackTrace();
		}
		int m=0,n;
		for(int i=1;i<stringList.size();++i){
			String[] data=stringList.get(i).split("[ ]");
			for(int j=1;j<data.length;++j){
				n=Integer.valueOf(data[j]);
				g[m][n]=1;
			}
			++m;
		}
		return true;
	}
	// 生成初始种群
	public static void initSolution(int[][] allSolution,int p,int n,int K,int seed){
		for(int i=0;i<p;++i){
			generateOneSolution(allSolution[i],n,K,seed+i);
		}
	}
	// 生成一个初始解
	public static void generateOneSolution(int[] colorArr,int n,int K, int seed){
		Random rand=new Random();
		rand.setSeed(seed);
		for(int i=0;i<n;++i){
			int k=rand.nextInt(K);
			colorArr[i]=k;
		}
	}
	// 将解染色方案生成对应的颜色集合,便于计算解的冲突
	public static ArrayList<ColorSet> solutionToColorSet(int[] solution,int n,int K){
		ArrayList<ColorSet> colorList=new ArrayList<ColorSet>(K);
		for(int i=0;i<K;++i){
			ColorSet color=new ColorSet();
			colorList.add(color);
		}
		for(int i=0;i<n;++i){
			ColorSet color=colorList.get(solution[i]);
			color.add(i);
		}
		return colorList;
	}
	// 计算一个解的冲突
	public static int calculateSolutionConflicts(int[][] g, int[] solution,int n,int K ){
		
		ArrayList<ColorSet> colorList=solutionToColorSet(solution,n,K);
		//printNodeSet(K,colorList);
		int conflicts=0;
		for(int i=0;i<K;++i){
			ColorSet color=colorList.get(i);
			for(int j=0;j<color.length()-1;++j){
				int row=color.get(j);
				for(int k=j+1;k<color.length();++k){
					int col=color.get(k);
					if(g[row][col]==1){
						conflicts +=1;	
					}
				}
			}
		}
		return conflicts;
	}
	//将某个解赋给最佳解
	public static void solutionToBestSoliution(int[] solution,int n){
		for(int i=0;i<n;++i){
			bestSolution[i]=solution[i];
		}
	}
	// 将新种群赋给原始种群
	public static void newAllSolutionToAllSoliution(int[][] allSolution,int[][] newAllSolution,int n,int P){
		for(int i=0;i<P;++i){
			for(int j=0;j<n;++j){
				allSolution[i][j]=newAllSolution[i][j];
			}
		}
	}
	// 计算种群中每个解的冲突
	public static void calculateConflictsArr(int[][] g,int[][] allSolution,int n,int P,int K){
		for(int i=0;i<P;++i){
			conflictsArr[i]=calculateSolutionConflicts(g,allSolution[i],NUM,K);
		}
	}
	// 获取最佳解 注意:如果在第i次种群最优解比第i+1次种群的所有解都要好,则将第i次种群最优解替换第i+1次种群最差解
	public static void getBestSolution(int[][] allSolution,int n, int P){
		// max记录冲突最大的索引 
		// flag用于标识此种群是否存在比上次种群优秀的解,有为1, 没有为0
		int  besti=-1, flag=0, max=0;
		for(int i=0;i<P;++i){
			if(bestConflict>conflictsArr[i]){
				bestConflict=conflictsArr[i];
				besti=i;
				flag=1;
			}
			if(conflictsArr[max]<conflictsArr[i]){
				max=i;
			}
		}
		if(flag==1){
			solutionToBestSoliution(allSolution[besti],n);
		}
		else{
			for(int k=0;k<n;++k){
				allSolution[max][k]=bestSolution[k];
			}
		}
	}
	// 采用轮盘赌算法实现种群的选择
	public static void selectSolution(int[][] allSolution,int[][] newAllSolution,int n,int P,int selectNum, int seed){
		/*举例:假设有冲突 12,15,16,18
		 *prob:1/12,1/15,1/16,1/18
		 *totalProb: 1/12+1/15+1/16+1/18
		 *accProb: (1/12)/totalProb, (1/12+1/15)/totalProb
		 *设计accProb的目的是,便于给定一个概率p,比较p与accProb的第i个值,若小于,则选中i
		*/
		// prob:存储每个解冲突的倒数
		double[] prob=new double[P];
		// 存储累计概率
		double[] accProb=new double[P];
		double totalProb=0.0;
		for(int i=0;i<P;++i){
			prob[i]=1.0/conflictsArr[i];
			totalProb +=prob[i];
		}
		for(int i=0;i<P;++i){
			if(i==0){
				accProb[i]=prob[i]/totalProb;
				continue;
			}
			accProb[i]=accProb[i-1]+prob[i]/totalProb;
		}
		Random rand=new Random();
		rand.setSeed(seed);
		for(int i=0;i<selectNum;++i){
			double p=rand.nextDouble();
			for(int j=0;j<P;++j){
				if(p<accProb[j]){
					for(int k=0;k<NUM;++k){
						newAllSolution[i][k]=allSolution[j][k];
					}
					break;
				}
			}
		}
		newSolutionIndex=selectNum;
	}
	// 实现两个解的交叉,
	public static void crossTwoSolution(int[] solution1,int [] solution2, int n, int K, int seed){
		Random rand=new Random();
		rand.setSeed(seed);
		int N=rand.nextInt(n);
		for(int i=0,j=n-1;i<N;++i,--j){
			int temp=solution1[i];
			solution1[i]=solution2[j];
			solution2[j]=temp;
		}
	}
	// 实现整个种群的交叉
	public static void crossSomeSolution(int[][] allSolution,int[][] newAllSolution, int n, int P, int K,int crossNunber, int seed){
		Random rand=new Random();
		rand.setSeed(seed);
		for(int i=0;i<crossNunber/2;++i){
			int index1=rand.nextInt(P);
			int index2=rand.nextInt(P);
			while(index1==index2){
				index2=rand.nextInt(P);
			}
			crossTwoSolution(allSolution[index1],allSolution[index2],n,K,seed);
			for(int k=0;k<NUM;++k){
				newAllSolution[newSolutionIndex][k]=allSolution[index1][k];
				newAllSolution[newSolutionIndex+1][k]=allSolution[index2][k];
			}
			newSolutionIndex +=2;
		}
		
	}
	// 实现种群的变异
	public static void mutateSolution(int[][] allSolution,int[][] newAllSolution,int n,int P, int K, int mutateNum,int seed){
		Random rand=new Random();
		rand.setSeed(seed);
		for(int i=0;i<mutateNum;++i){
			int mutateIndex=rand.nextInt(P);
			int index=rand.nextInt(n);
			int color=rand.nextInt(K);
			while(color==allSolution[mutateIndex][index]){
				color=rand.nextInt(K);
			}
			allSolution[mutateIndex][index]=color;	
			for(int k=0;k<NUM;++k){
				newAllSolution[newSolutionIndex][k]=allSolution[mutateIndex][k];
			}
			newSolutionIndex++;
		}
		
	}
	public static void printArr(int[] colorArr, int n){
		for(int i=0;i<n;++i){
			System.out.print(colorArr[i]+",");
			if((i+1)%50==0){
				System.out.println();
			}
		}
	}
	//	输出解的颜色集合
	public static void printNodeSet(int K,ArrayList<ColorSet> nodeList){
		for(int i=0;i<K;++i){
			ColorSet nodeSet=nodeList.get(i);
			int num=nodeSet.length();
			for(int j=0;j<num;++j){
				System.out.print(nodeSet.get(j)+",");
			}
			System.out.println();
			System.out.println();
		}	
	}
}

  • 7
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值