开发者社区> WSKH0929> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

【智能算法】SSA樽海鞘优化算法求解无约束多元函数最值(Java代码实现)

简介: 【智能算法】SSA樽海鞘优化算法求解无约束多元函数最值(Java代码实现)
+关注继续查看

@[toc]


前言

本文以求解二元函数最小值为例,如果需要求解多元函数,只需要修改以下变量即可:

  • varNum:变量维度数
  • ub和lb:变量的上下界
  • vMaxArr:每个维度的搜索速度限制

优化目标

目标:在变量区间范围最小化 Z = x^2 + y^2 - xy - 10x - 4y +60

求解结果

变量取值为:[7.9999860637249585, 5.999987161740172]
最优解为:8.000000000180123

搜索过程可视化

在这里插入图片描述

Java算法代码

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

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

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

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

    // 算法参数
    // 变量个数
    int varNum = 2;
    // 最大迭代次数
    int maxGen = 1000;
    // 樽海鞘群中樽海鞘的个数
    int salpNum = 200;
    // 领导者数量和追随者数量(领导者数量+追随者数量=樽海鞘群中樽海鞘的个数)
    int leaderNum = 1; // 开始我理解错了,其实领导者只有一个,所以领导者数量设置为1就好
    int followNum = salpNum - leaderNum;
    // 步长数组(各个维度的步长)
    double[] stepArr = new double[]{1.2, 1.2};
    // 变量的上下界
    double[] ub = new double[]{1000, 1000};
    double[] lb = new double[]{-1000, -1000};
    // 随机数对象
    Random random = new Random();
    // 樽海鞘群
    Salp[] salps;
    // 最佳的樽海鞘
    Salp bestSalp;
    // 记录迭代过程
    public double[][][] positionArr;
    // 当前记录的行数
    int r;

    // 求解主函数
    public void solve() {
        // 初始化樽海鞘群
        initSalps();
        // 开始迭代
        for (int t = 0; t < maxGen; t++) {
            updateLeaderPos(t);
            updateFollowPos();
            report();
        }
        // 输出最好的结果
        System.out.println("变量取值为:" + Arrays.toString(bestSalp.curVars));
        System.out.println("最优解为:" + bestSalp.curObjValue);
    }

    // 记录
    void report() {
        for (int i = 0; i < salps.length; i++) {
            for (int j = 0; j < varNum; j++) {
                positionArr[r][i][j] = salps[i].curVars[j];
            }
        }
        r++;
    }

    // 更新追随者位置(t:当前迭代次数)
    void updateFollowPos(){
        for (int i = leaderNum; i < salpNum; i++) {
            Salp tempSalp = copySalp(salps[i]);
            for (int m = 0; m < varNum; m++) {
                double move = 0.5*(tempSalp.curVars[m]+salps[i-1].curVars[m]) - tempSalp.curVars[m];
                moveSalp(tempSalp,m,move);
            }
            updateSalp(tempSalp);
            // 不贪心的追随者(只跟着领导走)
            salps[i] = tempSalp;
            if(salps[i].fit > bestSalp.fit){
                bestSalp = copySalp(salps[i]);
            }
        }
    }

    // 更新领导者位置 (t;当前迭代次数)
    void updateLeaderPos(int t) {
        for (int i = 0; i < leaderNum; i++) {
            Salp tempSalp = copySalp(salps[i]);
            for (int m = 0; m < varNum; m++) {
                double c1 = 2 * Math.exp(-(Math.pow(4d * t / (double) maxGen, 2)));
                double c2 = (random.nextDouble() - 0.5) * 2;
                double c3 = (random.nextDouble() - 0.5) * 2;
                double move = 0d;
                if (c3 >= 0) {
                    move = c1 * ((ub[m] - lb[m] * c2 + lb[m]));
                } else {
                    move = -c1 * ((ub[m] - lb[m] * c2 + lb[m]));
                }
                moveSalp(tempSalp,m,move);
            }
            updateSalp(tempSalp);
            // 贪心的领导(只去往比当前结果好的位置,如果领导不贪心,则不容易收敛,到处跑)
            if(tempSalp.fit > salps[i].fit){
                salps[i] = tempSalp;
                if(salps[i].fit > bestSalp.fit){
                    bestSalp = copySalp(salps[i]);
                }
            }
        }
    }

    // 求两个樽海鞘之间的距离
    double getDistance(Salp f1, Salp f2) {
        double dis = 0d;
        for (int i = 0; i < varNum; i++) {
            dis += Math.pow(f1.curVars[i] - f2.curVars[i], 2);
        }
        return Math.sqrt(dis);
    }

    // 初始化樽海鞘群
    private void initSalps() {
        positionArr = new double[maxGen][salpNum][varNum];
        salps = new Salp[salpNum];
        for (int i = 0; i < salpNum; i++) {
            salps[i] = getRandomSalp();
            if (i == 0 || bestSalp.fit < salps[i].fit) {
                bestSalp = copySalp(salps[i]);
            }
        }
    }

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

    // 更新樽海鞘信息
    void updateSalp(Salp salp) {
        double objValue = getObjValue(salp.curVars);
        salp.curObjValue = objValue;
        salp.fit = 1 / objValue;
    }

    // 获取一个随机生成的樽海鞘
    Salp getRandomSalp() {
        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 Salp(vars.clone(), objValue, 1 / objValue);
    }

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

    // 复制樽海鞘
    Salp copySalp(Salp old) {
        return new Salp(old.curVars.clone(), old.curObjValue, old.fit);
    }

}

可视化代码

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 {

        // 调用算法获取绘图数据
        SSA_Solve solver = new SSA_Solve();
        solver.solve();
        positionArr = solver.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);
        }
    }

}

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
JNI用C加载JDK产生JVM虚拟机,并运行JAVA类main函数(MACOS/LINUX/WINDOWS)
JNI用C加载JDK产生JVM虚拟机,并运行JAVA类main函数(MACOS/LINUX/WINDOWS)
0 0
Python:jpype模块调用Java函数
Python:jpype模块调用Java函数
0 0
JAVA如何动态加载jar,并调用对应的函数?
JAVA如何动态加载jar,并调用对应的函数?
0 0
一个类的两个接口有同名函数,JAVA如何调用
一个类的两个接口有同名函数,JAVA如何调用
0 0
java函数的基本概念,作用与语法
java函数的基本概念,作用与语法
0 0
【Java技术指南】「Java8技术盲区」函数接口字典:看看还有哪些你所不知道函数接口?(技术小贴士)
【Java技术指南】「Java8技术盲区」函数接口字典:看看还有哪些你所不知道函数接口?(技术小贴士)
0 0
阶乘函数后 K 个零(java 算法)
阶乘函数后 K 个零(java 算法)
0 0
记一次java命令执行IDE中java类的入口函数的坑
熟悉java的同学都知道,我们可以直接通过`java命令`执行Java类的`入口函数`,执行时还可以携带参数,参数是以`字符串数组`的形式传递给main入口函数的。 不过现在已经很少使用这种方式执行java代码了,都是通过IDE来执行。
0 0
Java方法的概念及其使用!!(相当于C语言中的函数)
Java方法的概念及其使用!!(相当于C语言中的函数)
0 0
实现 strStr()函数(Java实现)
实现 strStr()函数(Java实现)
0 0
+关注
文章
问答
文章排行榜
最热
最新
相关电子书
更多
JAVA 应用排查全景图
立即下载
Java工程师必读手册
立即下载
Java应用提速(速度与激情)
立即下载