【智能算法】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);

}

}

}

相关数据

小米5X深度评测:不同版本选购指南及优缺点对比
3匹的空调制热功率一般是多少W 3匹的空调制热功率介绍【详解】
大型集体照拍摄技巧全攻略:从站位到照片直播

友情链接