【运筹优化】贪心启发式算法和蜘蛛猴优化算法求解连续选址问题 + Java代码实现

news2024/12/24 8:36:04

文章目录

  • 一、问题描述
  • 二、思路分析
  • 三、解决方案
    • 3.1 贪心启发式算法
    • 3.2 群体智能算法(蜘蛛猴优化算法)
  • 四、总结


一、问题描述

选址问题是指在规划区域里选择一个或多个设施的位置,使得目标最优。

按照规划区域的结构划分,可以将选址问题分为:
1.连续选址问题:设施可以在给定范围的任意位置选址,设施的候选位置为无穷多。
2.离散选址问题:设施的候选位置是有限且较少的,实际中最常遇到这类问题。
3.网格选址问题:规划区域被划分为许多的小单元,每个设施占据其中有限个单元。

本博客将根据下面的例题,介绍连续选址问题的求解思路
(其中,问题1为运输问题,建议先阅读我的另一篇博客再来看本博客:【运筹优化】运输问题建模 + Java调用Cplex求解)

在这里插入图片描述


二、思路分析

问题(2)属于连续选址问题,它的数学模型和问题(1)的数学模型 Q 1 Q_1 Q1类似,唯一不同之处在于,问题(2)中, d i j d_{ij} dij也是一个决策变量。因此,它的数学模型是非线性的,无法直接通过Cplex直接求解。对于凸的无约束非线性模型,可以使用梯度下降法或牛顿法求出其最优解;对于其他非线性模型,通常使用启发式算法或群体智能算法寻求其较优解。由于不确定问题(2)模型是否为凸,因此本人采用了一个贪心启发式算法和一个群体智能算法(蜘蛛猴优化算法)对问题(2)进行求解。


三、解决方案

3.1 贪心启发式算法

首先介绍贪心启发式算法,为了更好地描述,给出下面的线性规划模型:

在这里插入图片描述
该模型的含义为:给定一个已知位置的料场和若干个工地的信息,确保将料场的储量全部运输到各个工地,并使得总运输成本最小。

然后介绍贪心启发式算法的基本思想:每次为一个料场 i i i定位置,基于每个工地的坐标和当前剩余需求量计算一个加权中心点作为当前料场 i i i的坐标,在确定了料场 i i i的位置之后,就可以将料场 i i i和当前工地的信息代入到线性的 Q 2 i Q^i_2 Q2i模型中调用Cplex进行求解,得到料场 i i i的最小成本的运输方案后,再更新每个工地的剩余需求量,然后以同样的步骤为下一个料场确定位置并更新工地信息,直到每个工地的位置都被确定。确定了每个工地的位置之后,再求解 Q 1 Q_1 Q1模型,即可得到当前料场坐标下最优的运输方案。

贪心启发式的Java代码实现如下:

public class Answer2 {
    /**
     * 料场对象
     */
    @AllArgsConstructor
    static class Stockyard {
        /**
         * x,y坐标和储量c
         */
        double x, y, c;
    }

    /**
     * 工地对象
     */
    @AllArgsConstructor
    static class ConstructionSite {
        /**
         * x,y坐标和需求d
         */
        double x, y, d;
    }

    private static double calcDistance(double x1, double y1, double x2, double y2) {
        return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
    }

    private static List<ConstructionSite> getInitConstructionSiteList() {
        List<ConstructionSite> constructionSiteList = new ArrayList<>();
        constructionSiteList.add(new ConstructionSite(1.25, 1.25, 3));
        constructionSiteList.add(new ConstructionSite(8.75, 0.75, 5));
        constructionSiteList.add(new ConstructionSite(0.5, 4.75, 4));
        constructionSiteList.add(new ConstructionSite(5.75, 5, 7));
        constructionSiteList.add(new ConstructionSite(3, 6.5, 6));
        constructionSiteList.add(new ConstructionSite(7.25, 7.75, 11));
        return constructionSiteList;
    }

