【运筹优化】PSO粒子群算法求解无约束多元函数最值(Java代码实现)


一、粒子群算法的定义

粒子群优化算法(Particle Swarm optimization,PSO)又翻译为粒子群算法、微粒群算法、或微粒群优化算法。是通过模拟鸟群觅食行为而发展起来的一种基于群体协作的随机搜索算法。通常认为它是群集智能 (Swarm intelligence, SI) 的一种。它可以被纳入多主体优化系统(Multiagent Optimization System, MAOS)。粒子群优化算法是由Eberhart博士和kennedy博士发明。

二、模拟鸟类捕食

PSO模拟鸟群的捕食行为。一群鸟在随机搜索食物,在这个区域里只有一块食物。所有的鸟都不知道食物在那里。但是他们知道当前的位置离食物还有多远。那么找到食物的最优策略是什么呢。最简单有效的就是搜寻离食物最近的鸟的周围区域。

三、启示

PSO从这种模型中得到启示并用于解决优化问题。PSO中,每个优化问题的解都是搜索空间中的一只鸟。我们称之为“粒子”。所有的粒子都有一个由被优化的函数决定的适应值(fitnessvalue),每个粒子还有一个速度决定他们飞翔的方向和距离。然后粒子们就追随当前的最优粒子在解空间中搜索。

四、算法流程图

在这里插入图片描述

五、案例实战 & Java代码实现

本项目的所有代码均上传至 GitHub 仓库:https://github.com/WSKH0929/SwarmIntelligenceAlgorithm(如果对你有帮助的话可以点个Star❤️哟~)

5.1 案例1:求解二元函数最小值

Z = x^2 + y^2 - xy - 10x - 4y +60

5.1.1 算法部分代码

import java.util.Arrays;
import java.util.Random;

/**
 * @Author:WSKH
 * @ClassName:PSO_Solve
 * @ClassType:
 * @Description:
 * @Date:2022/6/6/14:59
 * @Email:1187560563@qq.com
 * @Blog:https://blog.csdn.net/weixin_51545953?type=blog
 */
public class PSO_Solve {

    // 粒子对象
    class Particle {
        // 粒子速度数组(每个方向都有一个速度)
        double[] vArr;
        // 当前粒子坐标(自变量数组)
        double[] curVars;
        // 当前自变量对应的目标函数值
        double curObjValue;
        // 该粒子找到过的最佳目标函数值
        double bestObjValue;
        // 该粒子最好位置时的坐标
        double[] bestVars;
        // 全参构造
        public Particle(double[] vArr, double[] curVars, double curObjValue, double bestObjValue, double[] bestVars) {
            this.vArr = vArr;
            this.curVars = curVars;
            this.curObjValue = curObjValue;
            this.bestObjValue = bestObjValue;
            this.bestVars = bestVars;
        }
    }

    // 粒子数量
    int n = 500;
    // 每个粒子的个体学习因子:自我认知,设置较大则不容易被群体带入局部最优,但会减缓收敛速度
    double c1 = 2;
    // 每个粒子的社会学习因子:社会认知,设置较大则加快收敛,但容易陷入局部最优
    double c2 = 2;
    // 粒子的惯性权重
    double w = 0.9;
    // 迭代的次数
    int MaxGen = 500;
    // 粒子的每个维度上的最大速度(数组)
    double[] vMaxArr = new double[]{1.2,1.2};
    // 随机数对象
    Random random = new Random();
    // 自变量个数
    int varNum = 2;
    // 自变量的上下界数组
    double[] lbArr = new double[]{-1000, -1000};
    double[] ubArr = new double[]{1000, 1000};
    // 粒子群
    Particle[] particles;
    // 最佳粒子
    Particle bestParticle;
    // 记录迭代过程
    public double[][][] positionArr;

