【运筹优化】结合天际线启发式的蚁群算法求解二维矩形装箱问题 + Java代码实现

news2024/11/26 12:43:48

文章目录

  • 一、天际线启发式
  • 二、蚁群算法结合天际线启发式
    • 2.1 构建序列
      • 2.1.1 思路一
      • 2.1.2 思路二
      • 2.1.3 思路N
  • 三、Java代码实现
    • 3.1 项目结构
    • 3.2 Ant
    • 3.3 ACO
    • 3.4 Run
    • 3.5 运行结果展示
      • 3.5.1 思路一
      • 3.5.2 思路二
      • 3.5.3 思路N
  • 四、小结


一、天际线启发式

关于天际线启发式的介绍请看我的另一篇博客:
【运筹优化】基于堆优化的天际线启发式算法和复杂的评分策略求解二维矩形装箱问题 + Java代码实现


二、蚁群算法结合天际线启发式

蚁群算法结合天际线启发式其实很简单,只需要将序列对应成一个矩形集合,传给天际线启发式算法进行求解即可。
也就是说,天际线启发式其实就是这类启发式算法的解码器(评价函数)。

2.1 构建序列

让我们回顾一下蚁群算法求解TSP问题是如何处理的:在构建路径时, 蚂蚊通过当前信息素浓度, 按照一定概率选择下一个要到达的城市, 设 i \mathrm{i} i j \mathrm{j} j 分别为起点和终点, τ i j ( t ) \tau_{i j}(t) τij(t) 为时间 t \mathrm{t} t 时, 从站点 i \mathrm{i} i 到站点 j \mathrm{j} j 的信息素浓度, A k A^k Ak 为第 k \mathrm{k} k 只蚂蚊的尚末访问的邻接节点的集合, p k i j p^k{ }_{i j} pkij 代表第 k \mathrm{k} k 只蚂蚁从站点 i \mathrm{i} i 行驶到站 点 j \mathrm{j} j 的概率, η i j \eta_{i j} ηij 表示 i \mathrm{i} i j \mathrm{j} j 两点间距离的倒数, 综上, 概率的计算规则可以表达如下:
p k i j = { τ i j ( t ) η i j ∑ S ∈ A k τ i j ( t ) η i j , j ∈ A k 0 ,  其他  p^k{ }_{i j}=\left\{\begin{array}{l} \frac{\tau_{i j}(t) \eta_{i j}}{\sum_{S \in A^k} \tau_{i j}(t) \eta_{i j}}, j \in A^k \\ 0, \text { 其他 } \end{array}\right. pkij={SAkτij(t)ηijτij(t)ηij,jAk0, 其他 

构建路径的过程,其实就是构建序列的过程,但是有一个问题,在二维矩形装箱问题中,城市被换成了一个个矩形,城市之间存在距离,但是两个矩形之间没有距离呀!这可怎么办?没有距离的话 η i j \eta_{i j} ηij怎么求呢?对此我们有两种解决思路。

2.1.1 思路一

思路1:直接舍弃掉 η i j \eta_{i j} ηij这个参数,相当于让蚂蚁丧失基于“距离”的启发式信息

这么做的好处是,我们不用更改太多代码,只需要将概率的计算中的距离倒数删去,或者设置为一个常数即可。

但这么做的坏处是,丧失了基于“距离”的启发式信息,蚂蚁们前期的行走可能会较为漫无目的(这里是指迭代前期,后期信息素积累,信息素启发式信息占主导时会好一点)

emm转念一想,蚂蚁们在前期漫无目的一点也挺好呀!模拟退火的思想不就是如此嘛,前期搜索随机性大,后期慢慢稳定。

为了方便大家理解,直接亮Java代码:

修改前

        // 计算分母部分
        for (Integer i : allowedItems) {
            sum += Math.pow(pheromone[currentSquare][i], alpha)
                    * Math.pow(1.0 / 距离矩阵[currentSquare][i], beta);
        }
        // 计算概率矩阵
        for (int i : allowedItems) {
            p[i] = (Math.pow(pheromone[currentSquare][i], alpha) * Math
                    .pow(1.0 / 距离矩阵[currentSquare][i], beta)) / sum;
        }

修改后

        // 计算分母部分
        for (Integer i : allowedItems) {
            sum += Math.pow(pheromone[currentSquare][i], alpha)
                    * Math.pow(1.0, beta);
        }
        // 计算概率矩阵
        for (int i : allowedItems) {
            p[i] = (Math.pow(pheromone[currentSquare][i], alpha) * Math
                    .pow(1.0, beta)) / sum;
        }

有一些变量不知道什么含义没关系,后面会放完整代码,里面有详细注释。主要是观察修改前后的区别,相当于把距离矩阵[currentSquare][i]这一项看为了常数1

2.1.2 思路二

思路2:我们可以用一种指标来衡量两个矩形之间的“距离”

我想到的是利用两个矩形的不同度来衡量两个矩形间的距离,两个矩形越不同,则他们的距离越远,两个矩形越相似,则他们的距离越近(似不似听起来有点道理!)

然而。。问题又来了,两个矩形的不同度怎么计算?

这里就仁者见仁了,我采取的方法比较取巧

首先定义边不同度,其实就是两条边的差值再除以两条边的平均长度,差值代表了两个矩形的相差度量,除以平均值是为了消除量纲。
两条边的不同度 = ∣ 边 1 − 边 2 ∣ ➗ [ ( 边 1 + 边 2 ) / 2 ] 两条边的不同度 = |边1-边2|➗[(边1+边2)/2] 两条边的不同度=12∣➗[(1+2)/2]
然后再定义两个矩形的不同度为他们之间最小的边不同度

ok,搞定!真的搞定了吗!不对,差点忘了,蚁群算法构造路径时可是取的两个城市之间距离的倒数呀!如果两个矩形的不同度为0,再求倒数不就报错了吗!

所以这里还要做一点处理,对上面方法算出来的不同度和一个大于0的极小值做max操作,使得不同度不会等于0:

两矩形的不同度 = m a x ( 0.0001 , 两矩形不同度 ) 两矩形的不同度=max(0.0001,两矩形不同度) 两矩形的不同度=max(0.0001,两矩形不同度)

(我这里取了0.0001,大家也可以取其他值,不要太大,会影响原本的不同度,也不要太小,不然受浮点数精度影响可能还是认为这个数是0)

为了方便大家理解,直接亮Java代码,其中 isRotateEnable 代表矩形是否可以旋转,如果可以旋转,那就要继续用a的宽对b的高做一次边不同度计算,再a的高对b的宽做一次不同度计算。

    /**
     * @param a 矩形a
     * @param b 矩形b
     * @return 矩形a和b的不同度
     * @Description 计算矩形a对b的不同度
     */
    public double getDifferent(Item a, Item b) {
        double avgW = (a.getW() + b.getW()) / 2.0;
        double avgH = (a.getH() + b.getH()) / 2.0;
        double different = Math.abs(a.getH() - b.getH()) / avgH;
        different = Math.min(Math.abs(a.getW() - b.getW()) / avgW, different);
        if (isRotateEnable) {
            different = Math.min(Math.abs(a.getW() - b.getH()) / avgH, different);
            different = Math.min(Math.abs(a.getH() - b.getW()) / avgW, different);
        }
        return Math.max(0.0001, different);
    }

2.1.3 思路N

思路N交给你们啦,关键是怎么衡量两个矩形之间的关系,可以使得蚂蚁在前期的序列构建中不那么“随机”,评论区就交给大家自由发挥啦~~~


三、Java代码实现

3.1 项目结构

红框以外的代码文件在上面说的博客里有,本博客只给 Run 、 Ant和ACO 的代码

在这里插入图片描述

3.2 Ant

/**
 * @Author:WSKH
 * @ClassName:Ant
 * @ClassType:蚂蚁类
 * @Description:
 * @Date:2022/11/06/15:04
 * @Email:1187560563@qq.com
 * @Blog:https://blog.csdn.net/weixin_51545953?type=blog
 */
public class Ant {
    // 矩形集合
    private Item[] items;
    // 已经放置的矩形的索引
    private List<Integer> sequence;
    // 还没放置的矩形索引
    private List<Integer> allowedItems;
    // 信息素变化矩阵
    private double[][] delta;
    // 矩形不同度矩阵
    private double[][] different;
    // 信息素重要程度
    private double alpha;
    // 启发式因子重要程度
    private double beta;
    // 矩形数量
    private int itemNum;
    // 第一个放置的矩形
    private int firstSquare;
    // 当前放置的矩形
    private int currentSquare;
    // 随机数对象
    private Random random;

    // 外矩形的长宽
    double W, H;
    // 是否允许旋转
    private boolean isRotateEnable;

    Solution localSolution;

    //构造函数
    public Ant(boolean isRotateEnable, double W, double H, Item[] items, Long seed) {
        this.itemNum = items.length;
        this.items = items;
        this.H = H;
        this.W = W;
        this.isRotateEnable = isRotateEnable;
        this.random = seed == null ? new Random() : new Random(seed);
    }

    //初始化
    public void initAnt(double[][] different, double a, double b) {
        alpha = a;
        beta = b;
        this.different = different;
        // 初始允许搜索的矩形集合
        allowedItems = new ArrayList<>();
        // 初始禁忌表
        sequence = new ArrayList<>();
        // 初始信息数变化矩阵为0
        delta = new double[itemNum][itemNum];
        // 设置起始矩形(随机选取第一个矩形)
        firstSquare = random.nextInt(itemNum);

        for (int i = 0; i < itemNum; i++) {
            if (i != firstSquare) {
                allowedItems.add(i);
            }
        }
        // 将第一个放置的矩形添加至禁忌表
        sequence.add(firstSquare);
        // 第一个矩形即为当前放置的矩形
        currentSquare = firstSquare;
    }

    //选择下一个矩形
    public void selectNextSquare(double[][] pheromone) {
        double[] p = new double[itemNum];
        double sum = 0d;

        // --------------- 思路1:直接将距离看为一个常数1 --------------------
//        // 计算分母部分
//        for (Integer i : allowedItems) {
//            sum += Math.pow(pheromone[currentSquare][i], alpha)
//                    * Math.pow(1.0, beta);
//        }
//        // 计算概率矩阵
//        for (int i : allowedItems) {
//            p[i] = (Math.pow(pheromone[currentSquare][i], alpha) * Math
//                    .pow(1.0, beta)) / sum;
//        }

        // --------------- 思路2:采用矩形的不同度代替距离 --------------------
        // 计算分母部分
        for (Integer i : allowedItems) {
            sum += Math.pow(pheromone[currentSquare][i], alpha)
                    * Math.pow(1.0 / different[currentSquare][i], beta);
        }
        // 计算概率矩阵
        for (int i : allowedItems) {
            p[i] = (Math.pow(pheromone[currentSquare][i], alpha) * Math
                    .pow(1.0 / different[currentSquare][i], beta)) / sum;
        }



        // 轮盘赌选择下一个矩形
        double sleectP = random.nextDouble();
        int selectSquare = -1;
        double sum1 = 0d;
        for (int i = 0; i < itemNum; i++) {
            sum1 += p[i];
            if (compareDouble(sum1, sleectP) != -1) {
                selectSquare = i;
                break;
            }
        }
        // 从允许选择的矩形中去除select 矩形
        for (Integer i : allowedItems) {
            if (i == selectSquare) {
                allowedItems.remove(i);
                break;
            }
        }
        // 在禁忌表中添加select矩形
        sequence.add(selectSquare);
        currentSquare = selectSquare;
    }

    // 根据顺序进行装箱,并返回装载的矩形总面积
    public void evaluate() {
        // 根据顺序进行装箱
        Item[] items = new Item[this.items.length];
        for (int i = 0; i < sequence.size(); i++) {
            items[i] = this.items[sequence.get(i)];
        }
        localSolution = new SkyLinePacking(isRotateEnable, W, H, items).packing();
    }

    /**
     * @param d1 双精度浮点型变量1
     * @param d2 双精度浮点型变量2
     * @return 返回0代表两个数相等,返回1代表前者大于后者,返回-1代表前者小于后者,
     * @Description 判断两个双精度浮点型变量的大小关系
     */
    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);
        }
    }

    public Item[] getItems() {
        return items;
    }

    public void setItems(Item[] items) {
        this.items = items;
    }

    public List<Integer> getSequence() {
        return sequence;
    }

    public void setSequence(List<Integer> sequence) {
        this.sequence = sequence;
    }

    public List<Integer> getAllowedItems() {
        return allowedItems;
    }

    public void setAllowedItems(List<Integer> allowedItems) {
        this.allowedItems = allowedItems;
    }

    public double[][] getDelta() {
        return delta;
    }

    public void setDelta(double[][] delta) {
        this.delta = delta;
    }

    public double[][] getDifferent() {
        return different;
    }

    public void setDifferent(double[][] different) {
        this.different = different;
    }

    public double getAlpha() {
        return alpha;
    }

    public void setAlpha(double alpha) {
        this.alpha = alpha;
    }

    public double getBeta() {
        return beta;
    }

    public void setBeta(double beta) {
        this.beta = beta;
    }

    public int getItemNum() {
        return itemNum;
    }

    public void setItemNum(int itemNum) {
        this.itemNum = itemNum;
    }

    public int getFirstSquare() {
        return firstSquare;
    }

    public void setFirstSquare(int firstSquare) {
        this.firstSquare = firstSquare;
    }

    public int getCurrentSquare() {
        return currentSquare;
    }

    public void setCurrentSquare(int currentSquare) {
        this.currentSquare = currentSquare;
    }

    public Random getRandom() {
        return random;
    }

    public void setRandom(Random random) {
        this.random = random;
    }

    public double getW() {
        return W;
    }

    public void setW(double w) {
        W = w;
    }

    public double getH() {
        return H;
    }

    public void setH(double h) {
        H = h;
    }

    public boolean isRotateEnable() {
        return isRotateEnable;
    }

    public void setRotateEnable(boolean rotateEnable) {
        isRotateEnable = rotateEnable;
    }

    public Solution getLocalSolution() {
        return localSolution;
    }

    public void setLocalSolution(Solution localSolution) {
        this.localSolution = localSolution;
    }
}

