一:问题描述
给定n种物品,物品i(1≤i≥n)的体积为wi,价值为vi,将这n种物品放入容量为C的背包之中。在放入背包的物品总体积小于C的情况下,求放入背包物品的最大价值。
二:基本实现原理
1:编码
问题可以表示为一个n位的二进制码,第i位表示物品i,数值为0表示物品没有选中,1表示选中物品。
2:种群
种群是个体的集合
3:适应度
适应度在0-1背包问题中表示的是背包中的总价值的大小,总价值越大个体的适应度越大。
4:选择
从种群中选出部分个体之后进行重组或交叉,产生的新个体依据适应度函数进行优胜劣汰,选出优良个体。
5:交叉
不同的个体之间随机的进行杂交,其基因进行再次重组。其后产生新的具有不同适应度的新个体。
6:变异
变异指单个个体的基因按照小概率进行变化的方法。本题中采用的变异方法随机产生变异点,并进行了以下两种变异类型。
1) 变异点进行0和1的状态翻转。此方法趋于常规,但无甚缺点。
2) 变异点变为1,如果超出背包容积,则变为0。此方法更易产生较大的解,但如若初始种群产生的不好,则随后的变异绝不会产生最优解。
三:算法实现
1:初始化
初始化工作包括输入算法的参数,产生初始化种群,种群差异性越明显越好。
2:功能
行数共有以下几种功能:
1)数据结构初始化
2)种群初始化工作
3)选择操作
4)计算适应度函数功能
5)交叉操作
6)变异操作
7)产生随机数功能
8)最优解记录功能,将进化的每一个种群的最大值与之前产生的最优解进行比较,更大则替换之,否则不替换。
3:终止条件
在遗传一定大的代数时,可以认为产生过了优秀的个体,即最优解(或接近最优解)。
4:算法步骤
1) 在论域空间U上定义一个适应度函数f(x),给定种群规模N,交叉率Pc和变异率Pm,代数T;
2) 随机产生U中的N个染色体s1, s2, …, sN,组成初始种群S={s1, s2,…, sN},置代数计数器t=1;
3) 计算S中每个染色体的适应度f();
4) 若终止条件满足,则取S中适应度最大的染色体作为所求结果,算法结束。
5) 按选择概率P(xi)所决定的选中机会,每次从S中随机选定1个染色体并将其复制,共做N次,然后将复制所得的N个染色体组成群体S1;
6) 按交叉率Pc所决定的参加交叉的染色体数c,从S1中随机确定c个染色体,配对进行交叉操作,并用产生的新染色体代替原染色体,得群体S2;
7) 按变异率Pm所决定的变异次数m,从S2中随机确定m个染色体,分别进行变异操作,并用产生的新染色体代替原染色体,得群体S3;
8) 将群体S3作为新一代种群,即用S3代替S,t=t+1,转步3;
5:编程设计
1) 类的设计:
1:class Global {
publicfinal static int M =200; //种群的规模
publicfinal static int T = 1000; //遗传的最大代数
publicfinal static double pc = 0.8; //交叉率
publicfinal static double pv = 0.05; //变异率
}
2:class MaxValue{
publicstatic int[] have = null; //最大解选择到的物品
publicstatic int max_value = 0; //最大解的价值
}
3:public class GasolvePackage
2)
类的功能:
Global类中定义的相当于全局变量。
MaxValue类中记录最优解,have数组记录选择的到的物品,max_value记录最优解得价值
GasolvePackage类有遗传算法的各种函数
3)
函数设计:此中具体介绍一下两种最主要的部分
1:GasolvePackage类的构造器
public GasolvePackage(){
try{
BufferedReaderread=newBufferedReader(newInputStreamReader(newFileInputStream(“input.txt”)));
Stringa=newString();
Stringb=newString();
package_rj=Integer.parseInt(read.readLine().trim()); //读取背包容积
a=read.readLine(); //读取物品体积行
tiji=strArr_to_intArr(a.split(” “));
b=read.readLine(); //读取物品价值行
value =strArr_to_intArr(b.split(” “));
num=value.length;
MaxValue.have=new int[Global.M];
read.close();
}
catch(Exception e)
{
System.out.println(“请检查输入文件中的错误“);
}
}
private int[]strArr_to_intArr(String[] strArr){ //将字符数组转化为数字数组
int size = strArr.length;
int[] int_arr = new int[size];
for(int i = 0; i < size; i++){
int_arr[i]= Integer.valueOf(strArr[i]);
}
return int_arr;
}
此中主要从文本中输入背包容积,各个物品的体积以及各个物品的价值。
2:
public int[][] variation(int[][]cross_zq){ //变异算法
int i = 0;
int row_id = 0;
int col_id = 0;
Randomrand =newRandom();
int variation_num = (int)(Global.pv * Global.M * num);//参与变异的基因个数
int[][] variation_zq = new int[Global.M][num];
variation_zq= dCopy(cross_zq);
for(i = 0; i <variation_num; i ++){
row_id= rand.nextInt(Global.M);
col_id= rand.nextInt(num);
variation_zq[row_id][col_id]= 1-variation_zq[row_id][col_id];
//第二种变异为variation_zq[row_id][col_id] = 1;
if(get_singleWeight(variation_zq[row_id])> package_rj){
variation_zq[row_id][col_id]= 1-variation_zq[row_id][col_id];
//第二种变异为variation_zq[row_id][col_id] = 0;
}
}
return variation_zq;
}
四:测试结果(算例及四次随机测试结果)
五:程序源代码及其基本注释
import java.io.*;
import java.util.*;
classMaxValue{
publicstatic int[] have = null; //最大解选择到的物品
publicstatic int max_value = 0; //最大解的价值
}
classGlobal {
publicfinal static int M =200; //种群的规模
publicfinal static int T = 1000; //遗传的最大代数
publicfinal static double pc = 0.8; //交叉率
publicfinal static double pv = 0.05; //变异率
}
public class GasolvePackage {
privateint package_rj = 0; //背包的容量
privateint num = 0; //物品的个数
privateint[]tiji = null; //物品的容积
privateint[]value = null; //物品的价值
privateint[][] zq = null; //种群
publicGasolvePackage(){
try{
BufferedReaderread=new BufferedReader(new InputStreamReader(newFileInputStream(“input.txt”)));
Stringa=new String();
Stringb=new String();
package_rj=Integer.parseInt(read.readLine().trim());
a=read.readLine();
tiji=strArr_to_intArr(a.split(“”));
b=read.readLine();
value= strArr_to_intArr(b.split(” “));
num=value.length;
MaxValue.have=newint[Global.M];
read.close();
}
catch(Exceptione)
{
System.out.println(“请检查输入文件中的错误”);
}
}
//字符串数组转换为整数数组
privateint[] strArr_to_intArr(String[] strArr){
intsize = strArr.length;
int[]int_arr = new int[size];
for(inti = 0; i < size; i ++){
int_arr[i]= Integer.valueOf(strArr[i]);
}
returnint_arr;
}
privateint[][] dCopy(int[][] source){ //二维数组的复制
introw_num = source.length;
intcol_num = source[0].length;
int[][]des = new int[row_num][col_num];
for(inti = 0; i < row_num; i ++){
for(intj = 0; j < col_num; j ++){
des[i][j]= source[i][j];
}
}
returndes;
}
//产生初始种群
publicint[][] init(){
Randomrand=new Random();
zq= new int[Global.M][num];
for(inti = 0; i < Global.M; i ++){
for(intj = 0; j <num; j ++){
zq[i][j] = rand.nextInt(2);
}
if(get_singleWeight(zq[i])> package_rj){
i–;
}
}
returnzq;
}
//计算个体的总重量
privateint get_singleWeight(int[] single){
inttotal_weight = 0;
intsize = single.length;
for(inti = 0; i < size; i ++){
total_weight+= tiji[i]*single[i];
}
returntotal_weight;
}
//计算个体的总价值
privateint get_singleValue(int[] single){
inttotal_value = 0;
intsize = single.length;
for(inti = 0; i < size; i ++){
total_value+= value[i]*single[i];
}
returntotal_value;
}
//获取总价值最大的个体
publicvoid get_maxValue_single(int[][] popu){
intsize = popu.length;
int[]fitness = new int[size];
for(inti = 0; i < size; i ++){
fitness[i]= get_singleValue(popu[i]);
}
intid = 0;
intmax_value = fitness[0];
for(intj = 1; j < size; j ++){
if(fitness[j]> max_value){
max_value= fitness[j];
id= j;
}
}
if(max_value> MaxValue.max_value){
MaxValue.max_value= max_value;
int[]have = new int[num];
for(inti = 0; i < num; i ++){
have[i]= popu[id][i];
}
MaxValue.have= have;
}
}
//计算每个个体的适应度
publicdouble[] getFitness(int[][] popu){
intsize = popu.length;
double[]fitness = new double[size];
for(inti = 0; i < size; i ++){
fitness[i]= get_singleValue(popu[i]);
}
returnfitness;
}
//计算每个个体的选择概率
privatedouble[] get_selectRate(double[] fitness){
doublesum = 0;
intsize = fitness.length;
double[]select_rate = new double[size];
for(inti = 0; i < size; i ++){
sum+= fitness[i];
}
for(intj = 0; j < size; j ++){
select_rate[j]= fitness[j]/sum;
}
returnselect_rate;
}
//计算每个个体的圆盘概率
privatedouble[] get_accuRate(double[] select_rate){
inti = 0;
double[]accu_rate = new double[select_rate.length];
for(i= 0; i < select_rate.length; i ++){
accu_rate[i]= select_rate[i];
}
for(i= 1; i < select_rate.length; i ++){
accu_rate[i]+= accu_rate[i-1];
}
returnaccu_rate;
}
publicint[][]select(double[]select,int[][]zhong) //选择产生新的种群
{
Randomrand=new Random();
doublet=0;
int[][]ans=new int[Global.M][num];
for(inti=0;i<zhong.length;i++)
for(intj=0;j<zhong[0].length;j++)
ans[i][j]=zhong[i][j];
for(inti=0;i<Global.M;i++)
{
t=rand.nextInt(101)/100;
for(intj=0;j>num;j++)
if(t<=select[j])
{
for(intp=0;p<num;p++)
ans[i][p]=zhong[j][p];
break;
}
}
returnans;
}
//交叉
publicint[][] crossover(int[][] select_zq){
int i = 0;
Random rand =new Random();
int random_pos = 0, temp = 0;//交换基因的位置
int cross_num = (int)(Global.pc * Global.M);//参与交换的个体数
int[][] cross_popu = new int[Global.M][num];
cross_popu = dCopy(select_zq);
for(i = 1; i < cross_num; i += 2){
random_pos = rand.nextInt(num);
temp = cross_popu[i][random_pos];
cross_popu[i][random_pos]=cross_popu[i-1][random_pos];
cross_popu[i-1][random_pos] = temp;
if(get_singleWeight(cross_popu[i]) >package_rj || get_singleWeight(cross_popu[i-1]) > package_rj){
temp = cross_popu[i][random_pos];
cross_popu[i][random_pos]=cross_popu[i-1][random_pos];
cross_popu[i-1][random_pos] = temp;
}
}
return cross_popu;
}
//变异算法1
publicint[][] variation(int[][] cross_zq){
inti = 0;
introw_id = 0;
intcol_id = 0;
Randomrand =new Random();
intvariation_num = (int)(Global.pv * Global.M * num);//参与变异的基因个数
int[][]variation_zq = new int[Global.M][num];
variation_zq= dCopy(cross_zq);
for(i= 0; i < variation_num; i ++){
row_id= rand.nextInt(Global.M);
col_id= rand.nextInt(num);
variation_zq[row_id][col_id]= 1-variation_zq[row_id][col_id];
if(get_singleWeight(variation_zq[row_id])> package_rj){
variation_zq[row_id][col_id]= 1-variation_zq[row_id][col_id];
}
}
return variation_zq;
}
/* publicint[][] variation(int[][] cross_zq){ //变异算法2
inti = 0;
introw_id = 0;
intcol_id = 0;
Randomrand =new Random();
intvariation_num = (int)(Global.pv * Global.M * num);//参与变异的基因个数
int[][]variation_zq = new int[Global.M][num];
variation_zq= dCopy(cross_zq);
for(i= 0; i < variation_num; i ++){
row_id= rand.nextInt(Global.M);
col_id= rand.nextInt(num);
variation_zq[row_id][col_id]=1;
if(get_singleWeight(variation_zq[row_id])> package_rj){
variation_zq[row_id][col_id]= 0;
}
}
return variation_zq;
}
*/
//遗传算法
publicvoid allway(){
intpopu_id = 1; //总群的代数
double[]fitness = null;
double[]select_rate = null;
double[]accu_rate = null;
int[][]select_popu = null;
int[][]cross_popu = null;
int[][]popu = init();
get_maxValue_single(popu);
while(popu_id< Global.T){//没有终止
fitness= getFitness(popu);
select_rate= get_selectRate(fitness);
accu_rate= get_accuRate(select_rate);
select_popu= select(accu_rate, popu);
cross_popu= crossover(select_popu);
popu= variation(cross_popu);//下一代总群
popu_id++;
get_maxValue_single(popu);
}
}
//输出
publicvoid show(){
System.out.print(“背包容积:”);
System.out.println(package_rj);
System.out.print(“选择物品的体积:”);
for(intj = 0; j < num; j ++){
if(MaxValue.have[j]== 1){
System.out.print(tiji[j]+ ” “);
}
}
intMaxHeight=0;
for(inti=0;i<num;i++)
MaxHeight+=MaxValue.have[i]*tiji[i];
System.out.println();
System.out.print(“选择物品的总体积:”);
System.out.println(MaxHeight);
System.out.print(“选择物品的价值:”);
for(intj = 0; j <num; j ++){
if(MaxValue.have[j]== 1){
System.out.print(value[j]+ ” “);
}
}
System.out.println();
System.out.print(“物品的总价值:”);
System.out.print(MaxValue.max_value);
}
publicstatic void main(String[] args){
GasolvePackagega = new GasolvePackage();
ga.allway();
ga.show();
}
}
六:心得体会
为了做本次的遗传算法解决0-1背包问题的程序,几乎又重新翻阅了一遍java书籍,许多不大懂的问题都上网仔细查阅资料(如:文件输入,字符型转为数字,随机数的实现……)
但刚开始的时候,程序虽说已经做好,但产生的最优解却始终停留在900上下,很少能够达到1000。
如:
我分析可能有以下几种情况,种群初始化不行,或最优解实际并没有找到。于是我重新设计了产生随机数的代码;设计了MaxValue类记录产生的所有种群的最优解;并对变异一段改造,使其进行了我所谓的引导变异,即是使个体向大处变异,从而总体上提升了算法产生最优解的能力。
在随后的测试中,产生的结果全部大于1000,产生最优解1042的概率也明显提升。
在本次应用遗传算法中,提高了自己的编程技巧,深刻的体会到了随机算法的便利性,获得了对随机算法更深的理解及应用能力。