24282 1 жил өмнө
parent
commit
b4f78e0811

+ 0 - 86
sd-framework/src/main/java/com/sd/framework/util/packing/Application.java

@@ -1,86 +0,0 @@
-package com.sd.framework.util.packing;
-
-
-import com.sd.framework.util.packing.entity.Instance;
-import com.sd.framework.util.packing.entity.PlaceSquare;
-import com.sd.framework.util.packing.entity.Solution;
-import com.sd.framework.util.packing.model.TabuSearch;
-import javafx.scene.Scene;
-import javafx.scene.canvas.Canvas;
-import javafx.scene.canvas.GraphicsContext;
-import javafx.scene.control.Alert;
-import javafx.scene.control.Button;
-import javafx.scene.layout.AnchorPane;
-import javafx.scene.paint.Color;
-import javafx.stage.Stage;
-
-import java.util.Random;
-
-/**
- * 二维装箱禁忌搜索算法
- */
-public class Application extends javafx.application.Application {
-
-    private int counter = 0;
-
-    public static void main(String[] args) {
-        launch(args);
-    }
-
-    @Override
-    public void start(Stage primaryStage) throws Exception {
-
-        String path = "src/main/resources/data.txt";
-
-        TabuSearch model = new TabuSearch(ReadDataUtil.getInstance(path));
-        Solution solution = model.search();
-
-        Instance instance = solution.getInstance();
-        AnchorPane pane = new AnchorPane();
-        plot(solution);
-        Canvas canvas = new Canvas(instance.getLength(), instance.getWidth());
-        pane.getChildren().add(canvas);
-        canvas.relocate(100, 100);
-        // 绘制最外层的矩形
-        draw(canvas, 0, 0, instance.getLength(), instance.getWidth());
-        // 添加按钮
-        Button nextButton = getButton(solution, canvas);
-        pane.getChildren().add(nextButton);
-        primaryStage.setTitle("二维下料可视化");
-        primaryStage.setScene(new Scene(pane, 1000, 1000, Color.AQUA));
-        primaryStage.show();
-    }
-
-    private Button getButton(Solution solution, Canvas canvas) {
-        Button nextButton = new Button("Next");
-        nextButton.setOnAction(actionEvent -> {
-            try {
-                PlaceSquare placeSquare = solution.getPlaceSquareList().get(counter);
-                draw(canvas, placeSquare.getX(), placeSquare.getY(), placeSquare.getLength(), placeSquare.getWidth());
-                counter++;
-            } catch (Exception e) {
-                Alert alert = new Alert(Alert.AlertType.WARNING);
-                alert.setContentText("已经没有可以放置的矩形了!");
-                alert.showAndWait();
-            }
-        });
-        return nextButton;
-    }
-
-    private void plot(Solution solution) {
-        System.out.println(solution);
-    }
-
-    private void draw(Canvas canvas, double x, double y, double length, double width) {
-        GraphicsContext gc = canvas.getGraphicsContext2D();
-        // 边框
-        gc.setStroke(Color.BLACK);
-        gc.setLineWidth(2);
-        gc.strokeRect(x, y, length, width);
-        // 填充
-        gc.setFill(new Color(new Random().nextDouble(), new Random().nextDouble(), new Random().nextDouble(), new Random().nextDouble()));
-        gc.fillRect(x, y, length, width);
-    }
-
-}
-

+ 321 - 0
sd-framework/src/main/java/com/sd/framework/util/packing/GA.java