    /**
     * @Description 初始化粒子的位置和速度
     */
    private void initParticles() {
        // 初始化粒子群
        particles = new Particle[n];
        // 随机生成粒子
        for (int i = 0; i < particles.length; i++) {
            // 随机生成坐标和速度
            double[] vars = new double[varNum];
            double[] vArr = new double[varNum];
            for (int j = 0; j < varNum; j++) {
                vars[j] = random.nextDouble() * (ubArr[j] - lbArr[j]) + lbArr[j];
                vArr[j] = (random.nextDouble() - 0.5) * 2 * vMaxArr[j];
            }
            // 目标函数值
            double objValue = getObjValue(vars);
            particles[i] = new Particle(vArr.clone(), vars.clone(), objValue, objValue, vars.clone());
        }
        // 找到初始化粒子群中的最佳粒子
        bestParticle = copyParticle(particles[0]);
        for (int i = 1; i < particles.length; i++) {
            if (bestParticle.bestObjValue > particles[i].bestObjValue) {
                bestParticle = copyParticle(particles[i]);
            }
        }
    }

    /**
     * @Description 主要求解函数
     */
    public void solve() {
        // 变量设置初步判断
        if(varNum != vMaxArr.length || varNum != lbArr.length || varNum != ubArr.length){
            throw new RuntimeException("变量维度不一致");
        }
        positionArr = new double[MaxGen][n][varNum];
        // 初始化粒子的位置和速度
        initParticles();
        // 开始迭代
        for (int i = 0; i < MaxGen; i++) {
            // 依次更新第i个粒子的速度与位置
            for (int j = 0; j < particles.length; j++) {
                // 针对不同维度进行处理
                for (int k = 0; k < varNum; k++) {
                    // 更新速度
                    double newV = particles[j].vArr[k] * w
                            + c1 * random.nextDouble() * (particles[j].bestVars[k] - particles[j].curVars[k])
                            + c2 * random.nextDouble() * (bestParticle.bestVars[k] - particles[j].curVars[k]);
                    // 如果速度超过了最大限制,就对其进行调整
                    if (newV < -vMaxArr[k]) {
                        newV = -vMaxArr[k];
                    } else if (newV > vMaxArr[k]) {
                        newV = vMaxArr[k];
                    }
                    // 更新第j个粒子第k个维度上的位置
                    double newPos = particles[j].curVars[k] + newV;
                    // 记录迭代过程
                    positionArr[i][j][k] = newPos;
                    // 如果位置超出了定义域,就对其进行调整
                    if(newPos < lbArr[k]){
                        newPos = lbArr[k];
                    }else if(newPos > ubArr[k]){
                        newPos = ubArr[k];
                    }
                    // 赋值回去
                    particles[j].curVars[k] = newPos;
                    particles[j].vArr[k] = newV;
                }
                // 更新完所有维度后,再计算第j个粒子的函数值
                double objValueJ = getObjValue(particles[j].curVars);
                particles[j].curObjValue = objValueJ;
                if(objValueJ < particles[j].bestObjValue){
                    particles[j].bestVars = particles[j].curVars.clone();
                    particles[j].bestObjValue = particles[j].curObjValue;
                }
                if(objValueJ < bestParticle.bestObjValue){
                    bestParticle = copyParticle(particles[j]);
                }
            }
        }
        // 迭代结束,输出最优粒子位置和函数值
        System.out.println("最优解为:"+ bestParticle.bestObjValue);
        System.out.println("最优解坐标为:"+ Arrays.toString(bestParticle.bestVars));
    }

    /**
     * @param vars 自变量数组
     * @return 返回目标函数值
     */
    public double getObjValue(double[] vars) {
        //目标:在变量区间范围最小化 Z = x^2 + y^2 - xy - 10x - 4y +60
        return Math.pow(vars[0], 2) + Math.pow(vars[1], 2) - vars[0] * vars[1] - 10 * vars[0] - 4 * vars[1] + 60;
    }

    // 复制粒子
    public Particle copyParticle(Particle old) {
        return new Particle(old.vArr.clone(), old.curVars.clone(), old.curObjValue, old.bestObjValue, old.bestVars.clone());
    }

}

5.1.2 运行结果

最优解为:8.000057527138821
最优解坐标为:[8.00430921604821, 5.995551568450099]
求解用时:0.019 s

5.1.3 迭代过程可视化

在这里插入图片描述

5.1.4 可视化代码

import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Button;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.util.Duration;

/**
 * @Author:WSKH
 * @ClassName:PlotUtil
 * @ClassType:
 * @Description:
 * @Date:2022/6/6/18:31
 * @Email:1187560563@qq.com
 * @Blog:https://blog.csdn.net/weixin_51545953?type=blog
 */