    public static void main(String[] args) throws Exception {
        // 浮点型精度误差
        double EPS = 1e-06;
        // 料场列表
        List<Stockyard> stockyardList = new ArrayList<>();
        stockyardList.add(new Stockyard(0, 0, 20));
        stockyardList.add(new Stockyard(0, 0, 20));
        stockyardList.sort(new Comparator<Stockyard>() {
            @Override
            public int compare(Stockyard o1, Stockyard o2) {
                return -Double.compare(o1.c, o2.c);
            }
        });
        // 工地列表
        List<ConstructionSite> constructionSiteList = getInitConstructionSiteList();
        // 初始化距离矩阵
        double[][] distanceMatrix = new double[stockyardList.size()][constructionSiteList.size()];
        // 开始循环
        for (int i = 0; i < stockyardList.size(); i++) {
            // 计算总需求量
            double totalDemand = constructionSiteList.stream().map(constructionSite -> {
                return constructionSite.d;
            }).reduce(0d, Double::sum);
            // 计算加权中心点作为料场i的坐标
            stockyardList.get(i).x = 0d;
            stockyardList.get(i).y = 0d;
            for (ConstructionSite constructionSite : constructionSiteList) {
                stockyardList.get(i).x += (constructionSite.x * constructionSite.d / totalDemand);
                stockyardList.get(i).y += (constructionSite.y * constructionSite.d / totalDemand);
            }
            // 计算距离矩阵
            for (int j = 0; j < distanceMatrix[i].length; j++) {
                distanceMatrix[i][j] = calcDistance(stockyardList.get(i).x, stockyardList.get(i).y, constructionSiteList.get(j).x, constructionSiteList.get(j).y);
            }
            if (totalDemand <= stockyardList.get(i).c) {
                break;
            }
            // 然后强制料场i必须送够c_i的量,并计算最小成本的运输方案
            // 开始建模
            IloCplex cplex = new IloCplex();
            // 声明变量
            IloNumVar[] x = new IloNumVar[constructionSiteList.size()];
            for (int j = 0; j < x.length; j++) {
                x[j] = cplex.numVar(0, constructionSiteList.get(j).d);
            }
            // 构造约束1:不能超出每个工地的需求
            for (int j = 0; j < constructionSiteList.size(); j++) {
                cplex.addLe(x[j], constructionSiteList.get(j).d);
            }
            // 构造约束2:料场中的储量必须全部送出
            cplex.addEq(cplex.sum(x), stockyardList.get(i).c);
            // 声明目标函数
            cplex.addMinimize(cplex.scalProd(x, distanceMatrix[i]));
            // 配置cplex
            cplex.setOut(null);
            cplex.setWarning(null);
            cplex.setParam(IloCplex.DoubleParam.EpOpt, EPS);
            // 开始求解
            if (cplex.solve()) {
                // 更新每个工地的需求量
                for (int j = 0; j < x.length; j++) {
                    constructionSiteList.get(j).d -= cplex.getValue(x[j]);
                }
            } else {
                System.err.println("此题无解");
            }
            // 结束模型
            cplex.end();
        }
        for (int i = 0; i < stockyardList.size(); i++) {
            System.out.println("料场" + (i + 1) + "的坐标为: ( " + stockyardList.get(i).x + " , " + stockyardList.get(i).y + " )");
        }
        // 重新获取初始化的工地列表
        constructionSiteList = getInitConstructionSiteList();
        // 开始建模
        IloCplex cplex = new IloCplex();
        // 声明变量
        IloNumVar[][] x = new IloNumVar[stockyardList.size()][constructionSiteList.size()];
        for (int i = 0; i < x.length; i++) {
            for (int j = 0; j < x[i].length; j++) {
                x[i][j] = cplex.numVar(0, Math.min(stockyardList.get(i).c, constructionSiteList.get(j).d));
            }
        }
        // 构造约束1:必须满足每个工地的需求
        for (int j = 0; j < constructionSiteList.size(); j++) {
            IloLinearNumExpr expr = cplex.linearNumExpr();
            for (int i = 0; i < x.length; i++) {
                expr.addTerm(1, x[i][j]);
            }
            cplex.addEq(expr, constructionSiteList.get(j).d);
        }
        // 构造约束2:不能超过每个料场的储量
        for (int i = 0; i < stockyardList.size(); i++) {
            cplex.addLe(cplex.sum(x[i]), stockyardList.get(i).c);
        }
        // 声明目标函数
        IloLinearNumExpr target = cplex.linearNumExpr();
        for (int i = 0; i < x.length; i++) {
            for (int j = 0; j < x[i].length; j++) {
                target.addTerm(distanceMatrix[i][j], x[i][j]);
            }
        }
        cplex.addMinimize(target);
        // 配置cplex
        cplex.setOut(null);
        cplex.setWarning(null);
        cplex.setParam(IloCplex.DoubleParam.EpOpt, EPS);
        // 开始求解
        long s = System.currentTimeMillis();
        if (cplex.solve()) {
            System.out.println("最小吨千米数为: " + cplex.getObjValue());
            for (int i = 0; i < stockyardList.size(); i++) {
                for (int j = 0; j < x[i].length; j++) {
                    double xValue = cplex.getValue(x[i][j]);
                    if (xValue > EPS) {
                        System.out.println("料场" + (i + 1) + "向工地" + (j + 1) + "运输" + xValue + "吨水泥");
                    }
                }
            }
            System.out.println("求解用时: " + (System.currentTimeMillis() - s) / 1000d + " s");
        } else {
            System.err.println("此题无解");
        }
        // 结束模型
        cplex.end();
    }
}

运行结果如下:

料场1的坐标为: ( 5.208333333333333 , 5.159722222222222 )
料场2的坐标为: ( 4.90625 , 3.59375 )
最小吨千米数为: 114.45936860248577
料场1向工地4运输7.0吨水泥
料场1向工地5运输2.0吨水泥
料场1向工地6运输11.0吨水泥
料场2向工地1运输3.0吨水泥
料场2向工地2运输5.0吨水泥
料场2向工地3运输4.0吨水泥
料场2向工地5运输4.0吨水泥
求解用时: 0.0 s