@@ -0,0 +1,321 @@
+package com.sd.framework.util.packing;
+
+import com.sd.framework.util.packing.entity.Instance;
+import com.sd.framework.util.packing.entity.Solution;
+import com.sd.framework.util.packing.model.Genome;
+import com.sd.framework.util.packing.model.Item;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Random;
+
+public class GA {
+
+    // 最大的迭代次数
+    private static final int maxGen = 10;
+    // 变异对换次数
+    private static final double variationExchangeCount = 1.00;
+    // 复制最优解的次数(选择种群的最优个体,然后复制几次,将最优个体复制多个,存到新的集合中)
+    private static final int cloneNumOfBestIndividual = 10;
+    // 基因突变概率
+    private static final double mutationRate = 0.60;
+    // 基因交叉概率
+    private static final double crossoverRate = 0.98;
+    // 是否可以旋转
+    private static final boolean isRotateEnable = true;
+    // 随机函数对象
+    private static final Random random = new Random();
+
+    // 边界的宽
+    private final double width;
+
+    // 边界的长
+    private final double length;
+
+    // 矩形数组
+    private final Item[] items;
+
+    // 种群数量信息
+    private int popSize = 500;
+
+    // 遗传的代数(第几代)
+    private int t = 0;
+
+    // 放置所有的种群基因信息
+    private List<Genome> population = new ArrayList<>();
+
+    // 新一代的种群基因信息
+    private List<Genome> newPopulation = new ArrayList<>();
+
+    // 最好的适应度的对应的染色体(基因序列、路径序列)
+    private Genome bestGenome;
+
+    // 各个个体的累积概率
+    private double[] probability;
+
+    // 矩形数量
+    private int itemNum;
+
+    /**
+     * 遗传算法的构造函数
+     *
+     * @param instance 实例对象
+     */
+    public GA(Instance instance) {
+        this.length = instance.getLength();
+        this.width = instance.getWidth();
+        this.items = Item.copy(instance.getItemList().toArray(new Item[0]));
+    }
+
+    public GA(double length, double width, Item[] items) {
+        this.length = length;
+        this.width = width;
+        this.items = items;
+    }
+
+    /**
+     * 遗传算法主函数
+     *
+     * @return 最佳装载结果对象Solution
+     */
+    public Solution solve() {
+        initVar();
+        while (t < maxGen) {
+            popSize = population.size();
+            // 基因进化
+            evolution();
+            // 更新种群
+            population = copyGenomeList(newPopulation);
+            t++;
+        }
+        return bestGenome.getSolution();
+    }
+
+    /**
+     * 进化函数,正常交叉变异
+     */
+    private void evolution() {
+        //更新累积概率和总适应度
+        updateProbabilityAndTotalFitness();
+        // 挑选某代种群中适应度最高的个体
+        selectBestGenomeAndJoinNext();
+        // 赌轮选择策略对剩下的scale-1个个体进行随机的交叉,变异
+        while (newPopulation.size() < population.size()) {
+            double r = random.nextDouble();
+            for (int j = 0; j < probability.length; j++) {
+                if (compareDouble(r, probability[j]) != 1) {
+                    newPopulation.add(population.get(j));
+                    break;
+                }
+            }
+        }
+        // 交叉
+        if (compareDouble(random.nextDouble(), crossoverRate) != 1) {
+            int r = random.nextInt(popSize);
+            int k = random.nextInt(popSize);
+            while (r == k) {
+                r = random.nextInt(popSize);
+            }
+            cross(k, r);
+        }
+        // 变异
+        for (int i = 0; i < population.size(); i++) {
+            if (compareDouble(random.nextDouble(), mutationRate) != 1) {
+                variation(i);
+            }
+        }
+    }
+
+    /**
+     * 对基因1和基因2进行单点映射交叉
+     *
+     * @param k1 基因1的索引
+     * @param k2 基因2的索引
+     */
+    private void cross(int k1, int k2) {
+        // 获取要交叉的两个基因
+        int[] genomeArray1 = newPopulation.get(k1).getGenomeArray();
+        int[] genomeArray2 = newPopulation.get(k2).getGenomeArray();
+        // 找到交叉位置
+        int crossIndex = random.nextInt(itemNum);
+        // 获取交叉片段
+        for (int i = 0; i <= crossIndex; i++) {
+            int temp = genomeArray1[i];
+            genomeArray1[i] = genomeArray2[i];
+            genomeArray2[i] = temp;
+        }
+        // 找到重复基因
+        HashSet<Integer> set = new HashSet<>();
+        // <index,value>
+        HashMap<Integer, Integer> repeatMap = new HashMap<>();
+        for (int i = 0; i < genomeArray1.length; i++) {
+            if (!set.add(genomeArray1[i])) {
+                repeatMap.put(i, genomeArray1[i]);
+            }
+        }
+        set.clear();
+        for (int i = 0; i < genomeArray2.length; i++) {
+            if (!set.add(genomeArray2[i])) {
+                for (int key : repeatMap.keySet()) {
+                    genomeArray1[key] = genomeArray2[i];
+                    genomeArray2[i] = repeatMap.get(key);
+                    repeatMap.remove(key);
+                    break;
+                }
+            }
+        }
+        // 交叉完毕,将基因放回个体,再将个体放回种群,并更新他们的适应值和路径长度
+        newPopulation.get(k1).setGenomeArray(genomeArray1);
+        newPopulation.get(k1).updateFitnessAndSolution();
+        newPopulation.get(k2).setGenomeArray(genomeArray2);
+        newPopulation.get(k2).updateFitnessAndSolution();
+    }
+
+    /**
+     * 变异(n次两两对换)
+     *
+     * @param k 要进行变异的基因索引
+     */
+    private void variation(int k) {
+        Genome genome = newPopulation.get(k);
+        int[] genomeArray = genome.getGenomeArray();
+        for (int i = 0; i < variationExchangeCount; i++) {
+            int r1 = random.nextInt(itemNum);
+            int r2 = random.nextInt(itemNum);
+            while (r1 == r2) {
+                r2 = random.nextInt(itemNum);
+            }
+            //交换
+            int temp = genomeArray[r1];
+            genomeArray[r1] = genomeArray[r2];
+            genomeArray[r2] = temp;
+        }
+        //将变异后的基因序列放回个体
+        genome.setGenomeArray(genomeArray);
+        // 更新基因的适应值和路程长度
+        genome.updateFitnessAndSolution();
+        //将变异后的个体放回种群
+        newPopulation.set(k, genome);
+    }
+
+    /**
+     * 初始化函数,进行一些初始化操作
+     */
+    private void initVar() {
+        this.itemNum = this.items.length;
+        this.population = new ArrayList<>();
+        //初始化种群信息
+        List<Integer> sequence = new ArrayList<>();
+        for (int i = 0; i < itemNum; i++) {
+            sequence.add(i);
+        }
+        for (int i = 0; i < popSize; i++) {
+            Collections.shuffle(sequence);
+            int[] initSequence = new int[itemNum];
+            for (int j = 0; j < sequence.size(); j++) {
+                initSequence[j] = sequence.get(j);
+            }
+            Genome genome = new Genome(width, length, items, isRotateEnable, initSequence.clone());
+            genome.updateFitnessAndSolution();
+            population.add(genome);
+        }
+        bestGenome = copyGenome(population.get(0));
+        for (int i = 1; i < popSize; i++) {
+            Genome genome = population.get(i);
+            if (bestGenome.getFitness() < genome.getFitness()) {
+                bestGenome = copyGenome(genome);
+            }
+        }
+        System.out.println("初始解为:" + bestGenome.getSolution().getRate());
+    }
+
+    /**
+     * 挑选种群中适应度最大的个体基因,并复制n份,直接加入下一代种群
+     */
+    private void selectBestGenomeAndJoinNext() {
+        newPopulation = new ArrayList<>();
+        Genome tempBest = population.get(0);
+        for (int i = 1; i < population.size(); i++) {
+            if (population.get(i).getFitness() > tempBest.getFitness()) {
+                tempBest = population.get(i);
+            }
+        }
+        if (compareDouble(tempBest.getFitness(), bestGenome.getFitness()) == 1) {
+            bestGenome = copyGenome(tempBest);
+            // 最佳迭代次数
+            System.out.println("当前代数: " + t + " : " + bestGenome.getSolution().getRate());
+        }
+        for (int i = 0; i < cloneNumOfBestIndividual; i++) {
+            newPopulation.add(copyGenome(tempBest));
+        }
+    }
+
+    /**
+     * 更新各个个体的累积概率和种群总适应度
+     */
+    private void updateProbabilityAndTotalFitness() {
+        probability = new double[population.size()];
+        // 种群总的适应度数值
+        double totalFitness = 0;
+        for (Genome genome : population) {
+            totalFitness += genome.getFitness();
+        }
+        double rate = 0.0;
+        for (int i = 0; i < population.size(); i++) {
+            rate += (population.get(i).getFitness() / totalFitness);
+            probability[i] = rate;
+        }
+    }
+
+    /**
+     * 根据传入的模板基因,进行拷贝,返回拷贝好的基因
+     *
+     * @param genome 模板基因
+     * @return 返回拷贝好的基因
+     */
+    private Genome copyGenome(Genome genome) {
+        Genome copy = new Genome(genome.getWidth(), genome.getLength(), genome.getItems(), genome.isRotateEnable(), genome.getGenomeArray().clone());
+        copy.setSolution(genome.getSolution());
+        copy.setFitness(genome.getFitness());
+        return copy;
+    }
+
+    /**
+     * 根据传入的模板基因集合,进行拷贝,返回拷贝好的基因集合
+     *
+     * @param genomeList 模板基因集合
+     * @return 返回拷贝好的基因集合
+     */
+    private List<Genome> copyGenomeList(List<Genome> genomeList) {
+        List<Genome> copyList = new ArrayList<>();
+        for (Genome genome : genomeList) {
+            copyList.add(copyGenome(genome));
+        }
+        return copyList;
+    }
+
+    /**
+     * 判断两个双精度浮点型变量的大小关系
+     *
+     * @param d1 双精度浮点型变量1
+     * @param d2 双精度浮点型变量2
+     * @return 返回0代表两个数相等,返回1代表前者大于后者,返回-1代表前者小于后者,
+     */
+    private int compareDouble(double d1, double d2) {
+        // 定义一个误差范围,如果两个数相差小于这个误差,则认为他们是相等的 1e-06 = 0.000001
+        double error = 1e-06;
+        if (Math.abs(d1 - d2) < error) {
+            return 0;
+        } else if (d1 < d2) {
+            return -1;
+        } else if (d1 > d2) {
+            return 1;
+        } else {
+            throw new RuntimeException("d1 = " + d1 + " , d2 = " + d2);
+        }
+    }
+
+}