3.3 ACO

/**
 * @Author:WSKH
 * @ClassName:ACO
 * @ClassType:
 * @Description:蚁群算法结合天际线算法求解二维矩形装箱问题
 * @Date:2022/11/7/11:32
 * @Email:1187560563@qq.com
 * @Blog:https://blog.csdn.net/weixin_51545953?type=blog
 */
public class ACO {

    // 蚂蚁数组
    public Ant[] ants;
    // 蚂蚁数量
    public int antNum;
    // 矩形数量
    public int itemNum;
    // 最大迭代数
    public int MAX_GEN;
    // 信息素矩阵
    public double[][] pheromone;
    // 最佳放置序列
    public List<Integer> bestSquence;
    // 最佳迭代数
    public int bestT;
    // 最优解
    public Solution bestSolution;
    // 不同度矩形
    double[][] different;

    // 三个参数
    // 信息素重要程度
    private double alpha;
    // 启发式因子重要程度
    private double beta;
    // 信息素挥发速率
    private double rho;

    // 边界的宽
    private double W;
    // 边界的高
    private double H;
    // 矩形数组
    Item[] items;
    // 是否可以旋转
    private boolean isRotateEnable;
    // 随机数种子
    Long seed;

    /**
     * @param antNum   蚂蚁数量
     * @param MAX_GEN  迭代次数(提高这个值可以稳定地提高解质量,但是会增加求解时间)
     * @param alpha    信息素重要程度
     * @param beta     启发式因子重要程度
     * @param rho      信息素挥发速率
     * @param instance 实例对象
     * @param seed     随机数种子,如果传入null则不设置随机数种子,否则按照传入的种子进行设置,方便复现结果
     * @Description 构造函数
     */
    public ACO(int antNum, int MAX_GEN, double alpha, double beta, double rho, Instance instance, Long seed) {
        this.antNum = antNum;
        this.ants = new Ant[antNum];
        this.alpha = alpha;
        this.beta = beta;
        this.rho = rho;
        this.MAX_GEN = MAX_GEN;
        this.W = instance.getW();
        this.H = instance.getH();
        this.isRotateEnable = instance.isRotateEnable();
        this.items = Item.copy(instance.getItemList().toArray(new Item[0]));
        this.itemNum = this.items.length;
        this.seed = seed;
    }

