1 前言
之前我已经写过一篇禁忌搜索算法求解二维矩形装箱问题(java代码实现),如果有对二维矩形装箱问题的背景不是很了解的朋友可以去看看
2 代码迁移
项目的大体框架(一些实体类,数据读取类等)和禁忌搜索算法求解二维矩形装箱问题(java代码实现)中的差不多,所以本文只提供蚁群算法的核心代码,大家用的时候只需要将两个禁忌搜索算法类 替换为下文中的蚁群算法类 即可。
3 蚁群算法
3.1 蚂蚁类 Ant
@Data
public class Ant implements Cloneable {
private List<Square> squareList;
private List<PlaceSquare> placeSquareList;
private List<Integer> tabu;
private List<Integer> allowedCities;
private double[][] delta;
private double alpha;
private double beta;
private int squareNum;
private int firstSquare;
private int currentSquare;
private double[][] different;
double L, W;
Instance instance;
Solution localSolution;
public Ant(int squareNum, List<Square> squareList, double L, double W, Instance instance) {
this.squareNum = squareNum;
this.squareList = squareList;
this.L = L;
this.W = W;
this.instance = instance;
}
public void initAnt(double[][] different, double a, double b) {
alpha = a;
beta = b;
placeSquareList = new ArrayList<>();
allowedCities = new ArrayList<>();
tabu = new ArrayList<>();
this.different = different;
delta = new double[squareNum][squareNum];
for (int i = 0; i < squareNum; i++) {
Integer integer = i;
allowedCities.add(integer);
for (int j = 0; j < squareNum; j++) {
delta[i][j] = 0.f;
}
}
firstSquare = new Random(System.currentTimeMillis()).nextInt(squareNum);
for (Integer i : allowedCities) {
if (i == firstSquare) {
allowedCities.remove(i);
break;
}
}
tabu.add(firstSquare);
currentSquare = firstSquare;
}
public void selectNextSquare(double[][] pheromone) {
double[] p = new double[squareNum];
double sum = 0d;
for (Integer i : allowedCities) {
sum += Math.pow(pheromone[currentSquare][i], alpha)
* Math.pow(1.0 / different[currentSquare][i], beta);
}
for (int i = 0; i < squareNum; i++) {
boolean flag = false;
for (Integer j : allowedCities) {
if (i == j) {
p[i] = (Math.pow(pheromone[currentSquare][i], alpha) * Math
.pow(1.0 / different[currentSquare][i], beta)) / sum;
flag = true;
break;
}
}
if (!flag) {
p[i] = 0.f;
}
}
Random random = new Random(System.currentTimeMillis());
double sleectP = random.nextDouble();
int selectSquare = 0;
double sum1 = 0d;
for (int i = 0; i < squareNum; i++) {
sum1 += p[i];
if (sum1 >= sleectP) {
selectSquare = i;
break;
}
}
for (Integer i : allowedCities) {
if (i == selectSquare) {
allowedCities.remove(i);
break;
}
}
tabu.add(selectSquare);
currentSquare = selectSquare;
}
public Solution calculateTotalPlaceSquareS() {
return localSolution = packing(tabu.stream().map(index -> {
return squareList.get(index);
}).collect(Collectors.toList()));
}
private Solution packing(List<Square> squareList) {
List<PlaceSquare> placeSquareList = new ArrayList<>();
List<PlacePoint> placePointList = new ArrayList<>();
placePointList.add(new PlacePoint(0, 0, L));
for (int i = 0; i < placePointList.size();) {
PlacePoint placePoint = placePointList.get(i);
double maxMark = -1.0d;
int maxIndex = -1;
double isRotate = -1, curMarks = -1;
for (int j = 0; j < squareList.size(); j++) {
Square square = squareList.get(j);
double[] arr = getMarks(placePoint, square, placeSquareList);
double is_rotate = arr[0];
curMarks = arr[1];
if (curMarks > 0 && curMarks > maxMark) {
maxMark = curMarks;
maxIndex = j;
isRotate = is_rotate;
}
}
if (maxIndex < 0 && i < placePointList.size()) {
i++;
} else if (maxIndex < 0 && i >= placePointList.size()) {
break;
} else {
Square square = squareList.remove(maxIndex);
double l = square.getL();
double w = square.getW();
if (isRotate > 0) {
square.setL(w);
square.setW(l);
}
placePointList.remove(i);
placeSquareList.add(new PlaceSquare(placePoint.getX(), placePoint.getY(), square.getL(), square.getW()));
double surplus = placePoint.getLen() - square.getL();
if (surplus > 0) {
placePointList.add(new PlacePoint(placePoint.getX() + square.getL(), placePoint.getY(), surplus));
}
placePointList.add(new PlacePoint(placePoint.getX(), placePoint.getY() + square.getW(), square.getL()));
Collections.sort(placePointList);
i = 0;
if (isRotate > 0) {
square.setL(l);
square.setW(w);
}
}
}
Solution solution = new Solution();
solution.setInstance(instance);
solution.setSquareList(new ArrayList<>(squareList));
solution.setPlaceSquareList(new ArrayList<>(placeSquareList));
double rate = 0d;
double s = 0d;
for (PlaceSquare placeSquare : placeSquareList) {
s += (placeSquare.getL() * placeSquare.getW());
}
rate = s / (L * W);
solution.setRate(rate);
return solution;
}
private double[] getMarks(PlacePoint placePoint, Square square, List<PlaceSquare> placeSquareList) {
double delta = 0, mark1 = -1d, mark2 = -1d;
PlaceSquare placeSquare = new PlaceSquare(placePoint.getX(), placePoint.getY(), square.getL(), square.getW());
if (isOverlap(placeSquareList, placeSquare)) {
mark1 = -1.0d;
} else {
delta = Math.abs(placePoint.getLen() - square.getL());
mark1 = 1 - delta / placePoint.getLen();
}
mark2 = -1.0d;
if (instance.isRotateEnable()) {
placeSquare = new PlaceSquare(placePoint.getX(), placePoint.getY(), square.getW(), square.getL());
if (!isOverlap(placeSquareList, placeSquare)) {
delta = Math.abs(placePoint.getLen() - square.getW());
mark2 = 1 - delta / placePoint.getLen();
}
}
if (mark1 >= mark2) {
return new double[]{-1d, (int) (mark1 * 10)};
}
return new double[]{1d, (int) (mark2 * 10)};
}
public boolean isOverlap(List<PlaceSquare> placeSquareList, PlaceSquare tempPlaceSquare) {
if (tempPlaceSquare.getL() > L || tempPlaceSquare.getW() > W) {
return true;
}
if (tempPlaceSquare.getX() + tempPlaceSquare.getL() > L || tempPlaceSquare.getY() + tempPlaceSquare.getW() > W) {
return true;
}
for (PlaceSquare placeSquare : placeSquareList) {
if (placeSquare.getX() == tempPlaceSquare.getX() && placeSquare.getY() == tempPlaceSquare.getY()) {
placeSquareList.remove(placeSquare);
return true;
}
if (isOverlap2(placeSquare, tempPlaceSquare)) {
return true;
}
}
return false;
}
public boolean isOverlap2(PlaceSquare placeSquare, PlaceSquare tempPlaceSquare) {
double x1 = Math.max(placeSquare.getX(), tempPlaceSquare.getX());
double y1 = Math.max(placeSquare.getY(), tempPlaceSquare.getY());
double x2 = Math.min(placeSquare.getX() + placeSquare.getL(), tempPlaceSquare.getX() + tempPlaceSquare.getL());
double y2 = Math.min(placeSquare.getY() + placeSquare.getW(), tempPlaceSquare.getY() + tempPlaceSquare.getW());
if (x1 >= x2 || y1 >= y2) {
return false;
}
return true;
}
}
3.2 蚁群算法类 ACO_Packing
public class ACO_Packing {
public Ant[] ants;
public int antNum = 2;
public int squareNum;
public int MAX_GEN = 20;
public double[][] pheromone;
public double[][] different;
public double bestRate;
public int[] bestSquence;
public int bestT;
public List<Square> squareList;
public Instance instance;
double L, W;
public Solution bestSolution;
private double alpha = 1;
private double beta = 5;
private double rho = 0.5;
public ACO_Packing(Instance instance) {
this.squareList = instance.getSquareList();
this.instance = instance;
this.L = instance.getL();
this.W = instance.getW();
squareNum = squareList.size();
ants = new Ant[antNum];
}
public Solution search() {
long startTime = System.currentTimeMillis();
initVar();
bestSolution = solve();
long endTime = System.currentTimeMillis();
System.out.println("最佳迭代次数:"+bestT);
System.out.println("最佳利用率为:"+bestSolution.getRate());
System.out.println("程序运行了:" + (endTime - startTime) / 1000.0 + "秒");
return bestSolution;
}
public Solution solve() {
for (int g = 0; g < MAX_GEN; g++) {
for (int i = 0; i < antNum; i++) {
for (int j = 1; j < squareNum; j++) {
ants[i].selectNextSquare(pheromone);
}
ants[i].getTabu().add(ants[i].getFirstSquare());
ants[i].calculateTotalPlaceSquareS();
if (ants[i].getLocalSolution().getRate() > bestRate) {
bestRate = ants[i].getLocalSolution().getRate();
for (int k = 0; k < squareNum; k++) {
bestSquence[k] = ants[i].getTabu().get(k);
}
bestT = g;
bestSolution = ants[i].getLocalSolution();
System.out.println("蚂蚁"+(i+1)+": 当前迭代次数为:"+g+",当前最佳利用率为:"+bestSolution.getRate());
}
for (int j = 0; j < squareNum; j++) {
ants[i].getDelta()[ants[i].getTabu().get(j)][ants[i]
.getTabu().get(j + 1)] = (1.0 / ants[i]
.getLocalSolution().getRate());
ants[i].getDelta()[ants[i].getTabu().get(j + 1)][ants[i]
.getTabu().get(j)] = (1.0 / ants[i]
.getLocalSolution().getRate());
}
}
updatePheromone();
for (int i = 0; i < antNum; i++) {
ants[i].initAnt(different, alpha, beta);
}
}
return bestSolution;
}
public void initVar() {
bestSolution = new Solution();
different = new double[squareNum][squareNum];
for (int i = 0; i < squareNum; i++) {
for (int j = 0; j < squareNum; j++) {
if (i == j) {
different[i][j] = 0.0;
} else {
different[i][j] = getDifferent(squareList.get(i), squareList.get(j));
}
}
}
pheromone = new double[squareNum][squareNum];
for (int i = 0; i < squareNum; i++) {
for (int j = 0; j < squareNum; j++) {
pheromone[i][j] = 0.1;
}
}
bestRate = 0d;
bestSquence = new int[squareNum];
for (int i = 0; i < antNum; i++) {
ants[i] = new Ant(squareNum,squareList,L,W,instance);
ants[i].initAnt(different, alpha, beta);
}
}
private void updatePheromone() {
for (int i = 0; i < squareNum; i++) {
for (int j = 0; j < squareNum; j++) {
pheromone[i][j] = pheromone[i][j] * (1 - rho);
}
}
for (int i = 0; i < squareNum; i++) {
for (int j = 0; j < squareNum; j++) {
for (int k = 0; k < antNum; k++) {
pheromone[i][j] += ants[k].getDelta()[i][j];
}
}
}
}
public double getDifferent(Square A, Square B) {
double different = 0d;
different += Math.abs(A.getL()-B.getL())/B.getL();
different += Math.abs(A.getW()-B.getW())/B.getW();
return different;
}
}
4 运行结果
由于测试的时候我的电脑在跑其他程序,比较卡顿,所以我仅设置了2 只蚂蚁,和20 次迭代,最终跑出来利用率为96.7% ,大家迁移完代码之后记得要设置成合适的参数,否则可能会对解的质量会产生一定的影响。
5 后话
本文仅列出了使用蚁群算法求解二维矩形装箱问题的2个核心类,如果需要整个跑通,还需要其他类,具体可以参照禁忌搜索算法求解二维矩形装箱问题(java代码实现)
|