遗传算法和背包问题的具体细节我就不在这里描述了,下面主要简单介绍下如何将遗传算法应用到背包问题中。
要将遗传算法应用到一个问题中,需要解决下面几个问题:
1. 编码问题;
2. 适应度函数的定义;
3. 选择算子;
4. 交叉算子;
5. 变异算子。
那么现在我们来挨个解决在背包问题下上述这5个问题。
1. 首先我们看编码问题,这个比较好解决,对于n个物品,可以用n个二进制位来进行编码,值为1表示该物品加到背包中,值为0表示该物品不加入到背包中。
2. 适应度函数对于优质个体,其值应该较高;而对于劣质个体,其值应该较低。这样我们就可以通过适应度函数的取值来判断个体的优劣。很显然,适应度函数可以定义为一个个体所表示加入到背包中物品的价值总和(当然,它们的总重量得小于背包的容量,不然为0)。
3.选择算子,本文中选择算子的操作流程是:首先保留当前种群中最优的10%的个体;然后在剩下的90%的个体中再添加随机生成的10%的个体;从第二步中的100%的个体中随机选择90%的个体,这样加上第一次选择的10%的个体便可构成新一代的种群。
4. 交叉算子,种群中的个体两两组队,相互交叉,交叉时相对应位上按照某个可能性(具体的可能性可设置)进行交换值。
5. 变异算子,每个新的个体,都有可能发生变异(具体的可能性可设置)。一旦这个个体发生变异,那么这个个体上的每一位按照某个可能性(具体的可能性可设置)赋一个随机值。
其实,初始种群的生成也很重要,一个好的初始种群(随机性+稍微靠谱点),算法的执行结果往往更好。
本文在生成初始种群时,对于每个个体都生成一个0.5c至1.5c之间(c为背包容量)的一个随机值tmp,然后在该个体的编码上不断随机出一位,如果该位还未被设置为1,则设置其为1,并记录该个体所表示加入到背包中物品的价值总和,直至这个总和超过tmp(为了防止初始化耗费太多计算资源,当连续3次随机出的位都已经为1,该个体就生成结束了)。
下面看具体程序
这是物品信息读入的类:
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
public class Reader {
public static List
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
public class GAKnapsack {
private Random random = null; //随机数生成器
private float[] weight = null; //物品重量
private float[] profit = null; //物品价值
private int len; // 染色体长度
private float capacity; //背包容量
private int scale; //种群规模
private int maxgen; //最大代数
private float irate; //交叉率(所有的个体都需要相互交叉的,这里的交叉率指交叉时每位交叉发生交叉的可能性)
private float arate1; //变异率(某个个体发生变异的可能性)
private float arate2; //对于确定发生变异的个体每位发生变异的可能性
private File data = null; //物品重量和物品价值的数据文件
private boolean[][] population = null; //上一代种群
private float[] fitness = null; //种群的适应度
private float bestFitness; //最优个体的价值
private boolean[] bestUnit = null; //最优个体的物品取舍策略
class SortFitness implements Comparable{
int index;
float fitness;
public int compareTo(SortFitness c) {
float cfitness = c.fitness;
if(fitness > cfitness) {
return -1;
} else if(fitness < cfitness) {
return 1;
} else {
return 0;
}
}
}
/**
* @param capacity : 背包容量
* @param scale : 种群规模
* @param maxgen : 最大代数
* @param irate : 交叉率(所有的个体都需要相互交叉的,这里的交叉率指交叉时每位交叉发生交叉的可能性)
* @param arate1 :变异率(某个个体发生变异的可能性)
* @param arate2 :对于确定发生变异的个体每位发生变异的可能性
* @param file : 物品重量和物品价值的数据文件
*/
public GAKnapsack(float capacity, int scale, int maxgen, float irate, float arate1, float arate2, File data) {
this.capacity = capacity;
this.scale = scale;
this.maxgen = maxgen;
this.irate = irate;
this.arate1 = arate1;
this.arate2 = arate2;
this.data = data;
random = new Random(System.currentTimeMillis());
}
//读取物品重量和物品价值数据
private void readDate() {
List tmp = Reader.read(data);
weight = (float[])tmp.get(0);
profit = (float[])tmp.get(1);
len = weight.length;
}
//初始化初始种群
private void initPopulation() {
fitness = new float[scale];
population = new boolean[scale][len];
//考虑到随机生成的初始化种群效果有可能不好,这里对于种群的初始化作了一定的优化
//对于每一个个体,先随机一个容量值(0.5 capacity 至 1.5 capacity)
//然后随机相应的物品到该个体中,直到超过上面随机的容量
for(int i = 0; i < scale; i++) {
float tmp = (float)(0.5 + Math.random()) * capacity;
int count = 0; //防止初始化耗费太多计算资源
for(int j = 0; j < tmp;) {
int k = random.nextInt(len);
if(population[i][k]) {
if(count == 3) {
break;
}
count++;
continue;
} else {
population[i][k] = true;
j += weight[k];
count = 0;
}
}
}
}
//计算一个个体的适应度
private float evaluate(boolean[] unit) {
float profitSum = 0;
float weightSum = 0;
for (int i = 0; i < unit.length; i++) {
if (unit[i]) {
weightSum += weight[i];
profitSum += profit[i];
}
}
if (weightSum > capacity) {
//该个体的对应的所有物品的重量超过了背包的容量
return 0;
} else {
return profitSum;
}
}
//计算种群所有个体的适应度
private void calcFitness() {
for(int i = 0; i < scale; i++) {
fitness[i] = evaluate(population[i]);
}
}
//记录最优个体
private void recBest(int gen) {
for(int i = 0; i < scale; i++) {
if(fitness[i] > bestFitness) {
bestFitness = fitness[i];
bestUnit = new boolean[len];
for(int j = 0; j < len; j++) {
bestUnit[j] = population[i][j];
}
}
}
}
//种群个体选择
//选择策略:适应度前10%的个体带到下一次循环中,然后在(随机生成10%的个体 + 剩下的90%个体)中随机取90%出来
private void select() {
SortFitness[] sortFitness = new SortFitness[scale];
for(int i = 0; i < scale; i++) {
sortFitness[i] = new SortFitness();
sortFitness[i].index = i;
sortFitness[i].fitness = fitness[i];
}
Arrays.sort(sortFitness);
boolean[][] tmpPopulation = new boolean[scale][len];
//保留前10%的个体
int reserve = (int)(scale * 0.1);
for(int i = 0; i < reserve; i++) {
for(int j = 0; j < len; j++) {
tmpPopulation[i][j] = population[sortFitness[i].index][j];
}
//将加入后的个体随机化
for(int j = 0; j < len; j++) {
population[sortFitness[i].index][j] = false;
}
float tmpc = (float)(0.5 + Math.random()) * capacity;
int count = 0;
for(int j = 0; j < tmpc;) {
int k = random.nextInt(len);
if(population[sortFitness[i].index][k]) {
if(count == 3) {
break;
}
count++;
continue;
} else {
population[sortFitness[i].index][k] = true;
j += weight[k];
count = 0;
}
}//
}
//再随机90%的个体出来
List list = new ArrayList();
for(int i = 0; i < scale; i++) {
list.add(i);
}
for(int i = reserve; i < scale; i++) {
int selectid = list.remove((int)(list.size()*Math.random()));
for(int j = 0; j < len; j++) {
tmpPopulation[i][j] = population[selectid][j];
}
}
population = tmpPopulation;
}
//进行交叉
private void intersect() {
for(int i = 0; i < scale; i = i + 2)
for(int j = 0; j < len; j++) {
if(Math.random() < irate) {
boolean tmp = population[i][j];
population[i][j] = population[i + 1][j];
population[i + 1][j] = tmp;
}
}
}
//变异
private void aberra() {
for(int i = 0; i < scale; i++) {
if(Math.random() > arate1) {
continue;
}
for(int j = 0; j < len; j++) {
if(Math.random() < arate2) {
population[i][j] = Math.random() > 0.5 ? true : false;
}
}
}
}
//遗传算法
public void solve() {
readDate();
initPopulation();
for(int i = 0; i < maxgen; i++) {
//计算种群适应度值
calcFitness();
//记录最优个体
recBest(i);
//进行种群选择
select();
//进行交叉
intersect();
//发生变异
aberra();
}
int totalWeight = 0;
for(int i = 0; i < bestUnit.length; i++) {
if(bestUnit[i]){
totalWeight += weight[i];
}
}
System.out.println("total profit:" + bestFitness);
System.out.println("total weight:" + totalWeight);
}
public static void main(String[] args) {
File data = new File(".//data//data1.txt");
//背包容量
//种群规模
//最大代数
//交叉率(所有的个体都需要相互交叉的,这里的交叉率指交叉时每位交叉发生交叉的可能性)
//变异率(某个个体发生变异的可能性)
//对于确定发生变异的个体每位发生变异的可能性
//物品重量和物品价值的数据文件
GAKnapsack gaKnapsack = new GAKnapsack(1000, 200, 2000, 0.5f, 0.05f, 0.1f, data);
gaKnapsack.solve();
}
}