    /**
     * @return 最佳装载结果对象Solution
     * @Description 蚁群算法主函数
     */
    public Solution solve() {
        // 进行初始化操作
        init();
        // 迭代MAX_GEN次
        for (int g = 0; g < MAX_GEN; g++) {
            // antNum只蚂蚁
            for (int i = 0; i < antNum; i++) {
                // i这只蚂蚁走itemNum步,构成一个完整的矩形放置顺序
                for (int j = 1; j < itemNum; j++) {
                    ants[i].selectNextSquare(pheromone);
                }
                // 查看这只蚂蚁装载利用率是否比当前最优解优秀
                ants[i].evaluate();
                if (bestSolution == null || compareDouble(ants[i].getLocalSolution().getRate(), bestSolution.getRate()) == 1) {
                    // 比当前优秀则拷贝优秀的放置顺序
                    bestSquence = new ArrayList<>(ants[i].getSequence());
                    bestT = g;
                    bestSolution = ants[i].getLocalSolution();
                    System.out.println("蚂蚁 " + (i + 1) + " 找到更优解 , 当前迭代次数为: " + g + " , 利用率为:" + bestSolution.getRate());
                }
                // 更新这只蚂蚁的信息数变化矩阵,对称矩阵
                for (int j = 0; j < itemNum; j++) {
                    ants[i].getDelta()[ants[i].getSequence().get(j)][ants[i]
                            .getSequence().get(j + 1 >= itemNum ? 0 : j + 1)] = (1.0 / ants[i]
                            .getLocalSolution().getRate());
                    ants[i].getDelta()[ants[i].getSequence().get(j + 1 >= itemNum ? 0 : j + 1)][ants[i]
                            .getSequence().get(j)] = (1.0 / ants[i]
                            .getLocalSolution().getRate());
                }
            }
            // 更新信息素
            updatePheromone();
            // 重新初始化蚂蚁
            for (int i = 0; i < antNum; i++) {
                ants[i].initAnt(different, alpha, beta);
            }
        }
        // 返回结果
        return bestSolution;
    }