3.2 群体智能算法(蜘蛛猴优化算法)

然后是使用了蜘蛛猴优化算法。每个蜘蛛猴的变量数组表示了每个料场的坐标。设置蜘蛛猴数量为50,迭代次数为60,局部领导者决策阶段的更新几率为0.1,局部领导者阶段的更新几率为0.8,最大组数为5,料场的坐标上下界分别为10和-10。

Java代码实现如下:

public class SMO_Solver {

    /**
     * 料场对象
     */
    @AllArgsConstructor
    static class Stockyard {
        /**
         * x,y坐标和储量c
         */
        double x, y, c;
    }

    /**
     * 工地对象
     */
    @AllArgsConstructor
    static class ConstructionSite {
        /**
         * x,y坐标和需求d
         */
        double x, y, d;
    }

    private double calcDistance(double x1, double y1, double x2, double y2) {
        return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
    }

    private static List<ConstructionSite> getInitConstructionSiteList() {
        List<ConstructionSite> constructionSiteList = new ArrayList<>();
        constructionSiteList.add(new ConstructionSite(1.25, 1.25, 3));
        constructionSiteList.add(new ConstructionSite(8.75, 0.75, 5));
        constructionSiteList.add(new ConstructionSite(0.5, 4.75, 4));
        constructionSiteList.add(new ConstructionSite(5.75, 5, 7));
        constructionSiteList.add(new ConstructionSite(3, 6.5, 6));
        constructionSiteList.add(new ConstructionSite(7.25, 7.75, 11));
        return constructionSiteList;
    }

    List<ConstructionSite> constructionSiteList;
    List<Stockyard> stockyardList;
    double EPS = 1e-06;

    public SMO_Solver(List<Stockyard> stockyardList, List<ConstructionSite> constructionSiteList) throws IloException {
        this.constructionSiteList = constructionSiteList;
        this.stockyardList = stockyardList;
    }

    // 蜘蛛猴对象
    class SpiderMonkey {
        // 当前蜘蛛猴坐标(自变量数组)
        double[] curVars;
        // 当前自变量对应的目标函数值
        double curObjValue;
        // 适应度(解决最小化问题,所以适应度为目标函数值的倒数)
        double fit;

        // 全参数构造
        public SpiderMonkey(double[] curVars, double curObjValue, double fit) {
            this.curVars = curVars;
            this.curObjValue = curObjValue;
            this.fit = fit;
        }
    }

    // 算法参数
    // 蜘蛛猴群
    List<SpiderMonkey[]> spiderMonkeyList = new ArrayList<>();
    // 局部领导者
    List<SpiderMonkey> localLeaderList = new ArrayList<>();
    // 最好的蜘蛛猴(全局领导者)
    SpiderMonkey bestSpiderMonkey;
    // 随机数对象
    Random random = new Random();
    // 最大迭代次数
    int maxGen = 60;
    // 蜘蛛猴数量
    int spiderMonkeyNum = 50;
    // 局部搜索次数(一般等于蜘蛛猴数量)
    int localSearchCount = spiderMonkeyNum;
    // 局部领导者决策阶段的更新几率
    double LLDP_PR = 0.1;
    // 局部领导者阶段的更新几率
    double LLP_PR = 0.8;
    // 变量维度数
    int varNum = 4;
    // 最大组数(一般要至少保证每组里有10个蜘蛛猴)
    int maxgroupNum = spiderMonkeyNum / 10;
    // 变量的上下界
    double[] ub = new double[]{10, 10, 10, 10};
    double[] lb = new double[]{-10, -10, -10, -10};
    // 局部计数器
    int[] localLimitCount = new int[]{0};
    // 停止条件
    int limitCnt = 30;
    // 全局计数器
    int globalLimitCount;
    // 记录迭代过程
    public double[][][] positionArr;
    // 记录迭代器的行数
    int curC = 0;
    // 是否开启贪心机制(只接受比当前解好的解)
    boolean greedy = true;

    // 求解主函数
    public void solve() {
        // 初始化蜘蛛猴种群
        initSpiderMonkeys();
        // 开始迭代
        for (int t = 0; t < maxGen; t++) {
            System.out.println(t + " : " + bestSpiderMonkey.curObjValue);
            // 局部领导者阶段(LLP:所有的蜘蛛猴都有机会更新自己)
            LLP();
            // 全局领导者阶段(GLP:轮盘赌,随机选取,偏向于对fit值大的蜘蛛猴进行更新)
            GLP();
            // 全局领导者学习阶段(如果全局领导者有更新,则globalLimitCount=0,否则globalLimitCount++)
            GLLP();
            // 局部领导者学习阶段(如果局部领导者有更新,则localLimitCount=0,否则localLimitCount++)
            LLLP();
            // 局部领导者决策阶段
            LLDP();
            // 全局领导者决策阶段
            GLDP();
        }
        // 输出最好的结果
        System.out.println("变量取值为:" + Arrays.toString(bestSpiderMonkey.curVars));
        System.out.println("最优解为:" + bestSpiderMonkey.curObjValue);
    }