+ 0 - 34
sd-framework/src/main/java/com/sd/framework/util/packing/ReadDataUtil.java

@@ -1,34 +0,0 @@
-package com.sd.framework.util.packing;
-
-import cn.hutool.core.util.IdUtil;
-import com.sd.framework.util.packing.entity.Instance;
-import com.sd.framework.util.packing.entity.Square;
-
-import java.io.BufferedReader;
-import java.io.FileReader;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-public class ReadDataUtil {
-    public static Instance getInstance(String path) throws IOException {
-        BufferedReader bufferedReader = new BufferedReader(new FileReader(path));
-        String input;
-        Instance instance = new Instance();
-        List<Square> squareList = new ArrayList<>();
-        boolean isFirstLine = true;
-        while ((input = bufferedReader.readLine()) != null) {
-            String[] split = input.split(" ");
-            if (isFirstLine) {
-                instance.setLength(Double.parseDouble(split[0]));
-                instance.setWidth(Double.parseDouble(split[1]));
-                instance.setRotateEnable(split[2].equals("1"));
-                isFirstLine = false;
-            } else {
-                squareList.add(new Square(IdUtil.getSnowflakeNextId(), Double.parseDouble(split[0]), Double.parseDouble(split[1])));
-            }
-        }
-        instance.setSquareList(squareList);
-        return instance;
-    }
-}

+ 19 - 10
sd-framework/src/main/java/com/sd/framework/util/packing/entity/Instance.java

@@ -1,24 +1,33 @@
 package com.sd.framework.util.packing.entity;
 
-import lombok.Data;
+import com.sd.framework.util.packing.model.Item;
+import lombok.Getter;
+import lombok.Setter;
 
 import java.util.List;
 