    /**
     * @Description 初始化操作
     */
    private void init() {
        //初始化不同度矩阵
        different = new double[itemNum][itemNum];
        for (int i = 0; i < itemNum; i++) {
            for (int j = 0; j < itemNum; j++) {
                if (i == j) {
                    different[i][j] = 0.0;
                } else {
                    different[i][j] = getDifferent(items[i], items[j]);
                }
            }
        }
        //初始化信息素矩阵
        pheromone = new double[itemNum][itemNum];
        for (int i = 0; i < itemNum; i++) {
            for (int j = 0; j < itemNum; j++) {
                // 初始化为0.1
                pheromone[i][j] = 0.1;
            }
        }
        // 放置蚂蚁
        for (int i = 0; i < antNum; i++) {
            ants[i] = new Ant(isRotateEnable, W, H, items, seed);
            ants[i].initAnt(different, alpha, beta);
        }
    }

    /**
     * @Description 更新信息素
     */
    private void updatePheromone() {
        // 信息素挥发
        for (int i = 0; i < itemNum; i++) {
            for (int j = 0; j < itemNum; j++) {
                pheromone[i][j] = pheromone[i][j] * (1 - rho);
            }
        }
        // 信息素更新
        for (int i = 0; i < itemNum; i++) {
            for (int j = 0; j < itemNum; j++) {
                for (int k = 0; k < antNum; k++) {
                    pheromone[i][j] += ants[k].getDelta()[i][j];
                }
            }
        }
    }

