【运筹优化】剩余空间法求解带顺序约束的二维矩形装箱问题 + Java代码实现

news2024/9/24 11:27:17

文章目录

  • 一、带顺序约束的二维矩形装箱问题
  • 二、剩余空间法
  • 三、完整代码实现
    • 3.1 Instance 实例类
    • 3.2 Item 物品类
    • 3.3 PlaceItem 已放置物品类
    • 3.4 Solution 结果类
    • 3.5 RSPackingWithWeight 剩余空间算法类
    • 3.6 Run 运行类
    • 3.7 测试案例
    • 3.8 ReadDataUtil 数据读取类
    • 3.9 运行结果展示


一、带顺序约束的二维矩形装箱问题

常规的二维矩形装箱问题只要求利用率尽可能大就可以了,但是在现实场景中,由于订单顺序等缘故,有一些物品需要优先于其他物品进行装载,这就诞生了本文要解决的“带顺序约束的二维矩形装箱问题”。

带顺序约束的二维矩形装箱问题给定每个物品一定的权重,要求按照权重从大到小的顺序进行装载。这个问题用天际线算法也能解决,但是效果很差,如下图所示:

在这里插入图片描述
所以就引出了本文的主角:剩余空间法。经过测试,剩余空间法的求解效果在带顺序约束的二维矩形装箱问题上可能优于天际线启发式算法。下面是剩余空间法排出的结果:

在这里插入图片描述

想了解天际线启发式的朋友可以参考:(【运筹优化】基于堆优化的天际线启发式算法和复杂的评分策略求解二维矩形装箱问题 + Java代码实现)


二、剩余空间法

剩余空间法思路很简单:每放入一个矩形,就把空间按照下图的方式切成两部分剩余空间。最开始,剩余空间就是整个容器。每次放置矩形找到最合适的剩余空间放就行了。
在这里插入图片描述

那么什么是最适合的剩余空间呢?这就涉及到评价规则了。
常见的评价规则有下面三种:

return itemW / remainingSpace.w; // 规则1:矩形宽度和剩余空间宽度越接近分越高
return itemH / remainingSpace.h; // 规则2:矩形高度和剩余空间高度越接近分越高
return (itemW*itemH) / (remainingSpace.w*remainingSpace.h); // 规则3:矩形面积和剩余空间面积越接近分越高

三、完整代码实现

3.1 Instance 实例类

public class Instance {

    // 边界的宽
    private double W;
    // 边界的高
    private double H;
    // 矩形列表
    private List<Item> itemList;
    // 是否允许矩形旋转
    private boolean isRotateEnable;

    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 List<Item> getItemList() {
        return itemList;
    }

    public void setItemList(List<Item> itemList) {
        this.itemList = itemList;
    }

    public boolean isRotateEnable() {
        return isRotateEnable;
    }

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

3.2 Item 物品类

public class Item {

    // 名字
    private String name;
    // 宽
    private double w;
    // 高
    private double h;

    // 权重
    private double weight;

    // 构造函数
    public Item(String name, double w, double h,double weight) {
        this.name = name;
        this.w = w;
        this.h = h;
        this.weight = weight;
    }

    // 复制单个Item
    public static Item copy(Item item) {
        return new Item(item.name, item.w, item.h,item.weight);
    }

    // 复制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;
    }

    // 复制Item列表
    public static List<Item> copy(List<Item> items) {
        List<Item> newItems = new ArrayList<>();
        for (Item item : items) {
            newItems.add(copy(item));
        }
        return newItems;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getW() {
        return w;
    }

    public void setW(double w) {
        this.w = w;
    }

    public double getH() {
        return h;
    }

    public void setH(double h) {
        this.h = h;
    }

    public double getWeight() {
        return weight;
    }

    public void setWeight(double weight) {
        this.weight = weight;
    }
}

3.3 PlaceItem 已放置物品类

public class PlaceItem {

    // 名字
    private String name;
    // x坐标
    private double x;
    // y坐标
    private double y;
    // 宽(考虑旋转后的)
    private double w;
    // 高(考虑旋转后的)
    private double h;
    // 是否旋转
    private boolean isRotate;
    // 权重
    private double weight;

    // 构造函数
    public PlaceItem(String name, double x, double y, double w, double h, boolean isRotate,double weight) {
        this.name = name;
        this.x = x;
        this.y = y;
        this.w = w;
        this.h = h;
        this.isRotate = isRotate;
        this.weight = weight;
    }