public class PlotUtil extends Application {

    //当前的时间轴
    private Timeline nowTimeline;
    //绘图位置坐标
    private double[][][] positionArr;

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

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

        // 调用算法获取绘图数据
        PSO_Solve psoApi = new PSO_Solve();
        psoApi.solve();
        this.positionArr = psoApi.positionArr;

        // 画图
        try {
            BorderPane root = new BorderPane();
            root.setStyle("-fx-padding: 20;");
            Scene scene = new Scene(root, 1600, 900);
            double canvasWid = 800;
            double canvasHei = 800;
            //根据画布大小缩放坐标值
            this.fixPosition(canvasWid - 100, canvasHei - 100);

            //画布和画笔
            HBox canvasHbox = new HBox();
            Canvas canvas = new Canvas();
            canvas.setWidth(canvasWid);
            canvas.setHeight(canvasHei);
            canvasHbox.setPrefWidth(canvasWid);
            canvasHbox.getChildren().add(canvas);
            canvasHbox.setAlignment(Pos.CENTER);
            canvasHbox.setStyle("-fx-spacing: 20;" +
                    "-fx-background-color: #87e775;");
            root.setTop(canvasHbox);
            GraphicsContext paintBrush = canvas.getGraphicsContext2D();

            //启动
            HBox hBox2 = new HBox();
            Button beginButton = new Button("播放迭代过程");
            hBox2.getChildren().add(beginButton);
            root.setBottom(hBox2);
            hBox2.setAlignment(Pos.CENTER);
            //启动仿真以及暂停仿真
            beginButton.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> {
                nowTimeline.play();
            });

            //创建扫描线连接动画
            nowTimeline = new Timeline();
            createAnimation(paintBrush);

            primaryStage.setScene(scene);
            primaryStage.show();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 修正cityPositionArr的坐标,让画出来的点在画布内
     *
     * @param width
     * @param height
     */
    private void fixPosition(double width, double height) {
        double minX = Double.MAX_VALUE;
        double maxX = -Double.MAX_VALUE;
        double minY = Double.MAX_VALUE;
        double maxY = -Double.MAX_VALUE;

        for (int i = 0; i < this.positionArr.length; i++) {
            for (int j = 0; j < this.positionArr[0].length; j++) {
                minX = Math.min(minX, this.positionArr[i][j][0]);
                maxX = Math.max(maxX, this.positionArr[i][j][0]);
                minY = Math.min(minY, this.positionArr[i][j][1]);
                maxY = Math.max(maxY, this.positionArr[i][j][1]);
            }
        }

        double multiple = Math.max((maxX - minX) / width, (maxY - minY) / height);

        //转化为正数数
        for (int i = 0; i < this.positionArr.length; i++) {
            for (int j = 0; j < this.positionArr[0].length; j++) {
                if (minX < 0) {
                    this.positionArr[i][j][0] = this.positionArr[i][j][0] - minX;
                }
                if (minY < 0) {
                    this.positionArr[i][j][1] = this.positionArr[i][j][1] - minY;
                }
            }
        }

        for (int i = 0; i < this.positionArr.length; i++) {
            for (int j = 0; j < this.positionArr[0].length; j++) {
                this.positionArr[i][j][0] = this.positionArr[i][j][0] / multiple;
                this.positionArr[i][j][1] = this.positionArr[i][j][1] / multiple;
            }
        }

    }

    /**
     * 用画笔在画布上画出所有的孔
     * 画第i代的所有粒子
     */
    private void drawAllCircle(GraphicsContext paintBrush, int i) {
        paintBrush.clearRect(0, 0, 2000, 2000);
        paintBrush.setFill(Color.RED);
        for (int j = 0; j < this.positionArr[i].length; j++) {
            drawCircle(paintBrush, i, j);
        }
    }

    /**
     * 用画笔在画布上画出一个孔
     * 画第i代的第j个粒子
     */
    private void drawCircle(GraphicsContext paintBrush, int i, int j) {
        double x = this.positionArr[i][j][0];
        double y = this.positionArr[i][j][1];
        double radius = 2;
        // 圆的直径
        double diameter = radius * 2;
        paintBrush.fillOval(x, y, diameter, diameter);
    }