    /**
     * @param a 矩形a
     * @param b 矩形b
     * @return 矩形a和b的不同度
     * @Description 计算矩形a对b的不同度
     */
    public double getDifferent(Item a, Item b) {
        double avgW = (a.getW() + b.getW()) / 2.0;
        double avgH = (a.getH() + b.getH()) / 2.0;
        double different = Math.abs(a.getH() - b.getH()) / avgH;
        different = Math.min(Math.abs(a.getW() - b.getW()) / avgW, different);
        if (isRotateEnable) {
            different = Math.min(Math.abs(a.getW() - b.getH()) / avgH, different);
            different = Math.min(Math.abs(a.getH() - b.getW()) / avgW, different);
        }
        return Math.max(0.0001, different);
    }

    /**
     * @param d1 双精度浮点型变量1
     * @param d2 双精度浮点型变量2
     * @return 返回0代表两个数相等,返回1代表前者大于后者,返回-1代表前者小于后者,
     * @Description 判断两个双精度浮点型变量的大小关系
     */
    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);
        }
    }

}

3.4 Run

/**
 * @Author:WSKH
 * @ClassName:Run
 * @ClassType:
 * @Description:运行程序的主类
 * @Date:2022/11/6/19:39
 * @Email:1187560563@qq.com
 * @Blog:https://blog.csdn.net/weixin_51545953?type=blog
 */
public class Run extends javafx.application.Application {

    private int counter = 0;

    @Override
    public void start(Stage primaryStage) throws Exception {

        // 数据地址
        String path = "src/main/java/com/wskh/data/data.txt";
        // 根据txt文件获取实例对象
        Instance instance = new ReadDataUtil().getInstance(path);
        // 记录算法开始时间
        long startTime = System.currentTimeMillis();
        // 实例化蚁群算法对象
        ACO aco = new ACO(30, 300, 0.99, 5, 0.5, instance, null);
        // 调用蚁群算法对象进行求解
        Solution solution = aco.solve();
        // 输出相关信息
        System.out.println("------------------------------------------------------------------------------------");
        System.out.println("求解用时:" + (System.currentTimeMillis() - startTime) / 1000.0 + " s");
        System.out.println("共放置了矩形" + solution.getPlaceItemList().size() + "个");
        System.out.println("最佳利用率为:" + solution.getRate());
        // 输出画图数据
        String[] strings1 = new String[solution.getPlaceItemList().size()];
        String[] strings2 = new String[solution.getPlaceItemList().size()];
        for (int i = 0; i < solution.getPlaceItemList().size(); i++) {
            PlaceItem placeItem = solution.getPlaceItemList().get(i);
            strings1[i] = "{x:" + placeItem.getX() + ",y:" + placeItem.getY() + ",l:" + placeItem.getH() + ",w:" + placeItem.getW() + "}";
            strings2[i] = placeItem.isRotate() ? "1" : "0";
        }
        System.out.println("data:" + Arrays.toString(strings1) + ",");
        System.out.println("isRotate:" + Arrays.toString(strings2) + ",");

        // --------------------------------- 后面这些都是画图相关的代码,可以不用管 ---------------------------------------------
        AnchorPane pane = new AnchorPane();
        Canvas canvas = new Canvas(instance.getW(), instance.getH());
        pane.getChildren().add(canvas);
        canvas.relocate(100, 100);
        // 绘制最外层的矩形
        canvas = draw(canvas, 0, 0, instance.getW(), instance.getH(), true);
        // 添加按钮
        Button nextButton = new Button("Next +1");
        Canvas finalCanvas = canvas;
        nextButton.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent actionEvent) {
                try {
                    PlaceItem placeItem = solution.getPlaceItemList().get(counter);
                    draw(finalCanvas, placeItem.getX(), placeItem.getY(), placeItem.getW(), placeItem.getH(), false);
                    counter++;
                } catch (Exception e) {
                    Alert alert = new Alert(Alert.AlertType.WARNING);
                    alert.setContentText("已经没有可以放置的矩形了!");
                    alert.showAndWait();
                }
            }
        });
        //
        pane.getChildren().add(nextButton);
        primaryStage.setTitle("二维矩形装箱可视化");
        primaryStage.setScene(new Scene(pane, 1000, 1000, Color.AQUA));
        primaryStage.show();
    }

    private Canvas draw(Canvas canvas, double x, double y, double l, double w, boolean isBound) {
        GraphicsContext gc = canvas.getGraphicsContext2D();
        // 边框
        gc.setStroke(Color.BLACK);
        gc.setLineWidth(2);
        gc.strokeRect(x, y, l, w);
        // 填充
        if (!isBound) {
            gc.setFill(new Color(new Random().nextDouble(), new Random().nextDouble(), new Random().nextDouble(), new Random().nextDouble()));
        } else {
            gc.setFill(new Color(1, 1, 1, 1));
        }
        gc.fillRect(x, y, l, w);
        return canvas;
    }

    public static void main(String[] args) {
        launch(args);
    }

}