    @Override
    public String toString() {
        return "PlaceItem{" +
                "name='" + name + '\'' +
                ", x=" + x +
                ", y=" + y +
                ", w=" + w +
                ", h=" + h +
                ", isRotate=" + isRotate +
                ", weight=" + weight +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getX() {
        return x;
    }

    public void setX(double x) {
        this.x = x;
    }

    public double getY() {
        return y;
    }

    public void setY(double y) {
        this.y = y;
    }

    public double getW() {
        return w;
    }

    public void setW(double w) {
        this.w = w;
    }

    public double getH() {
        return h;
    }

    public void setH(double h) {
        this.h = h;
    }

    public boolean isRotate() {
        return isRotate;
    }

    public void setRotate(boolean rotate) {
        isRotate = rotate;
    }

    public double getWeight() {
        return weight;
    }

    public void setWeight(double weight) {
        this.weight = weight;
    }
}

3.4 Solution 结果类

public class Solution {

    // 已放置矩形
    private List<PlaceItem> placeItemList;
    // 放置总面积
    private double totalS;
    // 利用率
    private double rate;

    // 构造函数
    public Solution(List<PlaceItem> placeItemList, double totalS, double rate) {
        this.placeItemList = placeItemList;
        this.totalS = totalS;
        this.rate = rate;
    }

    public List<PlaceItem> getPlaceItemList() {
        return placeItemList;
    }

    public void setPlaceItemList(List<PlaceItem> placeItemList) {
        this.placeItemList = placeItemList;
    }

    public double getTotalS() {
        return totalS;
    }

    public void setTotalS(double totalS) {
        this.totalS = totalS;
    }

    public double getRate() {
        return rate;
    }

    public void setRate(double rate) {
        this.rate = rate;
    }
}

3.5 RSPackingWithWeight 剩余空间算法类

public class RSPackingWithWeight {

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

    /**
     * @param isRotateEnable 是否允许矩形旋转
     * @param W              边界宽度
     * @param H              边界高度
     * @param items          矩形集合
     * @Description 构造函数
     */
    public RSPackingWithWeight(boolean isRotateEnable, double W, double H, Item[] items) {
        this.isRotateEnable = isRotateEnable;
        this.W = W;
        this.H = H;
        this.items = Item.copy(items);
        // 按权重排序
        Arrays.sort(this.items, new Comparator<Item>() {
            @Override
            public int compare(Item o1, Item o2) {
                return -Double.compare(o1.getWeight(), o2.getWeight());
            }
        });
    }

    /**
     * @return 放置好的矩形列表
     * @Description 天际线启发式装箱主函数
     */
    public Solution packing() {
        // 用来存储已经放置的矩形
        List<PlaceItem> placeItemList = new ArrayList<>();
        // 用来记录已经放置矩形的总面积
        double totalS = 0d;
        // 剩余空间列表 [x,y,w,h]
        List<RemainingSpace> remainingSpaceList = new ArrayList<>();
        // 初始剩余空间就是整个容器
        remainingSpaceList.add(new RemainingSpace(0, 0, W, H));
        // 按照顺序放置矩形
        for (int i = 0; i < items.length; i++) {
            double maxScore = -1;
            int bestRemainingSpaceIndex = -1;
            boolean bestRotate = false;
            // 找到第一个没有被放置的权重最大的矩形i
            // 遍历所有剩余空间(不旋转)
            for (int j = 0; j < remainingSpaceList.size(); j++) {
                double score = score(items[i].getW(), items[i].getH(), remainingSpaceList.get(j));
                if (compareDouble(maxScore, score) == -1) {
                    maxScore = score;
                    bestRemainingSpaceIndex = j;
                }
            }
            // 遍历所有剩余空间(旋转)
            if (isRotateEnable) {
                for (int j = 0; j < remainingSpaceList.size(); j++) {
                    double score = score(items[i].getH(), items[i].getW(), remainingSpaceList.get(j));
                    if (compareDouble(maxScore, score) == -1) {
                        maxScore = score;
                        bestRemainingSpaceIndex = j;
                        bestRotate = true;
                    }
                }
            }
            // 装载
            if (bestRemainingSpaceIndex >= 0) {
                RemainingSpace remainingSpace = remainingSpaceList.remove(bestRemainingSpaceIndex);
                PlaceItem placeItem;
                if (bestRotate) {
                    // 旋转
                    placeItem = new PlaceItem(items[i].getName(), remainingSpace.x, remainingSpace.y, items[i].getH(), items[i].getW(), true, items[i].getWeight());
                } else {
                    // 不旋转
                    placeItem = new PlaceItem(items[i].getName(), remainingSpace.x, remainingSpace.y, items[i].getW(), items[i].getH(), false, items[i].getWeight());
                }
                placeItemList.add(placeItem);
                totalS += (placeItem.getW() * placeItem.getH());
                remainingSpaceList.add(new RemainingSpace(remainingSpace.x, remainingSpace.y + placeItem.getH(), placeItem.getW(), remainingSpace.h - placeItem.getH()));
                remainingSpaceList.add(new RemainingSpace(remainingSpace.x + placeItem.getW(), remainingSpace.y, remainingSpace.w - placeItem.getW(), remainingSpace.h));
            }
        }
        // 输出
        for (int i = 0; i < placeItemList.size(); i++) {
            System.out.println("第" + (i + 1) + "个矩形为: " + placeItemList.get(i));
        }
        // 返回求解结果
        return new Solution(placeItemList, totalS, totalS / (W * H));
    }