-@Data
+@Getter
+@Setter
 public class Instance {
+
     /**
-     * 长
+     * 边界的
      */
-    double length;
+    private double length;
+
     /**
-     * 宽
+     * 边界的
      */
-    double width;
+    private double width;
+
     /**
-     * 是否可以旋转
+     * 矩形列表
      */
-    private boolean isRotateEnable = true;
+    private List<Item> itemList;
 
-    private List<Square> squareList;
+    /**
+     * 是否允许矩形旋转
+     */
+    private boolean isRotateEnable;
 
-}
+}

+ 44 - 0
sd-framework/src/main/java/com/sd/framework/util/packing/entity/PlaceItem.java

@@ -0,0 +1,44 @@
+package com.sd.framework.util.packing.entity;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+public class PlaceItem {
+
+    /**
+     * id
+     */
+    private Long id;
+
+    /**
+     * x坐标
+     */
+    private double x;
+
+    /**
+     * y坐标
+     */
+    private double y;
+
+    /**
+     * 宽(考虑旋转后的)
+     */
+    private double width;
+
+    /**
+     * 长(考虑旋转后的)
+     */
+    private double length;
+
+    /**
+     * 是否旋转
+     */
+    private boolean isRotate;
+
+}

+ 0 - 28
sd-framework/src/main/java/com/sd/framework/util/packing/entity/PlacePoint.java

@@ -1,28 +0,0 @@
-package com.sd.framework.util.packing.entity;
-
-import lombok.Data;
-import lombok.NoArgsConstructor;
-
-@Data
-@NoArgsConstructor
-public class PlacePoint implements Comparable<PlacePoint> {
-    private double x, y, length;
-
-    public PlacePoint(double x, double y, double length) {
-        this.x = x;
-        this.y = y;
-        this.length = length;
-    }
-
-    @Override
-    public int compareTo(PlacePoint placePoint) {
-        // 优先往下排 然后优先往左排
-        int compare_y = Double.compare(y, placePoint.y);
-        if (compare_y != 0) {
-            return compare_y;
-        } else {
-            return Double.compare(x, placePoint.x);
-        }
-    }
-
-}

+ 0 - 13
sd-framework/src/main/java/com/sd/framework/util/packing/entity/PlaceSquare.java

@@ -1,13 +0,0 @@
-package com.sd.framework.util.packing.entity;
-
-import lombok.AllArgsConstructor;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-
-@Data
-@AllArgsConstructor
-@NoArgsConstructor
-public class PlaceSquare {
-    private Long id;
-    private double x, y, length, width;
-}

+ 25 - 6
sd-framework/src/main/java/com/sd/framework/util/packing/entity/Solution.java

@@ -1,17 +1,36 @@
 package com.sd.framework.util.packing.entity;
 
 import lombok.AllArgsConstructor;
-import lombok.Data;
+import lombok.Getter;
 import lombok.NoArgsConstructor;
+import lombok.Setter;
 
 import java.util.List;
 
-@Data
+@Getter
+@Setter
 @NoArgsConstructor
 @AllArgsConstructor
 public class Solution {
+
+    /**
+     * 已放置矩形
+     */
+    private List<PlaceItem> placeItemList;
+
+    /**
+     * 放置最大长度
+     */
+    private double maxLength;
+
+    /**
+     * 放置总面积
+     */
+    private double total;
+
+    /**
+     * 利用率
+     */
     private double rate;
-    private Instance instance;
-    private List<Square> squareList;
-    private List<PlaceSquare> placeSquareList;
-}
+
+}

+ 0 - 13
sd-framework/src/main/java/com/sd/framework/util/packing/entity/Square.java

@@ -1,13 +0,0 @@
-package com.sd.framework.util.packing.entity;
-
-import lombok.AllArgsConstructor;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-
-@Data
-@AllArgsConstructor
-@NoArgsConstructor
-public class Square {
-    private Long id;
-    private double length, width;
-}

+ 75 - 0
sd-framework/src/main/java/com/sd/framework/util/packing/model/Genome.java

@@ -0,0 +1,75 @@
+package com.sd.framework.util.packing.model;
+
+import com.sd.framework.util.packing.entity.Solution;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class Genome {
+
+    /**
+     * 边界的宽
+     */
+    private double width;
+
+    /**
+     * 边界的长
+     */
+    private double length;
+
+    /**
+     * 矩形数组
+     */
+    private Item[] items;
+
+    /**
+     * 是否可以旋转
+     */
+    private boolean isRotateEnable;
+
+    /**
+     * 基因序列
+     */
+    private int[] genomeArray;
+
+    /**
+     * 适应度函数值(在二维装箱问题中,其实就是装载利用率)
+     */
+    private double fitness;
+
+    /**
+     * 序列对应的装载结果
+     */
+    private Solution solution;
+
+    /**
+     * 基因对象的构造函数
+     *
+     * @param width          边界宽度
+     * @param length         边界高度
+     * @param items          矩形集合
+     * @param isRotateEnable 是否可以旋转
+     * @param genomeArray    基因序列
+     */
+    public Genome(double width, double length, Item[] items, boolean isRotateEnable, int[] genomeArray) {
+        this.width = width;
+        this.length = length;
+        this.items = items;
+        this.isRotateEnable = isRotateEnable;
+        this.genomeArray = genomeArray;
+    }
+
+    /**
+     * 获取适应值和路径长度
+     */
+    public void updateFitnessAndSolution() {
+        Item[] items = new Item[this.items.length];
+        for (int i = 0; i < genomeArray.length; i++) {
+            items[i] = this.items[genomeArray[i]];
+        }
+        solution = new SkyLinePacking(isRotateEnable, width, length, items).packing();
+        fitness = solution.getRate();
+    }
+
+}