3.5 运行结果展示

3.5.1 思路一

输出(最后两行是在前端画图用的,可以忽略):

蚂蚁 1 找到更优解 , 当前迭代次数为: 0 , 利用率为:0.94475625
蚂蚁 2 找到更优解 , 当前迭代次数为: 0 , 利用率为:0.9608875
蚂蚁 6 找到更优解 , 当前迭代次数为: 0 , 利用率为:0.96431875
蚂蚁 10 找到更优解 , 当前迭代次数为: 0 , 利用率为:0.97451875
蚂蚁 7 找到更优解 , 当前迭代次数为: 1 , 利用率为:0.9752125
蚂蚁 27 找到更优解 , 当前迭代次数为: 1 , 利用率为:0.97885625
蚂蚁 30 找到更优解 , 当前迭代次数为: 8 , 利用率为:0.980275
蚂蚁 8 找到更优解 , 当前迭代次数为: 9 , 利用率为:0.98029375
蚂蚁 16 找到更优解 , 当前迭代次数为: 10 , 利用率为:0.98356875
蚂蚁 27 找到更优解 , 当前迭代次数为: 16 , 利用率为:0.99161875
------------------------------------------------------------------------------------
求解用时:7.436 s
共放置了矩形24个
最佳利用率为:0.99161875
data:[{x:0.0,y:0.0,l:116.0,w:99.0}, {x:99.0,y:0.0,l:116.0,w:113.0}, {x:212.0,y:0.0,l:116.0,w:111.0}, {x:323.0,y:0.0,l:116.0,w:20.0}, {x:343.0,y:0.0,l:89.0,w:57.0}, {x:343.0,y:89.0,l:82.0,w:57.0}, {x:0.0,y:116.0,l:88.0,w:99.0}, {x:99.0,y:116.0,l:88.0,w:113.0}, {x:212.0,y:116.0,l:79.0,w:111.0}, {x:323.0,y:116.0,l:58.0,w:20.0}, {x:343.0,y:171.0,l:95.0,w:57.0}, {x:226.0,y:195.0,l:71.0,w:117.0}, {x:0.0,y:204.0,l:79.0,w:99.0}, {x:99.0,y:204.0,l:79.0,w:118.0}, {x:217.0,y:266.0,l:40.0,w:117.0}, {x:334.0,y:266.0,l:82.0,w:66.0}, {x:0.0,y:283.0,l:117.0,w:80.0}, {x:80.0,y:283.0,l:117.0,w:107.0}, {x:187.0,y:283.0,l:117.0,w:29.0}, {x:216.0,y:306.0,l:94.0,w:44.0}, {x:260.0,y:306.0,l:94.0,w:31.0}, {x:291.0,y:306.0,l:94.0,w:39.0}, {x:330.0,y:348.0,l:52.0,w:47.0}, {x:377.0,y:348.0,l:50.0,w:23.0}],
isRotate:[0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1],

可视化展示(利用率 99.16%)

在这里插入图片描述

3.5.2 思路二

输出(最后两行是在前端画图用的,可以忽略):

蚂蚁 1 找到更优解 , 当前迭代次数为: 0 , 利用率为:0.95544375
蚂蚁 2 找到更优解 , 当前迭代次数为: 0 , 利用率为:0.95601875
蚂蚁 6 找到更优解 , 当前迭代次数为: 0 , 利用率为:0.96580625
蚂蚁 13 找到更优解 , 当前迭代次数为: 0 , 利用率为:0.97780625
蚂蚁 12 找到更优解 , 当前迭代次数为: 1 , 利用率为:0.98049375
蚂蚁 20 找到更优解 , 当前迭代次数为: 13 , 利用率为:0.9811375
蚂蚁 18 找到更优解 , 当前迭代次数为: 21 , 利用率为:0.9843125
蚂蚁 1 找到更优解 , 当前迭代次数为: 37 , 利用率为:0.986
蚂蚁 13 找到更优解 , 当前迭代次数为: 63 , 利用率为:0.99160625
蚂蚁 28 找到更优解 , 当前迭代次数为: 244 , 利用率为:0.9920625
------------------------------------------------------------------------------------
求解用时:14.288 s
共放置了矩形22个
最佳利用率为:0.9920625
data:[{x:0.0,y:0.0,l:100.0,w:113.0}, {x:113.0,y:0.0,l:100.0,w:108.0}, {x:292.0,y:0.0,l:54.0,w:108.0}, {x:221.0,y:0.0,l:117.0,w:71.0}, {x:292.0,y:54.0,l:63.0,w:41.0}, {x:333.0,y:54.0,l:76.0,w:67.0}, {x:0.0,y:100.0,l:116.0,w:113.0}, {x:113.0,y:100.0,l:116.0,w:99.0}, {x:212.0,y:117.0,l:117.0,w:80.0}, {x:292.0,y:117.0,l:117.0,w:29.0}, {x:321.0,y:130.0,l:118.0,w:79.0}, {x:0.0,y:216.0,l:85.0,w:113.0}, {x:113.0,y:216.0,l:88.0,w:99.0}, {x:212.0,y:234.0,l:70.0,w:76.0}, {x:288.0,y:234.0,l:114.0,w:33.0}, {x:321.0,y:248.0,l:111.0,w:79.0}, {x:0.0,y:301.0,l:99.0,w:79.0}, {x:79.0,y:301.0,l:99.0,w:26.0}, {x:105.0,y:304.0,l:96.0,w:98.0}, {x:203.0,y:304.0,l:96.0,w:84.0}, {x:287.0,y:348.0,l:47.0,w:34.0}, {x:332.0,y:359.0,l:37.0,w:68.0}],
isRotate:[0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],