    // 全局领导者决策阶段
    private void GLDP() {
        if (globalLimitCount >= limitCnt) {
            globalLimitCount = 0;
            if (spiderMonkeyList.size() < maxgroupNum) {
                // 分裂
                List<SpiderMonkey> tempList = new ArrayList<>();
                for (SpiderMonkey[] spiderMonkeys : spiderMonkeyList) {
                    tempList.addAll(Arrays.asList(spiderMonkeys));
                }
                tempList.sort(new Comparator<SpiderMonkey>() {
                    @Override
                    public int compare(SpiderMonkey o1, SpiderMonkey o2) {
                        return Double.compare(o2.fit, o1.fit);
                    }
                });
                //
                int groupNum = spiderMonkeyList.size() + 1;
                spiderMonkeyList = new ArrayList<>();
                int avgNum = spiderMonkeyNum / groupNum;
                for (int i = 0; i < groupNum - 1; i++) {
                    SpiderMonkey[] spiderMonkeys = new SpiderMonkey[avgNum];
                    for (int j = 0; j < avgNum; j++) {
                        spiderMonkeys[j] = copySpiderMonkey(tempList.remove(0));
                    }
                    spiderMonkeyList.add(spiderMonkeys);
                }
                spiderMonkeyList.add(tempList.toArray(new SpiderMonkey[0]));
                localLimitCount = new int[groupNum];
            } else {
                // 融合
                SpiderMonkey[] spiderMonkeys = new SpiderMonkey[spiderMonkeyNum];
                int i = 0;
                for (SpiderMonkey[] monkeys : spiderMonkeyList) {
                    for (SpiderMonkey monkey : monkeys) {
                        spiderMonkeys[i++] = copySpiderMonkey(monkey);
                    }
                }
                spiderMonkeyList = new ArrayList<>();
                spiderMonkeyList.add(spiderMonkeys);
                localLimitCount = new int[]{0};
            }
            // 更新局部领导者
            localLeaderList = new ArrayList<>();
            for (SpiderMonkey[] spiderMonkeys : spiderMonkeyList) {
                localLeaderList.add(copySpiderMonkey(spiderMonkeys[0]));
                int index = localLeaderList.size() - 1;
                for (int i = 1; i < spiderMonkeys.length; i++) {
                    if (localLeaderList.get(index).fit < spiderMonkeys[i].fit) {
                        localLeaderList.set(index, copySpiderMonkey(spiderMonkeys[i]));
                    }
                }
            }
        }
    }

    // 局部领导者决策阶段
    private void LLDP() {
        int c = 0;
        for (int i = 0; i < spiderMonkeyList.size(); i++) {
            SpiderMonkey[] spiderMonkeys = spiderMonkeyList.get(i);
            if (localLimitCount[i] < limitCnt) {
                for (int j = 0; j < spiderMonkeys.length; j++) {
                    SpiderMonkey tempSpiderMonkey = copySpiderMonkey(spiderMonkeys[j]);
                    for (int m = 0; m < varNum; m++) {
                        if (random.nextDouble() <= LLDP_PR) {
                            tempSpiderMonkey.curVars[m] = lb[m] + random.nextDouble() * (ub[m] - lb[m]);
                        } else {
                            double moveDist = random.nextDouble() * (bestSpiderMonkey.curVars[m] - tempSpiderMonkey.curVars[m]) + random.nextDouble() * (spiderMonkeys[random.nextInt(spiderMonkeys.length)].curVars[m] - tempSpiderMonkey.curVars[m]);
                            moveSpiderMonkey(tempSpiderMonkey, m, moveDist);
                        }
                    }
                    tempSpiderMonkey.curObjValue = getObjValue(tempSpiderMonkey.curVars);
                    tempSpiderMonkey.fit = 1 / tempSpiderMonkey.curObjValue;
                    if (greedy) {
                        if (tempSpiderMonkey.fit > spiderMonkeys[j].fit) {
                            spiderMonkeys[j] = tempSpiderMonkey;
                        }
                    } else {
                        spiderMonkeys[j] = tempSpiderMonkey;
                    }
                }
            }
            for (int j = 0; j < spiderMonkeys.length; j++) {
                for (int m = 0; m < spiderMonkeys[j].curVars.length; m++) {
                    positionArr[curC][c][m] = spiderMonkeys[j].curVars[m];
                }
                c++;
            }
        }
        curC++;
    }

    // 局部领导者学习阶段(如果局部领导者有更新,则localLimitCount=0,否则localLimitCount++)
    private void LLLP() {
        for (int i = 0; i < spiderMonkeyList.size(); i++) {
            boolean isUpdate = false;
            for (SpiderMonkey spiderMonkey : spiderMonkeyList.get(i)) {
                if (localLeaderList.get(i).fit < spiderMonkey.fit) {
                    localLeaderList.set(i, copySpiderMonkey(spiderMonkey));
                    isUpdate = true;
                }
            }
            if (isUpdate) {
                localLimitCount[i] = 0;
            } else {
                localLimitCount[i]++;
            }
        }
    }