+ 45 - 0
sd-framework/src/main/java/com/sd/framework/util/packing/model/Item.java

@@ -0,0 +1,45 @@
+package com.sd.framework.util.packing.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@AllArgsConstructor
+public class Item {
+
+    /**
+     * id
+     */
+    private Long id;
+
+    /**
+     * 长
+     */
+    private double length;
+
+    /**
+     * 宽
+     */
+    private double width;
+
+    /**
+     * 复制单个Item
+     */
+    public static Item copy(Item item) {
+        return new Item(item.id, item.length, item.width);
+    }
+
+    /**
+     * 复制Item数组
+     */
+    public static Item[] copy(Item[] items) {
+        Item[] newItems = new Item[items.length];
+        for (int i = 0; i < items.length; i++) {
+            newItems[i] = copy(items[i]);
+        }
+        return newItems;
+    }
+
+}

+ 40 - 0
sd-framework/src/main/java/com/sd/framework/util/packing/model/SkyLine.java

@@ -0,0 +1,40 @@
+package com.sd.framework.util.packing.model;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class SkyLine implements Comparable<SkyLine> {
+
+    /**
+     * 天际线左端点x坐标
+     */
+    private double x;
+
+    /**
+     * 天际线左端点y坐标
+     */
+    private double y;
+
+    /**
+     * 天际线长度
+     */
+    private double length;
+
+    public SkyLine(double x, double y, double length) {
+        this.x = x;
+        this.y = y;
+        this.length = length;
+    }
+
+    /**
+     * 天际线排序规则,y越小越优先,y一样时,x越小越优先
+     */
+    @Override
+    public int compareTo(SkyLine o) {
+        int c1 = Double.compare(y, o.y);
+        return c1 == 0 ? Double.compare(x, o.x) : c1;
+    }
+
+}

+ 361 - 0
sd-framework/src/main/java/com/sd/framework/util/packing/model/SkyLinePacking.java