可视化展示(利用率 99.21%)

在这里插入图片描述

3.5.3 思路N

等待你们上榜哈哈哈~


四、小结

上面的测试结果我只跑了一遍,并不能说明思路二就比思路一好,就算跑了很多遍思路二普遍比思路一利用率高,也不能绝对说明思路二就更好,还可能是由于这个测试数据的某些特性导致思路二更好,所以,启发式嘛,最重要的就是要多调参多测试啦~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/4277.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

推荐算法---矩阵分解

矩阵分解报告 1. 试验介绍 矩阵分解就是把原来的大矩阵&#xff0c;近似的分解成小矩阵的乘积&#xff0c;在实际推荐计算时不再使用大矩阵&#xff0c;而是使用分解得到的两个小矩阵。具体来说就是&#xff0c;假设用户物品的评分矩阵R是m乘n维&#xff0c;即一共有m个用户&…

Redis资料整理

Redis--->是非关系型数据库(也称缓存数据库),是一种NoSQL数据库 存放5种数据类型 String key-value形式 另外list,set,zset,hash 另外两种不常用的:bitmap(位图类型),geo(地理位置类型),另外Redis5.0新增 stream类型 相对来说Redis适合存放少数据量的数据,如果需要存放…

BERT知识蒸馏TinyBERT

1. 概述 诸如BERT等预训练模型的提出显著的提升了自然语言处理任务的效果&#xff0c;但是随着模型的越来越复杂&#xff0c;同样带来了很多的问题&#xff0c;如参数过多&#xff0c;模型过大&#xff0c;推理事件过长&#xff0c;计算资源需求大等。近年来&#xff0c;通过模…

PX4基本配置

目录 下载固件 下载原生稳定版固件 安装PX4 Master, Beta或自定义固件 FMUv2 Bootloader 更新 机架设置 飞行控制器/传感器方向 计算朝向 设置朝向 罗盘校准 执行校准 陀螺仪校准 # 执行校准 加速度计 执行校准 空速计校准 执行校准 水平平面校准 执行校准 …

Spring Cloud Zookeeper 升级为Spring Cloud Kubernetes

这里是weihubeats,觉得文章不错可以关注公众号小奏技术&#xff0c;文章首发。拒绝营销号&#xff0c;拒绝标题党 背景 现有的微服务是使用的Spring Cloud Zookeeper这一套&#xff0c;实际应用在Kubernetes中部署并不需要额外的注册中心&#xff0c;本身Kubernetes自己就支持…

10道不得不会的 Java容器 面试题

博主介绍&#xff1a; &#x1f680;自媒体 JavaPub 独立维护人&#xff0c;全网粉丝15w&#xff0c;csdn博客专家、java领域优质创作者&#xff0c;51ctoTOP10博主&#xff0c;知乎/掘金/华为云/阿里云/InfoQ等平台优质作者、专注于 Java、Go 技术领域和副业。&#x1f680; 最…

FFmpeg内存IO模式

ffmpeg 支持从网络流 或者本地文件读取数据&#xff0c;然后拿去丢给解码器解码&#xff0c;但是有一种特殊情况&#xff0c;就是数据不是从网络来的&#xff0c;也不在本地文件里面&#xff0c;而是在某块内存里面的。 这时候 av_read_frame() 函数怎样才能从内存把 AVPacket…

TensorFlow的GPU使用相关设置整理

前言 TensorFlow是一个在机器学习和深度学习领域被广泛使用的开源软件库&#xff0c;用于各种感知和语言理解任务的机器学习。 默认情况下&#xff0c;TensorFlow 会映射进程可见的所有 GPU&#xff08;取决于 CUDA_VISIBLE_DEVICES&#xff09;的几乎全部内存。这是为了减少内…