    // 全局领导者学习阶段(如果全局领导者有更新,则globalLimitCount=0,否则globalLimitCount++)
    private void GLLP() {
        boolean isUpdate = false;
        for (int i = 0; i < spiderMonkeyList.size(); i++) {
            for (SpiderMonkey spiderMonkey : spiderMonkeyList.get(i)) {
                if (spiderMonkey.fit > bestSpiderMonkey.fit) {
                    bestSpiderMonkey = copySpiderMonkey(spiderMonkey);
                    isUpdate = true;
                }
            }
        }
        if (isUpdate) {
            globalLimitCount = 0;
        } else {
            globalLimitCount++;
        }
    }

    // 全局领导者阶段(GLP:轮盘赌,随机选取,偏向于对fit值大的蜘蛛猴进行更新)
    private void GLP() {
        int c = 0;
        for (int i = 0; i < spiderMonkeyList.size(); i++) {
            SpiderMonkey[] spiderMonkeys = spiderMonkeyList.get(i);
            // 计算fit总和
            double totalFit = 0;
            for (SpiderMonkey spiderMonkey : spiderMonkeys) {
                totalFit += spiderMonkey.fit;
            }
            // 轮盘赌的累计概率数组
            double[] p = new double[spiderMonkeys.length];
            for (int j = 0; j < p.length; j++) {
                p[j] = (spiderMonkeys[j].fit / totalFit) + (j == 0 ? 0 : p[j - 1]);
            }
            // 局部搜索
            for (int j = 0; j < localSearchCount; j++) {
                double r = random.nextDouble();
                for (int k = 0; k < p.length; k++) {
                    if (r <= p[k]) {
                        for (int m = 0; m < varNum; m++) {
                            double moveDist = random.nextDouble() * (bestSpiderMonkey.curVars[m] - spiderMonkeys[k].curVars[m]) + (random.nextDouble() - 0.5) * 2 * (spiderMonkeys[random.nextInt(spiderMonkeys.length)].curVars[m] - spiderMonkeys[k].curVars[m]);
                            moveSpiderMonkey(spiderMonkeys[k], m, moveDist);
                        }
                        spiderMonkeys[k].curObjValue = getObjValue(spiderMonkeys[k].curVars);
                        spiderMonkeys[k].fit = 1 / spiderMonkeys[k].curObjValue;
                        break;
                    }
                }
            }
            for (int j = 0; j < spiderMonkeys.length; j++) {
                for (int m = 0; m < spiderMonkeys[j].curVars.length; m++) {
                    positionArr[curC][c][m] = spiderMonkeys[j].curVars[m];
                }
                c++;
            }
            spiderMonkeyList.set(i, spiderMonkeys);
        }
        curC++;
    }

    // 局部领导者阶段(LLP:所有的蜘蛛猴都有机会更新自己)
    private void LLP() {
        int c = 0;
        for (int i = 0; i < spiderMonkeyList.size(); i++) {
            SpiderMonkey[] spiderMonkeys = spiderMonkeyList.get(i);
            SpiderMonkey localLeader = localLeaderList.get(i);
            for (int j = 0; j < spiderMonkeys.length; j++) {
                // 以一定几率更新自己
                if (random.nextDouble() <= LLP_PR) {
                    SpiderMonkey tempSpiderMonkey = copySpiderMonkey(spiderMonkeys[j]);
                    for (int m = 0; m < varNum; m++) {
                        double moveDist = random.nextDouble() * (localLeader.curVars[m] - tempSpiderMonkey.curVars[m]) + (random.nextDouble() - 0.5) * 2 * (spiderMonkeys[random.nextInt(spiderMonkeys.length)].curVars[m] - tempSpiderMonkey.curVars[m]);
                        moveSpiderMonkey(tempSpiderMonkey, m, moveDist);
                    }
                    tempSpiderMonkey.curObjValue = getObjValue(tempSpiderMonkey.curVars);
                    tempSpiderMonkey.fit = 1 / tempSpiderMonkey.curObjValue;
                    if (greedy) {
                        if (tempSpiderMonkey.fit > spiderMonkeys[j].fit) {
                            spiderMonkeys[j] = tempSpiderMonkey;
                        }
                    } else {
                        spiderMonkeys[j] = tempSpiderMonkey;
                    }
                }
                for (int m = 0; m < spiderMonkeys[j].curVars.length; m++) {
                    positionArr[curC][c][m] = spiderMonkeys[j].curVars[m];
                }
                c++;
            }
            spiderMonkeyList.set(i, spiderMonkeys);
        }
        curC++;
    }