    /**
     * 创建动画
     */
    private void createAnimation(GraphicsContext paintBrush) {
        for (int i = 0; i < this.positionArr[0].length; i++) {
            int finalI = i;
            KeyFrame keyFrame = new KeyFrame(Duration.seconds(i * 0.05), event -> drawAllCircle(paintBrush, finalI));
            nowTimeline.getKeyFrames().add(keyFrame);
        }
    }

}

5.2 案例2:求解六元函数最小值

设六元变量分别为 a b c d e f
目标:在变量区间范围最小化 Z = abc - bce + cd + ef - bdf

5.2.1 算法部分代码

import java.util.Arrays;
import java.util.Random;

/**
 * @Author:WSKH
 * @ClassName:PSO_Solve_6d
 * @ClassType:
 * @Description:
 * @Date:2022/6/6/14:59
 * @Email:1187560563@qq.com
 * @Blog:https://blog.csdn.net/weixin_51545953?type=blog
 */
public class PSO_Solve_6d {

    // 粒子对象
    class Particle {
        // 粒子速度数组(每个方向都有一个速度)
        double[] vArr;
        // 当前粒子坐标(自变量数组)
        double[] curVars;
        // 当前自变量对应的目标函数值
        double curObjValue;
        // 该粒子找到过的最佳目标函数值
        double bestObjValue;
        // 该粒子最好位置时的坐标
        double[] bestVars;

        // 全参构造
        public Particle(double[] vArr, double[] curVars, double curObjValue, double bestObjValue, double[] bestVars) {
            this.vArr = vArr;
            this.curVars = curVars;
            this.curObjValue = curObjValue;
            this.bestObjValue = bestObjValue;
            this.bestVars = bestVars;
        }
    }

    // 粒子数量
    int n = 100;
    // 每个粒子的个体学习因子:自我认知,设置较大则不容易被群体带入局部最优,但会减缓收敛速度
    double c1 = 2;
    // 每个粒子的社会学习因子:社会认知,设置较大则加快收敛,但容易陷入局部最优
    double c2 = 2;
    // 粒子的惯性权重
    double w = 0.9;
    // 迭代的次数
    int MaxGen = 500;
    // 粒子的每个维度上的最大速度(数组)
    double[] vMaxArr = new double[]{1.2, 1.2, 1.2, 1.2, 1.2, 1.2};
    // 随机数对象
    Random random = new Random();
    // 自变量个数
    int varNum = 6;
    // 自变量的上下界数组
    double[] lbArr = new double[]{-1000, -1000, -1000, -1000, -1000, -1000};
    double[] ubArr = new double[]{1000, 1000, 1000, 1000, 1000, 1000};
    // 粒子群
    Particle[] particles;
    // 最佳粒子
    Particle bestParticle;

    /**
     * @Description 初始化粒子的位置和速度
     */
    private void initParticles() {
        // 初始化粒子群
        particles = new Particle[n];
        // 随机生成粒子
        for (int i = 0; i < particles.length; i++) {
            // 随机生成坐标和速度
            double[] vars = new double[varNum];
            double[] vArr = new double[varNum];
            for (int j = 0; j < varNum; j++) {
                vars[j] = random.nextDouble() * (ubArr[j] - lbArr[j]) + lbArr[j];
                vArr[j] = (random.nextDouble() - 0.5) * 2 * vMaxArr[j];
            }
            // 目标函数值
            double objValue = getObjValue(vars);
            particles[i] = new Particle(vArr.clone(), vars.clone(), objValue, objValue, vars.clone());
        }
        // 找到初始化粒子群中的最佳粒子
        bestParticle = copyParticle(particles[0]);
        for (int i = 1; i < particles.length; i++) {
            if (bestParticle.bestObjValue > particles[i].bestObjValue) {
                bestParticle = copyParticle(particles[i]);
            }
        }
    }