@@ -0,0 +1,361 @@
+package com.sd.framework.util.packing.model;
+
+import com.sd.framework.util.packing.entity.PlaceItem;
+import com.sd.framework.util.packing.entity.Solution;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.PriorityBlockingQueue;
+
+public class SkyLinePacking {
+
+    /**
+     * 边界的宽
+     */
+    private final double width;
+
+    /**
+     * 边界的长
+     */
+    private final double length;
+
+    /**
+     * 矩形数组
+     */
+    private final Item[] items;
+
+    /**
+     * 是否可以旋转
+     */
+    private final boolean isRotateEnable;
+
+    /**
+     * 基于堆优化的天际线优先队列(PriorityBlockingQueue是线程安全的,底层基于最小二叉堆实现,具有高效动态排序能力)
+     */
+    private final PriorityBlockingQueue<SkyLine> skyLineQueue = new PriorityBlockingQueue<>();
+
+    /**
+     * 构造函数
+     *
+     * @param isRotateEnable 是否允许矩形旋转
+     * @param width          边界宽度
+     * @param length         边界长度
+     * @param items          矩形集合
+     */
+    public SkyLinePacking(boolean isRotateEnable, double width, double length, Item[] items) {
+        this.isRotateEnable = isRotateEnable;
+        this.width = width;
+        this.length = length;
+        this.items = items;
+    }
+
+    /**
+     * 天际线启发式装箱主函数
+     *
+     * @return 放置好的矩形列表
+     */
+    public Solution packing() {
+
+        // 用来存储已经放置的矩形
+        List<PlaceItem> placeItemList = new ArrayList<>();
+
+        // 用来记录已经放置矩形的总面积
+        double totalS = 0;
+
+        // 最大长度
+        double maxLength = 0;
+
+        // 获取初始天际线(边界底部)
+        skyLineQueue.add(new SkyLine(0, 0, width));
+
+        // 记录已经放置的矩形
+        boolean[] used = new boolean[items.length];
+
+        // 开始天际线启发式迭代
+        while (!skyLineQueue.isEmpty() && placeItemList.size() < items.length) {
+            // 获取当前最下最左的天际线(取出队首元素)
+            SkyLine skyLine = skyLineQueue.poll();
+            // 初始化hl和hr
+            double hl = length - skyLine.getY();
+            double hr = length - skyLine.getY();
+            // 提前跳出计数器(如果hl和hr都获取到了就可以提前跳出,节省时间)
+            int c = 0;
+            // 顺序遍历天际线队列,根据skyline和skyline队列获取hl和hr
+            for (SkyLine line : skyLineQueue) {
+                // 由于skyLine是队首元素,所以它的Y肯定最小,所以line.getY() - skyLine.getY()肯定都大于等于0
+                if (compareDouble(line.getX() + line.getLength(), skyLine.getX()) == 0) {
+                    // 尾头相连,是hl
+                    hl = line.getY() - skyLine.getY();
+                    c++;
+                } else if (compareDouble(line.getX(), skyLine.getX() + skyLine.getLength()) == 0) {
+                    // 头尾相连,是hr
+                    hr = line.getY() - skyLine.getY();
+                    c++;
+                }
+                // hl和hr都获取到了,就没必要继续遍历了,可以提前跳出节省时间
+                if (c == 2) {
+                    break;
+                }
+            }
+            // 记录最大评分矩形的索引
+            int maxItemIndex = -1;
+            // 记录最大评分的矩形是否旋转
+            boolean isRotate = false;
+            // 记录最大评分
+            int maxScore = -1;
+            // 遍历每一个矩形,选取评分最大的矩形进行放置
+            for (int i = 0; i < items.length; i++) {
+                // 矩形没有放置过,才进行接下来的流程
+                if (!used[i]) {
+                    // 不旋转的情况
+                    int score = score(items[i].getWidth(), items[i].getLength(), skyLine, hl, hr);
+                    if (score > maxScore) {
+                        // 更新最大评分
+                        maxScore = score;
+                        maxItemIndex = i;
+                        isRotate = false;
+                    }
+                    // 旋转的情况(矩形宽和高互换)
+                    if (isRotateEnable) {
+                        int rotateScore = score(items[i].getLength(), items[i].getWidth(), skyLine, hl, hr);
+                        if (rotateScore > maxScore) {
+                            // 更新最大评分
+                            maxScore = rotateScore;
+                            maxItemIndex = i;
+                            isRotate = true;
+                        }
+                    }
+                }
+            }
+            // 如果当前最大得分大于等于0,则说明有矩形可以放置,则按照规则对其进行放置
+            if (maxScore >= 0) {
+                PlaceItem placeItem;
+
+                // 左墙高于等于右墙
+                if (hl >= hr) {
+                    // 评分为2时,矩形靠天际线右边放,否则靠天际线左边放
+                    if (maxScore == 2) {
+                        placeItem = placeRight(items[maxItemIndex], skyLine, isRotate);
+                    } else {
+                        placeItem = placeLeft(items[maxItemIndex], skyLine, isRotate);
+                    }
+                }
+
+                // 左墙低于右墙
+                else {
+                    // 评分为4或0时,矩形靠天际线右边放,否则靠天际线左边放
+                    if (maxScore == 4 || maxScore == 0) {
+                        placeItem = placeRight(items[maxItemIndex], skyLine, isRotate);
+                    } else {
+                        placeItem = placeLeft(items[maxItemIndex], skyLine, isRotate);
+                    }
+                }
+
+                // 添加已经放置的矩形
+                placeItemList.add(placeItem);
+
+                // 根据索引将该矩形设置为已经放置的矩形
+                used[maxItemIndex] = true;
+
+                // 将该矩形面积追加到totalS中
+                totalS += (items[maxItemIndex].getWidth() * items[maxItemIndex].getLength());
+
+                double length = placeItem.getY() + placeItem.getLength();
+
+                maxLength = Math.max(maxLength, length);
+
+            } else {
+                // 如果当前天际线一个矩形都放不下,那就上移天际线,与其他天际线合并
+                combineSkylines(skyLine);
+            }
+        }
+        // 返回求解结果
+        return new Solution(placeItemList, maxLength, totalS, totalS / (width * length));
+    }
+
+
+    /**
+     * 将矩形靠左放
+     *
+     * @param item     要放置的矩形对象
+     * @param skyLine  矩形放置所在的天际线
+     * @param isRotate 矩形是否旋转
+     * @return 返回一个PlaceItem对象(放置好的矩形对象)
+     */
+    private PlaceItem placeLeft(Item item, SkyLine skyLine, boolean isRotate) {
+        // 生成PlaceItem对象
+        PlaceItem placeItem;
+        if (!isRotate) {
+            placeItem = new PlaceItem(item.getId(), skyLine.getX(), skyLine.getY(), item.getWidth(), item.getLength(), isRotate);
+        } else {
+            placeItem = new PlaceItem(item.getId(), skyLine.getX(), skyLine.getY(), item.getLength(), item.getWidth(), isRotate);
+        }
+        // 将新天际线加入队列
+        addSkyLineInQueue(skyLine.getX(), skyLine.getY() + placeItem.getLength(), placeItem.getWidth());
+        addSkyLineInQueue(skyLine.getX() + placeItem.getWidth(), skyLine.getY(), skyLine.getLength() - placeItem.getWidth());
+        // 返回PlaceItem对象
+        return placeItem;
+    }
+
+    /**
+     * 将矩形靠右放
+     *
+     * @param item     要放置的矩形对象
+     * @param skyLine  矩形放置所在的天际线
+     * @param isRotate 矩形是否旋转
+     * @return 返回一个PlaceItem对象(放置好的矩形对象)
+     */
+    private PlaceItem placeRight(Item item, SkyLine skyLine, boolean isRotate) {
+        // 生成PlaceItem对象
+        PlaceItem placeItem;
+        if (!isRotate) {
+            placeItem = new PlaceItem(item.getId(), skyLine.getX() + skyLine.getLength() - item.getWidth(), skyLine.getY(), item.getWidth(), item.getLength(), isRotate);
+        } else {
+            placeItem = new PlaceItem(item.getId(), skyLine.getX() + skyLine.getLength() - item.getLength(), skyLine.getY(), item.getLength(), item.getWidth(), isRotate);
+        }
+        // 将新天际线加入队列
+        addSkyLineInQueue(skyLine.getX(), skyLine.getY(), skyLine.getLength() - placeItem.getWidth());
+        addSkyLineInQueue(placeItem.getX(), skyLine.getY() + placeItem.getLength(), placeItem.getWidth());
+        // 返回PlaceItem对象
+        return placeItem;
+    }
+
+    /**
+     * 将指定属性的天际线加入天际线队列
+     *
+     * @param x   新天际线x坐标
+     * @param y   新天际线y坐标
+     * @param len 新天际线长度
+     */
+    private void addSkyLineInQueue(double x, double y, double len) {
+        // 新天际线长度大于0时,才加入
+        if (compareDouble(len, 0.0) == 1) {
+            skyLineQueue.add(new SkyLine(x, y, len));
+        }
+    }
+
+    /**
+     * 传入一个放置不下任意矩形的天际线,将其上移,与其他天际线进行合并
+     *
+     * @param skyLine 一个放置不下任意矩形的天际线
+     */
+    private void combineSkylines(SkyLine skyLine) {
+        boolean b = false;
+        for (SkyLine line : skyLineQueue) {
+            if (compareDouble(skyLine.getY(), line.getY()) != 1) {
+                // 头尾相连
+                if (compareDouble(skyLine.getX(), line.getX() + line.getLength()) == 0) {
+                    skyLineQueue.remove(line);
+                    b = true;
+                    skyLine.setX(line.getX());
+                    skyLine.setY(line.getY());
+                    skyLine.setLength(line.getLength() + skyLine.getLength());
+                    break;
+                }
+                // 尾头相连
+                if (compareDouble(skyLine.getX() + skyLine.getLength(), line.getX()) == 0) {
+                    skyLineQueue.remove(line);
+                    b = true;
+                    skyLine.setY(line.getY());
+                    skyLine.setLength(line.getLength() + skyLine.getLength());
+                    break;
+                }
+            }
+        }
+        // 如果有进行合并,才加入
+        if (b) {
+            // 将最后合并好的天际线加入天际线队列
+            skyLineQueue.add(skyLine);
+        }
+    }
+
+    /**
+     * 对矩形进行评分
+     *
+     * @param width   当前要放置的矩形的宽
+     * @param length  当前要放置的矩形的高
+     * @param skyLine 该天际线对象
+     * @param hl      该天际线的左墙
+     * @param hr      该天际线的右墙
+     * @return 矩形块的评分,如果评分为 -1 ,则说明该矩形不能放置在该天际线上
+     */
+    private int score(double width, double length, SkyLine skyLine, double hl, double hr) {
+        // 当前天际线长度小于当前矩形宽度,放不下
+        if (compareDouble(skyLine.getLength(), width) == -1) {
+            return -1;
+        }
+        // 如果超出上界,也不能放
+        if (compareDouble(skyLine.getY() + length, this.length) == 1) {
+            return -1;
+        }
+        int score = -1;
+        // 左边墙高于等于右边墙
+        if (hl >= hr) {
+            if (compareDouble(width, skyLine.getLength()) == 0 && compareDouble(length, hl) == 0) {
+                score = 7;
+            } else if (compareDouble(width, skyLine.getLength()) == 0 && compareDouble(length, hr) == 0) {
+                score = 6;
+            } else if (compareDouble(width, skyLine.getLength()) == 0 && compareDouble(length, hl) == 1) {
+                score = 5;
+            } else if (compareDouble(width, skyLine.getLength()) == -1 && compareDouble(length, hl) == 0) {
+                score = 4;
+            } else if (compareDouble(width, skyLine.getLength()) == 0 && compareDouble(length, hl) == -1 && compareDouble(length, hr) == 1) {
+                score = 3;
+            } else if (compareDouble(width, skyLine.getLength()) == -1 && compareDouble(length, hr) == 0) {
+                // 靠右
+                score = 2;
+            } else if (compareDouble(width, skyLine.getLength()) == 0 && compareDouble(length, hr) == -1) {
+                score = 1;
+            } else if (compareDouble(width, skyLine.getLength()) == -1 && compareDouble(length, hl) != 0) {
+                score = 0;
+            } else {
+                throw new RuntimeException("width = " + width + " , length = " + length + " , hl = " + hl + " , hr = " + hr + " , skyline = " + skyLine);
+            }
+        } else {
+            if (compareDouble(width, skyLine.getLength()) == 0 && compareDouble(length, hr) == 0) {
+                score = 7;
+            } else if (compareDouble(width, skyLine.getLength()) == 0 && compareDouble(length, hl) == 0) {
+                score = 6;
+            } else if (compareDouble(width, skyLine.getLength()) == 0 && compareDouble(length, hr) == 1) {
+                score = 5;
+            } else if (compareDouble(width, skyLine.getLength()) == -1 && compareDouble(length, hr) == 0) {
+                // 靠右
+                score = 4;
+            } else if (compareDouble(width, skyLine.getLength()) == 0 && compareDouble(length, hr) == -1 && compareDouble(length, hl) == 1) {
+                score = 3;
+            } else if (compareDouble(width, skyLine.getLength()) == -1 && compareDouble(length, hl) == 0) {
+                score = 2;
+            } else if (compareDouble(width, skyLine.getLength()) == 0 && compareDouble(length, hl) == -1) {
+                score = 1;
+            } else if (compareDouble(width, skyLine.getLength()) == -1 && compareDouble(length, hr) != 0) {
+                // 靠右
+                score = 0;
+            } else {
+                throw new RuntimeException("width = " + width + " , length = " + length + " , hl = " + hl + " , hr = " + hr + " , skyline = " + skyLine);
+            }
+        }
+        return score;
+    }
+
+    /**
+     * 判断两个双精度浮点型变量的大小关系
+     *
+     * @param d1 双精度浮点型变量1
+     * @param d2 双精度浮点型变量2
+     * @return 返回0代表两个数相等,返回1代表前者大于后者,返回-1代表前者小于后者,
+     */
+    private int compareDouble(double d1, double d2) {
+        // 定义一个误差范围,如果两个数相差小于这个误差,则认为他们是相等的 1e-06 = 0.000001
+        double error = 1e-06;
+        if (Math.abs(d1 - d2) < error) {
+            return 0;
+        } else if (d1 < d2) {
+            return -1;
+        } else if (d1 > d2) {
+            return 1;
+        } else {
+            throw new RuntimeException("d1 = " + d1 + " , d2 = " + d2);
+        }
+    }
+
+}

