|
@@ -0,0 +1,328 @@
|
|
|
+package com.sd.framework.util.packing.model;
|
|
|
+
|
|
|
+import com.sd.framework.util.packing.entity.*;
|
|
|
+import lombok.Data;
|
|
|
+
|
|
|
+import java.util.*;
|
|
|
+
|
|
|
+@Data
|
|
|
+public class TabuSearch {
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 最大的迭代次数(提高这个值可以稳定地提高解质量,但是会增加求解时间)
|
|
|
+ */
|
|
|
+ public final int MAX_GEN = 10;
|
|
|
+ /**
|
|
|
+ * 每次搜索领域的个数(这个值不要太大,太大的话搜索效率会降低)
|
|
|
+ */
|
|
|
+ public final int N = 200;
|
|
|
+ /**
|
|
|
+ * 禁忌队列
|
|
|
+ */
|
|
|
+ public HashMap<Long, TabuMapTree> tabuTreeMap = new HashMap<>();
|
|
|
+ /**
|
|
|
+ * 矩形数量
|
|
|
+ */
|
|
|
+ public int sqNum;
|
|
|
+ /**
|
|
|
+ * 初始顺序
|
|
|
+ */
|
|
|
+ public List<Square> initGhh;
|
|
|
+ /**
|
|
|
+ * 最佳顺序
|
|
|
+ */
|
|
|
+ public List<Square> bestGh;
|
|
|
+ /**
|
|
|
+ * 当前最好顺序
|
|
|
+ */
|
|
|
+ public List<Square> LocalGh;
|
|
|
+ /**
|
|
|
+ * 存放临时顺序
|
|
|
+ */
|
|
|
+ public List<Square> tempGh;
|
|
|
+ /**
|
|
|
+ * 最佳的迭代次数
|
|
|
+ */
|
|
|
+ public int bestT;
|
|
|
+ /**
|
|
|
+ * 最优解
|
|
|
+ */
|
|
|
+ public Solution bestSolution;
|
|
|
+ /**
|
|
|
+ * 每次领域搜索的最优解(领域最优解)
|
|
|
+ */
|
|
|
+ public Solution LocalSolution;
|
|
|
+ /**
|
|
|
+ * 临时解
|
|
|
+ */
|
|
|
+ public Solution tempSolution;
|
|
|
+ /**
|
|
|
+ * 当前迭代
|
|
|
+ */
|
|
|
+ public int t;
|
|
|
+ /**
|
|
|
+ * 随机函数对象
|
|
|
+ */
|
|
|
+ public Random random;
|
|
|
+ /**
|
|
|
+ * 问题实例
|
|
|
+ */
|
|
|
+ public Instance instance;
|
|
|
+ /**
|
|
|
+ * 长
|
|
|
+ */
|
|
|
+ double length;
|
|
|
+ /**
|
|
|
+ * 宽
|
|
|
+ */
|
|
|
+ double width;
|
|
|
+
|
|
|
+ public TabuSearch(Instance instance) {
|
|
|
+ this.instance = instance;
|
|
|
+ this.initGhh = new ArrayList<>(instance.getSquareList());
|
|
|
+ // 初始化变量
|
|
|
+ random = new Random(System.currentTimeMillis());
|
|
|
+ length = instance.getLength();
|
|
|
+ width = instance.getWidth();
|
|
|
+ sqNum = initGhh.size();
|
|
|
+ }
|
|
|
+
|
|
|
+ public Solution search() {
|
|
|
+ long start = System.currentTimeMillis();
|
|
|
+ // 获取初始解
|
|
|
+ getInitSolution();
|
|
|
+ System.out.println(bestSolution.getRate());
|
|
|
+ //开始迭代,停止条件为达到指定迭代次数
|
|
|
+ while (t < MAX_GEN) {
|
|
|
+ //当前领域搜索次数
|
|
|
+ int n = 0;
|
|
|
+ LocalSolution = new Solution();
|
|
|
+ LocalSolution.setRate(0);
|
|
|
+ while (n < N) {
|
|
|
+ // 随机打乱顺序 得到当前编码Ghh的邻居编码tempGh
|
|
|
+ tempGh = generateNewGh(new ArrayList<>(initGhh));
|
|
|
+ // 判断其是否在禁忌表中
|
|
|
+ if (!judge(tempGh)) {
|
|
|
+ //加入禁忌表
|
|
|
+ enterTabooList(tempGh);
|
|
|
+ tempSolution = evaluate(new ArrayList<>(tempGh));
|
|
|
+ if (tempSolution.getRate() > LocalSolution.getRate()) {
|
|
|
+ // 如果临时解优于本次领域搜索的最优解
|
|
|
+ // 那么就将临时解替换本次领域搜索的最优解
|
|
|
+ LocalGh = new ArrayList<>(tempGh);
|
|
|
+ LocalSolution = tempSolution;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ n++;
|
|
|
+ }
|
|
|
+ if (LocalSolution.getRate() > bestSolution.getRate()) {
|
|
|
+ //如果本次搜索的最优解优于全局最优解
|
|
|
+ //那么领域最优解替换全局最优解
|
|
|
+ bestT = t;
|
|
|
+ bestGh = new ArrayList<>(LocalGh);
|
|
|
+ bestSolution = LocalSolution;
|
|
|
+ }
|
|
|
+ initGhh = new ArrayList<>(LocalGh);
|
|
|
+ t++;
|
|
|
+ System.out.println("当前迭代次数为:" + t + ",当前最佳利用率为:" + bestSolution.getRate());
|
|
|
+ }
|
|
|
+ //求解完毕
|
|
|
+ System.out.println("最佳迭代次数:" + bestT);
|
|
|
+ System.out.println("最佳利用率为:" + bestSolution.getRate());
|
|
|
+ System.out.println("用时:" + (System.currentTimeMillis() - start) + "ms");
|
|
|
+ return bestSolution;
|
|
|
+ }
|
|
|
+
|
|
|
+ //评价函数
|
|
|
+ public Solution evaluate(List<Square> squareList) {
|
|
|
+ Solution solution = new Solution();
|
|
|
+ solution.setInstance(instance);
|
|
|
+ solution.setSquareList(new ArrayList<>(squareList));
|
|
|
+ List<PlaceSquare> placeSquareList = new ArrayList<>();
|
|
|
+ // 创建初始可放置角点
|
|
|
+ List<PlacePoint> placePointList = new ArrayList<>();
|
|
|
+ placePointList.add(new PlacePoint(0, 0, length));
|
|
|
+
|
|
|
+ // 开始按照顺序和规则放置
|
|
|
+ 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) {
|
|
|
+ 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);
|
|
|
+ //新增已放置的square
|
|
|
+ placeSquareList.add(new PlaceSquare(square.getId(), placePoint.getX(), placePoint.getY(), square.getL(), square.getW()));
|
|
|
+ // 新增两个可行角点
|
|
|
+ double surplus = placePoint.getLength() - 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.setPlaceSquareList(new ArrayList<>(placeSquareList));
|
|
|
+ // 计算利用率
|
|
|
+ double rate;
|
|
|
+ double s = 0.0f;
|
|
|
+ for (PlaceSquare placeSquare : placeSquareList) {
|
|
|
+ s += (placeSquare.getL() * placeSquare.getW());
|
|
|
+ }
|
|
|
+ rate = s / (length * width);
|
|
|
+ solution.setRate(rate);
|
|
|
+ return solution;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 评价该点的得分
|
|
|
+ private double[] getMarks(PlacePoint placePoint, Square square, List<PlaceSquare> placeSquareList) {
|
|
|
+ // 返回{是否旋转,分数}
|
|
|
+ double delta, mark1, mark2;
|
|
|
+ PlaceSquare placeSquare = new PlaceSquare(square.getId(), placePoint.getX(), placePoint.getY(), square.getL(), square.getW());
|
|
|
+ if (isOverlap(placeSquareList, placeSquare)) {
|
|
|
+ mark1 = -1.0d;
|
|
|
+ } else {
|
|
|
+ delta = Math.abs(placePoint.getLength() - square.getL());
|
|
|
+ mark1 = 1 - delta / placePoint.getLength();
|
|
|
+ }
|
|
|
+ mark2 = -1.0d;
|
|
|
+ if (instance.isRotateEnable()) {
|
|
|
+ placeSquare = new PlaceSquare(square.getId(), placePoint.getX(), placePoint.getY(), square.getW(), square.getL());
|
|
|
+ if (!isOverlap(placeSquareList, placeSquare)) {
|
|
|
+ delta = Math.abs(placePoint.getLength() - square.getW());
|
|
|
+ mark2 = 1 - delta / placePoint.getLength();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ 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() > length || tempPlaceSquare.getW() > width) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ // 出界
|
|
|
+ if (tempPlaceSquare.getX() + tempPlaceSquare.getL() > length || tempPlaceSquare.getY() + tempPlaceSquare.getW() > width) {
|
|
|
+ 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());
|
|
|
+ return !(x1 >= x2) && !(y1 >= y2);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 生成初始解
|
|
|
+ public void getInitSolution() {
|
|
|
+ Collections.shuffle(initGhh);
|
|
|
+ bestSolution = evaluate(new ArrayList<>(initGhh));
|
|
|
+ tempSolution = bestSolution;
|
|
|
+ bestGh = new ArrayList<>(initGhh);
|
|
|
+ tempGh = new ArrayList<>(initGhh);
|
|
|
+ LocalGh = new ArrayList<>(initGhh);
|
|
|
+ }
|
|
|
+
|
|
|
+ //加入禁忌队列
|
|
|
+ public void enterTabooList(List<Square> squareList) {
|
|
|
+ if (tabuTreeMap == null) {
|
|
|
+ tabuTreeMap = new HashMap<>();
|
|
|
+ }
|
|
|
+ Square square = squareList.get(0);
|
|
|
+ Long id = square.getId();
|
|
|
+ if (tabuTreeMap.containsKey(id)) {
|
|
|
+ tabuTreeMap.get(id).add(new ArrayList<>(squareList), 1);
|
|
|
+ } else {
|
|
|
+ TabuMapTree tabuMapTree = new TabuMapTree();
|
|
|
+ tabuMapTree.setNodeSquare(square);
|
|
|
+ tabuMapTree.add(new ArrayList<>(squareList), 1);
|
|
|
+ tabuTreeMap.put(id, tabuMapTree);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //生成新解
|
|
|
+ public List<Square> generateNewGh(List<Square> localGh) {
|
|
|
+ Square temp;
|
|
|
+ //将Gh复制到tempGh
|
|
|
+ List<Square> tempGh = new ArrayList<>(localGh);
|
|
|
+
|
|
|
+ for (int i = 0; i < 6; i++) {
|
|
|
+ int r1 = 0;
|
|
|
+ int r2 = 0;
|
|
|
+
|
|
|
+ while (r1 == r2) {
|
|
|
+ r1 = random.nextInt(tempGh.size());
|
|
|
+ r2 = random.nextInt(tempGh.size());
|
|
|
+ }
|
|
|
+ //交换
|
|
|
+ temp = tempGh.get(r1);
|
|
|
+ tempGh.set(r1, tempGh.get(r2));
|
|
|
+ tempGh.set(r2, temp);
|
|
|
+ }
|
|
|
+
|
|
|
+ return new ArrayList<>(tempGh);
|
|
|
+ }
|
|
|
+
|
|
|
+ //判断路径编码是否存在于禁忌表中
|
|
|
+ public boolean judge(List<Square> Gh) {
|
|
|
+ Square square = Gh.get(0);
|
|
|
+ if (tabuTreeMap.containsKey(square.getId())) {
|
|
|
+ return tabuTreeMap.get(square.getId()).contains(Gh, 1);
|
|
|
+ } else {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+}
|