    // 初始化蜘蛛猴种群
    private void initSpiderMonkeys() {
        positionArr = new double[3 * maxGen][spiderMonkeyNum][varNum];
        SpiderMonkey[] spiderMonkeys = new SpiderMonkey[spiderMonkeyNum];
        SpiderMonkey localLeader = null;
        for (int i = 0; i < spiderMonkeyNum; i++) {
            spiderMonkeys[i] = getRandomSpiderMonkey();
            if (i == 0) {
                bestSpiderMonkey = copySpiderMonkey(spiderMonkeys[0]);
                localLeader = copySpiderMonkey(spiderMonkeys[0]);
            } else {
                if (bestSpiderMonkey.fit < spiderMonkeys[i].fit) {
                    bestSpiderMonkey = copySpiderMonkey(spiderMonkeys[i]);
                    localLeader = copySpiderMonkey(spiderMonkeys[0]);
                }
            }
        }
        spiderMonkeyList.add(spiderMonkeys);
        localLeaderList.add(localLeader);
    }

    // 获取一个随机生成的蜘蛛猴
    SpiderMonkey getRandomSpiderMonkey() {
        double[] vars = new double[varNum];
        for (int j = 0; j < vars.length; j++) {
            vars[j] = lb[j] + random.nextDouble() * (ub[j] - lb[j]);
        }
        double objValue = getObjValue(vars);
        return new SpiderMonkey(vars.clone(), objValue, 1 / objValue);
    }

    // 控制spiderMonkey在第m个维度上移动n个距离
    public void moveSpiderMonkey(SpiderMonkey spiderMonkey, int m, double n) {
        // 移动
        spiderMonkey.curVars[m] += n;
        // 超出定义域的判断
        if (spiderMonkey.curVars[m] < lb[m]) {
            spiderMonkey.curVars[m] = lb[m];
        }
        if (spiderMonkey.curVars[m] > ub[m]) {
            spiderMonkey.curVars[m] = ub[m];
        }
    }

    /**
     * @param vars 自变量数组
     * @return 返回目标函数值
     */
    IloCplex cplex = new IloCplex();