    /**
     * @Description 主要求解函数
     */
    public void solve() {
        // 变量设置初步判断
        if (varNum != vMaxArr.length || varNum != lbArr.length || varNum != ubArr.length) {
            throw new RuntimeException("变量维度不一致");
        }
        // 初始化粒子的位置和速度
        initParticles();
        // 开始迭代
        for (int i = 0; i < MaxGen; i++) {
            // 依次更新第i个粒子的速度与位置
            for (int j = 0; j < particles.length; j++) {
                // 针对不同维度进行处理
                for (int k = 0; k < varNum; k++) {
                    // 更新速度
                    double newV = particles[j].vArr[k] * w
                            + c1 * random.nextDouble() * (particles[j].bestVars[k] - particles[j].curVars[k])
                            + c2 * random.nextDouble() * (bestParticle.bestVars[k] - particles[j].curVars[k]);
                    // 如果速度超过了最大限制,就对其进行调整
                    if (newV < -vMaxArr[k]) {
                        newV = -vMaxArr[k];
                    } else if (newV > vMaxArr[k]) {
                        newV = vMaxArr[k];
                    }
                    // 更新第j个粒子第k个维度上的位置
                    double newPos = particles[j].curVars[k] + newV;
                    // 如果位置超出了定义域,就对其进行调整
                    if (newPos < lbArr[k]) {
                        newPos = lbArr[k];
                    } else if (newPos > ubArr[k]) {
                        newPos = ubArr[k];
                    }
                    // 赋值回去
                    particles[j].curVars[k] = newPos;
                    particles[j].vArr[k] = newV;
                }
                // 更新完所有维度后,再计算第j个粒子的函数值
                double objValueJ = getObjValue(particles[j].curVars);
                particles[j].curObjValue = objValueJ;
                if (objValueJ < particles[j].bestObjValue) {
                    particles[j].bestVars = particles[j].curVars.clone();
                    particles[j].bestObjValue = particles[j].curObjValue;
                }
                if (objValueJ < bestParticle.bestObjValue) {
                    bestParticle = copyParticle(particles[j]);
                }
            }
        }
        // 迭代结束,输出最优粒子位置和函数值
        System.out.println("最优解为:" + bestParticle.bestObjValue);
        System.out.println("最优解坐标为:" + Arrays.toString(bestParticle.bestVars));
    }

    /**
     * @param vars 自变量数组
     * @return 返回目标函数值
     */
    public double getObjValue(double[] vars) {
        // 设六元变量分别为 a b c d e f
        // 目标:在变量区间范围最小化 Z = abc - bce + cd + ef - bdf
        return (vars[0] * vars[1] * vars[2] - vars[1] * vars[2] * vars[3] + vars[2] * vars[4] + vars[4] * vars[5] - vars[1] * vars[3] * vars[5]);
    }

    // 复制粒子
    public Particle copyParticle(Particle old) {
        return new Particle(old.vArr.clone(), old.curVars.clone(), old.curObjValue, old.bestObjValue, old.bestVars.clone());
    }

}

5.2.2 运行结果

最优解为:-8.387035664320209E8
最优解坐标为:[-886.8352994556639, -748.1858708041178, -560.6940636412202, 646.2689604948565, -274.97367655848336, -404.99446493036083]
求解用时:0.028 s
  • 10
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
运筹优化Python是使用Python编程语言进行运筹优化问题建模和求解的过程。运筹优化是一种数学方法,用于优化决策问题,例如最大化利润或最小化成本。Python是一种功能强大的编程语言,具有丰富的库和工具,可以帮助我们在运筹优化中进行问题建模和求解。 通过使用Python,您可以使用线性规划库来解决线性规划问题。这些库包装了本机求解器,可以提供解决方案状态、决策变量值、松弛变量、目标函数等结果信息。一些常用的Python线性规划库包括GLPK、LP Solve、CBC、CVXOPT、SciPy等。 此外,还有一些商业求解器提供了Python API,如Gurobi Optimization。Gurobi是一家提供快速商业求解器的公司,他们还提供关于线性规划和混合整数线性规划的宝贵资源,包括基础知识、教程和如何选择数学规划求解器的指南。 总之,运筹优化Python是指使用Python编程语言进行运筹优化问题建模和求解的过程,可以通过使用线性规划库或商业求解器的Python API来解决问题。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [运筹优化学习26:Python求解线性规划问题入门手册](https://blog.csdn.net/m1m2m3mmm/article/details/112579384)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WSKH0929

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值