+ 0 - 50
sd-framework/src/main/java/com/sd/framework/util/packing/model/TabuMapTree.java

@@ -1,50 +0,0 @@
-package com.sd.framework.util.packing.model;
-
-import com.sd.framework.util.packing.entity.Square;
-import lombok.Data;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-@Data
-public class TabuMapTree {
-
-    Map<Long, TabuMapTree> sonTreeMap = new HashMap<>();
-
-    Square nodeSquare;
-
-    public void add(List<Square> squareList, int index) {
-        if (index >= squareList.size()) {
-            return;
-        }
-        if (nodeSquare == null) {
-            nodeSquare = squareList.get(index);
-            index++;
-        }
-        Square square = squareList.get(index);
-        Long id = square.getId();
-        if (sonTreeMap.containsKey(id)) {
-            sonTreeMap.get(id).add(squareList, index + 1);
-        } else {
-            TabuMapTree tabuMapTree = new TabuMapTree();
-            tabuMapTree.setNodeSquare(square);
-            sonTreeMap.put(id, tabuMapTree);
-            sonTreeMap.get(id).add(squareList, index + 1);
-        }
-    }
-
-    public boolean contains(List<Square> squareList, int index) {
-        if (index >= squareList.size()) {
-            return true;
-        }
-        Square square = squareList.get(index);
-        Long id = square.getId();
-        if (sonTreeMap.containsKey(id)) {
-            return sonTreeMap.get(id).contains(squareList, index + 1);
-        } else {
-            return false;
-        }
-    }
-
-}