    public double getObjValue(double[] vars) {
        try {
            // 更新距离
            for (int i = 0; i < vars.length; i++) {
                if (i % 2 == 0) {
                    stockyardList.get(i / 2).x = vars[i];
                } else {
                    stockyardList.get(i / 2).y = vars[i];
                }
            }
            // 计算距离矩阵
            double[][] distanceMatrix = new double[stockyardList.size()][constructionSiteList.size()];
            for (int i = 0; i < distanceMatrix.length; i++) {
                Stockyard stockyard = stockyardList.get(i);
                for (int j = 0; j < distanceMatrix[i].length; j++) {
                    ConstructionSite constructionSite = constructionSiteList.get(j);
                    distanceMatrix[i][j] = calcDistance(stockyard.x, stockyard.y, constructionSite.x, constructionSite.y);
                }
            }
            // 开始建模
//            cplex = new IloCplex();
            // 声明变量
            IloNumVar[][] x = new IloNumVar[stockyardList.size()][constructionSiteList.size()];
            for (int i = 0; i < x.length; i++) {
                for (int j = 0; j < x[i].length; j++) {
                    x[i][j] = cplex.numVar(0, Math.min(stockyardList.get(i).c, constructionSiteList.get(j).d));
                }
            }
            // 构造约束1:必须满足每个工地的需求
            for (int j = 0; j < constructionSiteList.size(); j++) {
                IloLinearNumExpr expr = cplex.linearNumExpr();
                for (int i = 0; i < x.length; i++) {
                    expr.addTerm(1, x[i][j]);
                }
                cplex.addEq(expr, constructionSiteList.get(j).d);
            }
            // 构造约束2:不能超过每个料场的储量
            for (int i = 0; i < stockyardList.size(); i++) {
                cplex.addLe(cplex.sum(x[i]), stockyardList.get(i).c);
            }
            // 声明目标函数
            IloLinearNumExpr target = cplex.linearNumExpr();
            for (int i = 0; i < x.length; i++) {
                for (int j = 0; j < x[i].length; j++) {
                    target.addTerm(distanceMatrix[i][j], x[i][j]);
                }
            }
            cplex.addMinimize(target);
            // 配置cplex
            cplex.setOut(null);
            cplex.setWarning(null);
            cplex.setParam(IloCplex.DoubleParam.EpOpt, EPS);
            // 开始求解
            double obj;
            if (cplex.solve()) {
                obj = cplex.getObjValue();
            } else {
                throw new RuntimeException("此题无解");
            }
            cplex.clearModel();
            return obj;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    // 复制蜘蛛猴
    SpiderMonkey copySpiderMonkey(SpiderMonkey old) {
        return new SpiderMonkey(old.curVars.clone(), old.curObjValue, old.fit);
    }

    public static void main(String[] args) throws IloException {
        // 料场列表
        List<Stockyard> stockyardList = new ArrayList<>();
        stockyardList.add(new Stockyard(0, 0, 20));
        stockyardList.add(new Stockyard(0, 0, 20));
        // 工地列表
        List<ConstructionSite> constructionSiteList = getInitConstructionSiteList();
        long s = System.currentTimeMillis();
        new SMO_Solver(stockyardList, constructionSiteList).solve();
        System.out.println("总用时: " + (System.currentTimeMillis() - s) / 1000d + " s");
    }

}

输出结果如下:

0 : 122.18394756340149
1 : 115.22137772493119
2 : 107.64015378143884
3 : 91.25562099863063
4 : 87.27034345689911
5 : 86.08239574989028
6 : 85.5990696901402
7 : 85.5675162336327
8 : 85.40967721594052
9 : 85.33079736839548
10 : 85.29533933219953
11 : 85.27770206804239
12 : 85.27194687104382
13 : 85.26919031512125
14 : 85.26862219676372
15 : 85.26750239234426
16 : 85.26663764799034
17 : 85.26636259651076
18 : 85.26628507350429
19 : 85.26618997863784
20 : 85.26605221867857
21 : 85.26605221867857
22 : 85.26604976682619
23 : 85.26604439739287
24 : 85.26604351700756
25 : 85.26604156579967
26 : 85.26604141563301
27 : 85.26604110466047
28 : 85.2660410153342
29 : 85.26604098426941
30 : 85.26604096946092
31 : 85.26604090826108
32 : 85.26604089012692
33 : 85.26604088253666
34 : 85.26604087662045
35 : 85.26604087515656
36 : 85.26604087344376
37 : 85.26604087292048
38 : 85.26604087263298
39 : 85.26604087239937
40 : 85.26604087223953
41 : 85.26604087214378
42 : 85.26604087212847
43 : 85.2660408721172
44 : 85.26604087211567
45 : 85.2660408721116
46 : 85.26604087211126
47 : 85.26604087211126
48 : 85.26604087211103
49 : 85.26604087211096
50 : 85.26604087211076
51 : 85.2660408721107
52 : 85.2660408721107
53 : 85.26604087211066
54 : 85.26604087211066
55 : 85.26604087211066
56 : 85.26604087211064
57 : 85.26604087211064
58 : 85.26604087211064
59 : 85.26604087211064
变量取值为:[7.25, 7.75, 3.2548825928531566, 5.652332445501583]
最优解为:85.26604087211064
总用时: 0.997 s

四、总结

本博客分别介绍了一种贪心启发式算法和一种群体智能算法求解选址问题,结果表明,贪心启发式算法可以在极短的时间内得到较优的解,而蜘蛛猴优化算法则可以在迭代多次后得到更好的解。

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

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

相关文章

QT的network的使用

一个简单的双向的网络连接和信息发送。效果如下图所示&#xff1a; 只需要配置这个主机的IP和端口号&#xff0c;由客户端发送链接请求。即可进行连接。 QT的network模块是一个用于网络编程的模块&#xff0c;它提供了一系列的类和函数&#xff0c;可以让您使用TCP/IP协议来创…

pdf格式文件下载不预览,云存储的跨域解决

需求背景 后端接口中返回的是pdf文件路径比如&#xff1a; pdf文件路径 &#xff08;https://wangzhendongsky.oss-cn-beijing.aliyuncs.com/wzd-test.pdf&#xff09; 前端适配是这样的 <ahref"https://wangzhendongsky.oss-cn-beijing.aliyuncs.com/wzd-test.pdf&…

Spring框架之AOP详解

目录 一、前言 1.1.Spring简介 1.2.使用Spring的优点 二、Spring之AOP详解 2.1.什么是AOP 2.2.AOP在Spring的作用 2.3.AOP案例讲解 三、AOP案例实操 3.0.代理小故事&#xff08;方便理解代理模式&#xff09; 3.1.代码演示 3.2.前置通知 3.3.后置通知 3.3.环绕通知…

聚观早报|俞敏洪东方甄选带货北京特产;京东物流上半年盈利

【聚观365】8月17日消息 俞敏洪东方甄选直播间带货北京特产 京东物流上半年实现盈利 百度CTO称大语言模型为人工智能带来曙光 腾讯控股第二季度盈利262亿元 2023中国家庭智慧大屏消费白皮书 俞敏洪东方甄选直播间带货北京特产 近日&#xff0c;东方甄选在北京平谷区开播&…

Linux:shell脚本:基础使用(5)《正则表达式-sed工具》

sed是一种流编辑器&#xff0c;它是文本处理中非常中的工具&#xff0c;能够完美的配合正则表达式使用&#xff0c;功能不同凡响。 处理时&#xff0c;把当前处理的行存储在临时缓冲区中&#xff0c;称为“模式空间”&#xff08;pattern space&#xff09;&#xff0c;接着用s…

数据库MySQL 创建表INSERT

创建表 常见数据类型(列类型) 列类型之整型 unsigned的用法 列类型之bit 二进制表示 bit&#xff08;8&#xff09;表示一个字节 列类型之小数 1.单精度float 双精度double 2.decimal 自定义 M为小数点前面有多少位 D是小数点后面有多少位 列类型之字符串 1.char( 字符 )…

实现简单的element-table的拖拽效果

第一步&#xff0c;先随便创建element表格 <el-table ref"dragTable" :data"tableData" style"width: 100%" border fit highlight-current-row><el-table-column label"日期" width"180"><template slot-sc…

element-Plus中el-menu菜单无法正常收缩解决方案

<el-menu :collapse"true">如图所示收缩之后&#xff0c;有子级的菜单还有箭头文字显示 从代码对比看层级就不太对了&#xff0c;嵌套错误了&#xff0c;正常下方官网的ul标签下直接是li&#xff0c;在自己的代码中&#xff0c;ul标签下是div标签&#xff0c;层…

爬虫工具的选择与使用:阐述Python爬虫优劣势

作为专业爬虫ip方案解决服务商&#xff0c;我们每天都面对着大量的数据采集任务需求。在众多的爬虫工具中&#xff0c;Python爬虫凭借其灵活性和功能强大而备受青睐。本文将为大家分享Python爬虫在市场上的优势与劣势&#xff0c;帮助你在爬虫业务中脱颖而出。 一、优势篇 灵活…

初试rabbitmq

rabbitmq的七种模式 Hello word 客户端引入依赖 <!--rabbitmq 依赖客户端--><dependency><groupId>com.rabbitmq</groupId><artifactId>amqp-client</artifactId><version>5.8.0</version></dependency> 生产者 imp…

相对于多进程,你真的知道为什么要使用多线程吗(C/C++多线程编程)

目录 前言 线程VS进程 POSIX线程库的使用 线程创建 线程等待 线程分离 线程状态 可结合态线程实例 分离态线程实例 线程退出 线程的同步与互斥 同步互斥的概念 互斥锁&#xff08;互斥&#xff09; 互斥锁的使用步骤 总结说明 信号量 信号量的使用步骤 条件变…

数据包如何游走于 Iptables 规则之间?

在前文《Linux路由三大件》中&#xff0c;我们提到了 iptables 可以修改数据包的特征从而影响其路由。这个功能无论是传统场景下的 防火墙&#xff0c;还是云原生场景下的 服务路由&#xff08;k8s service&#xff09;、网络策略(calico network policy) 等都有依赖。 虽然业…

7.逻辑结构VS物理结构

第四章 文件管理 7.逻辑结构VS物理结构 ​   fopen这个函数做的事情就是打开了“test.txt”这个文件&#xff0c;并且“w”说明是以“写”的方式打开的&#xff0c;以“写”的方式打开才能往这个文件里写入数据&#xff0c;如果文件打开了那么fp这个指针就可以指向和这个文件…

Eclipse如何设置快捷键

在eclopse设置注释行和取消注释行 // 打开eclipse&#xff0c;依次打开&#xff1a;Window -> Preferences -> General -> Key&#xff0c;

数据结构--关键路径

数据结构–关键路径 AOE⽹ 在 带权有向图 \color{red}带权有向图 带权有向图中&#xff0c;以 顶点表示事件 \color{red}顶点表示事件 顶点表示事件&#xff0c;以 有向边表示活动 \color{red}有向边表示活动 有向边表示活动&#xff0c;以 边上的权值表示完成该活动的开销 \…

HCIE--------------------------------------第一节OSPF快速收敛(OSPF与BGP联动)

一、OSPF快速收敛概述 OSPF快速收敛是为了提高路由的收敛速度而做的扩展特性&#xff0c;包括&#xff1a;PRC&#xff08;Partial Route Calculation&#xff0c;部分路由计算&#xff09;和智能定时器。 同时&#xff0c;OSPF支持故障恢复快速收敛&#xff0c;例如通过OSPF …

Linux Server 20.04 Qt5.14.2配置Jetson Orin Nano Developer Kit 交叉编译环境

最近公司给了我一块Jetson Orin Nano的板子&#xff0c;让我搭建交叉编译环境&#xff0c;所以有了下面的文章 一 :Qt5.14.2交叉编译环境安装 1.准备 1.1设备环境 1.1.1 Server: Ubuntu20.04: Qt 源码 5.14.2 Qt 软件 5.14.2 gcc 版本 9.4.0 g 版本 9.4.0 1.1.2 Jetson …

在 React 中获取数据的6种方法

一、前言 数据获取是任何 react 应用程序的核心方面。对于 React 开发人员来说&#xff0c;了解不同的数据获取方法以及哪些用例最适合他们很重要。 但首先&#xff0c;让我们了解 JavaScript Promises。 简而言之&#xff0c;promise 是一个 JavaScript 对象&#xff0c;它将…

openGauss学习笔记-42 openGauss 高级数据管理-触发器

文章目录 openGauss学习笔记-42 openGauss 高级数据管理-触发器42.1 语法格式42.2 参数说明42.3 示例 openGauss学习笔记-42 openGauss 高级数据管理-触发器 触发器会在指定的数据库事件发生时自动执行函数。 42.1 语法格式 创建触发器 CREATE TRIGGER trigger_name { BEFORE…

Java8实战-总结16

Java8实战-总结16 引入流流与集合只能遍历一次外部迭代与内部迭代 引入流 流与集合 只能遍历一次 和迭代器类似&#xff0c;流只能遍历一次。遍历完之后&#xff0c;这个流就已经被消费掉了。可以从原始数据源那里再获得一个新的流来重新遍历一遍&#xff0c;就像迭代器一样…