    // 评分函数:评价矩形放在剩余空间里的分数
    private double score(double itemW, double itemH, RemainingSpace remainingSpace) {
        if (compareDouble(remainingSpace.w, itemW) == -1 || compareDouble(remainingSpace.h, itemH) == -1) {
            // 超出剩余空间,返回-1分
            return -1;
        }
        // 评分规则
        return itemW / remainingSpace.w; // 规则1:矩形宽度和剩余空间宽度越接近分越高
//        return itemH / remainingSpace.h; // 规则2:矩形高度和剩余空间高度越接近分越高
//        return (itemW*itemH) / (remainingSpace.w*remainingSpace.h); // 规则3:矩形面积和剩余空间面积越接近分越高

    }

    /**
     * @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);
        }
    }

    static class RemainingSpace {
        double x, y, w, h;

        public RemainingSpace(double x, double y, double w, double h) {
            this.x = x;
            this.y = y;
            this.w = w;
            this.h = h;
        }
    }

}

3.6 Run 运行类

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_weight.txt";
        // 根据txt文件获取实例对象
        Instance instance = new ReadDataUtil().getInstance(path);
        // 记录算法开始时间
        long startTime = System.currentTimeMillis();
        // 实例化剩余空间法对象
        RSPackingWithWeight rsPackingWithWeight = new RSPackingWithWeight(instance.isRotateEnable(), instance.getW(), instance.getH(), instance.getItemList().toArray(new Item[0]));
        // 调用算法进行求解
        Solution solution = rsPackingWithWeight.packing();
        // 输出相关信息
        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.7 测试案例

270,28,0,
1,82.3,10.4,0.54
2,123.5,20.62,0.25
3,80.4,16.2,0.42
4,74,13.41,0.81
5,105.6,11.6,0.19
6,62.1,10.1,0.67
7,43.2,8,0.93
8,39.8,11.25,0.73
9,50,12,0.3
10,75,8.6,0.89
11,129.92,16.24,0.08
12,90.8,14.9,0.16

3.8 ReadDataUtil 数据读取类

public class ReadDataUtil {
    public Instance getInstance(String path) throws IOException {
        BufferedReader bufferedReader = new BufferedReader(new FileReader(path));
        String input = null;
        Instance instance = new Instance();
        List<Item> itemList = new ArrayList<>();
        boolean isFirstLine = true;
        while ((input = bufferedReader.readLine()) != null) {
            String[] split = input.split(",");
            if (isFirstLine) {
                instance.setW(Double.parseDouble(split[0]));
                instance.setH(Double.parseDouble(split[1]));
                instance.setRotateEnable("1".equals(split[2]));
                isFirstLine = false;
            } else {
                itemList.add(new Item(split[0], Double.parseDouble(split[1]), Double.parseDouble(split[2]), Double.parseDouble(split[3])));
            }
        }
        instance.setItemList(itemList);
        return instance;
    }
}

3.9 运行结果展示

在这里插入图片描述

1个矩形为: PlaceItem{name='7', x=0.0, y=0.0, w=43.2, h=8.0, isRotate=false, weight=0.93}2个矩形为: PlaceItem{name='10', x=43.2, y=0.0, w=75.0, h=8.6, isRotate=false, weight=0.89}3个矩形为: PlaceItem{name='4', x=43.2, y=8.6, w=74.0, h=13.41, isRotate=false, weight=0.81}4个矩形为: PlaceItem{name='8', x=0.0, y=8.0, w=39.8, h=11.25, isRotate=false, weight=0.73}5个矩形为: PlaceItem{name='6', x=118.2, y=0.0, w=62.1, h=10.1, isRotate=false, weight=0.67}6个矩形为: PlaceItem{name='1', x=180.3, y=0.0, w=82.3, h=10.4, isRotate=false, weight=0.54}7个矩形为: PlaceItem{name='3', x=180.3, y=10.4, w=80.4, h=16.2, isRotate=false, weight=0.42}8个矩形为: PlaceItem{name='9', x=118.2, y=10.1, w=50.0, h=12.0, isRotate=false, weight=0.3}
求解用时:0.002 s
共放置了矩形8个
利用率为:0.7693518518518518
data:[{x:0.0,y:0.0,l:8.0,w:43.2}, {x:43.2,y:0.0,l:8.6,w:75.0}, {x:43.2,y:8.6,l:13.41,w:74.0}, {x:0.0,y:8.0,l:11.25,w:39.8}, {x:118.2,y:0.0,l:10.1,w:62.1}, {x:180.3,y:0.0,l:10.4,w:82.3}, {x:180.3,y:10.4,l:16.2,w:80.4}, {x:118.2,y:10.1,l:12.0,w:50.0}],
isRotate:[0, 0, 0, 0, 0, 0, 0, 0],

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

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

相关文章

Spring boot + mybatis-plus 遇到 数据库字段 创建不规范 大驼峰 下划线 导致前端传参数 后端收不到参数 解决方案

最近使用springboot 连接了一个 sqlserver 数据库 由于数据库年数久远 &#xff0c;建表字段不规范 大驼峰 下划线的字段名都有 但是 java 中 Spring boot mybatis-plus 又严格按照小驼峰 格式 生成实体类 如果不是小驼峰格式 Data 注解 get set 方法 在前端请求参数 使用这个…

如何评估模糊测试工具-unibench的使用

unibench是一个用来评估模糊测试工具的benchmark。这个benchmark集成了20多个常用的测试程序&#xff0c;以及许多模糊测试工具。 这篇文章&#xff08;https://zhuanlan.zhihu.com/p/421124258&#xff09;对unibench进行了简单的介绍&#xff0c;本文就不再赘诉&#xff0c;…

设计模式-第6章(工厂模式)

工厂模式简单工厂实现工厂模式实现简单工厂 VS 工厂方法商场收银程序再再升级&#xff08;简单工厂策略装饰工厂方法&#xff09;工厂方法模式总结简单工厂实现 在简单工厂类中&#xff0c;通过不同的运算符&#xff0c;创建具体的运算类。 public class OperationFactory {pu…

CMMI流程规范—实现与测试

一、概述实现与测试&#xff08;Implementation and Test, IT&#xff09;的目的是依据系统设计文档&#xff0c;编写并测试整个系统的代码。在本规范中&#xff0c;实现与测试是“编程、代码审查、单元测试、集成测试、缺陷管理与改错”的综合表述。实现与测试过程域是SPP模型…

从 AI 绘画到 ChatGPT,聊聊生成式 AI

我们小时候经常有幻想&#xff0c;未来不用再去上班了&#xff0c;在工厂工作的都是机器人。在家也不用打扫卫生&#xff0c;机器人可以包揽一切。不知不觉间&#xff0c;我们小时候的幻想已经慢慢变成现实&#xff0c;工厂里有了多种型号的机械臂&#xff0c;代替了部分流水线…

Vue3中watch的value问题

目录前言一&#xff0c;ref和reactive的简单复习1.ref函数1.2 reactive函数1.3 用ref定义对象类型数据不用reactive二&#xff0c;watch的value问题2.1 ref2.1.1 普通类型数据2.1.2 对象类型数据2.1.3 另一种方式2.2 reactive三&#xff0c;总结后记前言 在Vue3中&#xff0c;…

论文投稿指南——中文核心期刊推荐(中国文学作品)

【前言】 &#x1f680; 想发论文怎么办&#xff1f;手把手教你论文如何投稿&#xff01;那么&#xff0c;首先要搞懂投稿目标——论文期刊 &#x1f384; 在期刊论文的分布中&#xff0c;存在一种普遍现象&#xff1a;即对于某一特定的学科或专业来说&#xff0c;少数期刊所含…

微信小程序通过 node 连接 mysql——方法,简要原理,及一些常见问题

前言 博主自己在22年夏天根据课程要求做了一个小程序连接阿里云服务器的案例&#xff0c;在最近又碰到了相应的需求。 原参考文章&#xff1a;微信小程序 Node连接本地MYSQL_微信小程序nodejs连接数据库_JJJenny0607的博客-CSDN博客 ,还请多多支持原作者&#xff01; 第二次…

vue2 @hook 的解析与妙用

目录前言几种用法用法一 将放在多个生命周期的逻辑&#xff0c;统一到一个生命周期中用法二 监听子组件生命周期运行的情况运用场景场景一 许多时候&#xff0c;我们不得不在不同的生命周期中执行某些逻辑&#xff0c;并且这些逻辑会用到一些通用的变量&#xff0c;这些通用变量…

nginx日志服务之敏感信息脱敏

1. 创建实验资源 开始实验之前&#xff0c;您需要先创建实验相关资源。 日志服务之敏感信息脱敏与审计 2. 创建原始数据 本步骤将指导您如何创建NGINX模拟数据。 双击打开虚拟桌面的Firefox ESR浏览器。 在RAM用户登录框中单击下一步&#xff0c;并复制粘贴页面左上角的子…

使用groovy代码方式解开gradle配置文件神秘面纱

来到这里的是不是都有以下疑问&#xff1a; 1.build.gradle配置文件结构好复杂啊&#xff0c;怎么记&#xff1f; 2.内部是怎么进行分析和执行的&#xff1f; 3.为什么可以在配置文件里面写groovy代码&#xff0c;怎么识别的&#xff1f; 4.怎么才能很方便的记住和快速上手…

空口协议Eapol、802.11 Action、802.11 BAR 和 802.11BA、802.11 Encrypted Data讲解

如下报文 可以看到,除了有之前开放认证的报文之外,还多了 EAPOL 次握手的报文。另外,还有其他几种类型的报文:802.11 Action、802.11 BAR 和 802.11BA、802.11 Encrypted Data ​ 密匙认证协议EAPOL: EAP是Extensible Authentication Protocol的缩写,EAPOL就是(EAP…

Python搭建一个steam钓鱼网站,只要免费领游戏,一钓一个准

前言 嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! 我们日常上网的时候&#xff0c;总是会碰到一些盗号的网站&#xff0c;或者是别人发一些链接给你&#xff0c; 里面的内容是一些可以免费购物网站的优惠券、游戏官网上可以免费领取皮肤、打折的游戏。 这些盗号网站统一的目…

go版本分布式锁redsync使用教程

redsync使用教程前言redsync结构Pool结构Mutex结构acquire加锁操作release解锁操作redsync包的使用前言 在编程语言中锁可以理解为一个变量&#xff0c;该变量在同一时刻只能有一个线程拥有&#xff0c;以便保护共享数据在同一时刻只有一个线程去操作。对于高可用的分布式锁应…

6招优化WordPress打开速度-让你的网站飞起来

为什么我们的WordPress网站比你的快&#xff1f; 我们的官网是使用WordPress框架搭建的&#xff0c;有没有发现我们的网站非常快&#xff0c;而你的WordPress网站比较慢呢&#xff1f;那是因为我们的网站经过了优化。 WordPress 很慢&#xff1f; 为什么很多人都会觉得 Word…

【C语言进阶:指针的进阶】函数指针

本章重点内容&#xff1a; 字符指针指针数组数组指针数组传参和指针传参函数指针函数指针数组指向函数指针数组的指针回调函数指针和数组面试题的解析⚡函数指针 函数指针&#xff1a;指向函数的指针。 通过之前的学习我们知道数组指针中存放的是数组的地址&#xff0c;那么函…

如何在网络安全中使用人工智能并避免受困于此

人工智能在网络安全中的应用正在迅速增长&#xff0c;并对威胁检测、事件响应、欺诈检测和漏洞管理产生了重大影响。根据Juniper Research的一份报告&#xff0c;预计到2023年&#xff0c;使用人工智能进行欺诈检测和预防将为企业每年节省110亿美元。但是&#xff0c;如何将人工…

Sqoop 使用详解

Sqoop 概述Sqoop 是Apache 旗下的一款开源工具&#xff0c;用于Hadoop与关系型数据库之间传送数据&#xff0c;其核心功能有两个&#xff1a;导入数据和导出数据。导入数据是指将MySQL、Oracle等关系型数据库导入Hadoop的HDFS、Hive、HBase等数据存储系统&#xff1b;导出数据是…

web学习-Node.js入门学习

web学习-Node.js入门学习1.回顾与思考2. 初识Node.js2.1 Node.js的简介2.2Node.js的环境安装2.3. fs文件系统模块2.3.1 fs.readFile()2.3.2 fs.writeFile()2.3.3 练习-整理考试成绩2.3.4 fs模块-路径动态拼接的问题2.4 path路径模块2.5 http模块2.5.1 服务器相关的概念2.5.2 创…

移动app安全测试工具好物分享

移动互联网时代&#xff0c;我们的生活和工作深受移动app的影响。随着移动app的广泛应用&#xff0c;安全问题成为人们最关注的话题之一。移动app安全除了和软件开发密不可分之外&#xff0c;软件测试的作用也是不容忽视的。移动app安全测试是指测试人员利用各种测试手段验证Ap…