+ 0 - 327
sd-framework/src/main/java/com/sd/framework/util/packing/model/TabuSearch.java

@@ -1,327 +0,0 @@
-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 = 20;
-    /**
-     * 每次搜索领域的个数(这个值不要太大,太大的话搜索效率会降低)
-     */
-    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();
-        //开始迭代,停止条件为达到指定迭代次数
-        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 + 1));
-        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;
-            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.getLength();
-                double w = square.getWidth();
-                if (isRotate > 0) {
-                    // 表示进行了旋转
-                    square.setLength(w);
-                    square.setWidth(l);
-                }
-                // 移除当前角点
-                placePointList.remove(i);
-                //新增已放置的square
-                placeSquareList.add(new PlaceSquare(square.getId(), placePoint.getX(), placePoint.getY(), square.getLength(), square.getWidth()));
-                // 新增两个可行角点
-                double surplus = placePoint.getLength() - square.getLength(); // 剩余长度
-                if (surplus > 0) {
-                    placePointList.add(new PlacePoint(placePoint.getX() + square.getLength(), placePoint.getY(), surplus));
-                }
-                placePointList.add(new PlacePoint(placePoint.getX(), placePoint.getY() + square.getWidth(), square.getLength()));
-                // 重新排序
-                Collections.sort(placePointList);
-                i = 0;
-                // 还原矩形
-                if (isRotate > 0) {
-                    // 表示进行了旋转
-                    square.setLength(l);
-                    square.setWidth(w);
-                }
-            }
-        }
-        // 设置已经放置的矩形列表
-        solution.setPlaceSquareList(new ArrayList<>(placeSquareList));
-        // 计算利用率
-        double rate;
-        double s = 0.0f;
-        for (PlaceSquare placeSquare : placeSquareList) {
-            s += (placeSquare.getLength() * placeSquare.getWidth());
-        }
-        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.getLength(), square.getWidth());
-        if (isOverlap(placeSquareList, placeSquare)) {
-            mark1 = -1.0d;
-        } else {
-            delta = Math.abs(placePoint.getLength() - square.getLength());
-            mark1 = 1 - delta / placePoint.getLength();
-        }
-        mark2 = -1.0d;
-        if (instance.isRotateEnable()) {
-            placeSquare = new PlaceSquare(square.getId(), placePoint.getX(), placePoint.getY(), square.getWidth(), square.getLength());
-            if (!isOverlap(placeSquareList, placeSquare)) {
-                delta = Math.abs(placePoint.getLength() - square.getWidth());
-                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.getLength() > length || tempPlaceSquare.getWidth() > width) {
-            return true;
-        }
-        // 出界
-        if (tempPlaceSquare.getX() + tempPlaceSquare.getLength() > length || tempPlaceSquare.getY() + tempPlaceSquare.getWidth() > 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.getLength(), tempPlaceSquare.getX() + tempPlaceSquare.getLength());
-        double y2 = Math.min(placeSquare.getY() + placeSquare.getWidth(), tempPlaceSquare.getY() + tempPlaceSquare.getWidth());
-        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;
-        }
-    }
-
-}