国考省考行测:问题型材料主旨分析,有问题有对策,主旨是对策,有问题无对策,要合理引申对策

国考省考行测&#xff1a;问题型材料主旨分析&#xff0c;有问题有对策&#xff0c;主旨是对策&#xff0c;有问题无对策&#xff0c;要合理引申对策 2022找工作是学历、能力和运气的超强结合体! 公务员特招重点就是专业技能&#xff0c;附带行测和申论&#xff0c;而常规国考…

【Linux】Linux背景、环境的搭建以及用XShell实现远程登陆

目录Linux 背景Linux环境搭建Linux远程登陆Linux 背景 肯尼斯蓝汤普森最早用汇编语言创建了UNIX系统&#xff0c;后来与他的好“基友”丹尼斯里奇&#xff08;C语言之父&#xff09;&#xff0c;他们两个一同用C语言重新写了UNIX系统&#xff0c;但是操作系统的使用是需要收费…

ActiveState Platform - November 2022

ActiveState Platform - November 2022 ActiveState平台定期更新新的、修补的和版本化的软件包和语言。 Python 3.10.7、3.9.14、3.8.14-解决了许多安全问题的点发布。 Python C库-ibxml 2.10.3、libxslt 1.1.37、libexpat 2.4.9、zlib 1.2.13、curl 7.85.0和sqlite3 3.39.4&am…

Python添加水印简简单单,三行代码教你批量添加

环境使用: Python 3.8Pycharm 如何配置pycharm里面的python解释器? 选择file(文件) >>> setting(设置) >>> Project(项目) >>> python interpreter(python解释器)点击齿轮, 选择add添加python安装路径 pycharm如何安装插件? 选择file(文件) …

使用Python PyQt5完成残缺棋盘覆盖仿真作业

摘要&#xff1a;本文内容是关于如何实现残缺棋盘覆盖仿真软件&#xff0c;算法课作业要求设计开发一个残缺棋盘覆盖仿真软件。使用”分治算法“求解问题&#xff0c;Python编程语言实现功能&#xff1b;使用PyQt5和Python热力图实现界面和仿真效果展示。 1 残缺棋盘覆盖仿真作…

[Linux打怪升级之路]-yun安装和gcc的使用

前言 作者&#xff1a;小蜗牛向前冲 名言&#xff1a;我可以接受失败&#xff0c;但我不能接受放弃 如果觉的博主的文章还不错的话&#xff0c;还请点赞&#xff0c;收藏&#xff0c;关注&#x1f440;支持博主。如果发现有问题的地方欢迎❀大家在评论区指正。 本期学习目标&am…

Java:外包Java项目有什么好处?

Java已经成为众多解决方案的通用开发语言&#xff0c;包括web应用、游戏、软件开发等等。超过710万全球的Java程序员都在忙着为业界下一个最好的应用程序编码。 随着企业努力在当今的全球市场中保持竞争力&#xff0c;对Java项目外包的需求不断增加。 以下是你的企业通过外包Ja…

python基于PHP+MySQL的论坛管理系统

互联网给了我们一个互通互信的途径,但是如何能够更加高效的进行各种问题的分享和交流是很多人关心的问题,市面上比较知名的一些分享交流平台也很多,比如百度的贴吧,知乎等高质量内容分享平台,本系统是一个类似这样的论坛分享系统 随着互联网的发展人们分享和交流的分享也变的越…

leetcode刷题(128)——1575. 统计所有可行路径,动态规划解法

leetcode刷题&#xff08;127&#xff09;——1575. 统计所有可行路径&#xff0c;DFS解法 给你一个 互不相同 的整数数组&#xff0c;其中 locations[i] 表示第 i 个城市的位置。同时给你 start&#xff0c;finish 和 fuel 分别表示出发城市、目的地城市和你初始拥有的汽油总…

【CSS】CSS字体样式【CSS基础知识详解】

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;花无缺 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 花无缺 原创 本文章收录于专栏 【CSS】 【CSS专栏】已发布文章 &#x1f4c1;【CSS基础认知】 &#x1f4c1;【CSS选择器全解指南】 本文目录【CS…

物联网感知-光纤光栅传感器技术

一、光纤光栅传感技术 光纤光栅是利用光纤材料的光敏性&#xff0c;通过紫外光曝光的方法将入射光相干场图样写入纤芯&#xff0c;将周期性微扰作用于光纤纤芯&#xff0c;在纤芯内产生沿纤芯轴向的折射率周期性变化&#xff0c;从而形成永久性空间的相位光栅&#xff0c;其作用…

MySQL数据库的基本操作及存储引擎的使用

大家好呀&#xff01;我是猿童学&#x1f435;&#xff0c;最近在学习Mysql数据库&#xff0c;给初学者分享一些知识&#xff0c;也是学习的总结&#xff0c;关注我将会不断地更新数据库知识&#xff0c;也欢迎大家指点一二&#x1f339;。 目录 一、常用的MySQL语句 二、创建…