代码
package com.dam.heuristic.pso.test; import java.util.List; import java.util.Random; public class PsoApi { //粒子数量 private int particleNum; //个体学习因子,设置得越大,粒子越容易根据自己的想法飞行,若设置过大,容易跳出局部最优,但收敛较慢 private double c1; //社会学习因子,设置得越大,粒子越容易根据群体的想法飞行,若设置过大,容易陷入局部最优,收敛较快 private double c2; //速度最大值 private double vMax; //速度的惯性权重 private double w; //迭代次数 private int genMax; public PsoApi(int particleNum, double c1, double c2, double vMax, double w, int genMax) { this.particleNum = particleNum; this.c1 = c1; this.c2 = c2; this.vMax = vMax; this.w = w; this.genMax = genMax; } /** * 求解 */ public double[][][] solve() { 变量声明 //存储粒子 Particle[] particleArr; //所有粒子找到的最优解(由于问题为最小化问题,设置初始最优值为较大的数) double gBest = Double.MAX_VALUE; //群体最优解对应的x和y double bestX = 0; double bestY = 0; //随机数工具 Random random = new Random(); long start = System.currentTimeMillis(); //存储每一代粒子所在位置 double[][][] positionArr = new double[this.genMax][this.particleNum][2]; 初始化粒子 particleArr = new Particle[this.particleNum]; for (int i = 0; i < particleArr.length; i++) { //初始化粒子群,注意:这里设置每个粒子的速度一样,读者可以根据自己的喜爱进行设置 //初始化粒子的坐标和速度 particleArr[i] = new Particle(-1000, 1000, -1000, 1000, 0.01, 0.01, random); //初始化粒子的函数值(粒子还没有开始飞,当前位置肯定是找到过的最优位置啦) particleArr[i].setBestX(particleArr[i].getX()); particleArr[i].setBestY(particleArr[i].getY()); double pValue = this.objectFunction(particleArr[i].getX(), particleArr[i].getY()); particleArr[i].setpBest(pValue); //由于问题为最小化问题,目标函数越小越好 if (pValue < gBest) { bestX = particleArr[i].getX(); bestY = particleArr[i].getY(); gBest = pValue; } } 开始求解 for (int i = 0; i < this.genMax; i++) { //对每个粒子进行操作 for (int j = 0; j < this.particleNum; j++) { ///更新速度 //更新x轴方向上的速度 particleArr[j].setxV(this.w * particleArr[j].getxV() + this.c1 * random.nextDouble() * (particleArr[j].getBestX() - particleArr[j].getX()) + this.c2 * random.nextDouble() * (bestX - particleArr[j].getX())); //处理越界 if (particleArr[j].getxV() > this.vMax) { particleArr[j].setxV(this.vMax); }else if (particleArr[j].getxV() < -this.vMax) { particleArr[j].setxV(-this.vMax); } //更新y轴方向上的速度 particleArr[j].setyV(this.w * particleArr[j].getyV() + this.c1 * random.nextDouble() * (particleArr[j].getBestY() - particleArr[j].getY()) + this.c2 * random.nextDouble() * (bestY - particleArr[j].getY())); //处理越界 if (particleArr[j].getyV() > this.vMax) { particleArr[j].setyV(this.vMax); } else if (particleArr[j].getyV() < -this.vMax) { particleArr[j].setyV(-this.vMax); } ///更新位置 double nextX = particleArr[j].getX() + particleArr[j].getxV(); //处理越界 if (nextX > particleArr[j].getxMax()) { nextX = particleArr[j].getxMax(); } else if (nextX < particleArr[j].getxMin()) { nextX = particleArr[j].getxMin(); } particleArr[j].setX(nextX); double nextY = particleArr[j].getY() + particleArr[j].getyV(); //处理越界 if (nextY > particleArr[j].getyMax()) { nextY = particleArr[j].getyMax(); } else if (nextY < particleArr[j].getyMin()) { nextY = particleArr[j].getyMin(); } particleArr[j].setY(nextY); ///更新粒子历史最优解和粒子全体最优解 double pValue = this.objectFunction(particleArr[j].getX(), particleArr[j].getY()); if (pValue < particleArr[j].getpBest()) { particleArr[j].setBestX(particleArr[j].getX()); particleArr[j].setBestY(particleArr[j].getY()); particleArr[j].setpBest(pValue); } //由于问题为最小化问题,目标函数越小越好 if (pValue < gBest) { bestX = particleArr[j].getX(); bestY = particleArr[j].getY(); gBest = pValue; } ///存储画图数据 positionArr[i][j][0] = particleArr[j].getX(); positionArr[i][j][1] = particleArr[j].getY(); } } //输出保留6位小数 System.out.println("最优目标函数值:" + String.format("%.6f", gBest)); System.out.println("最优x:" + String.format("%.6f", bestX)); System.out.println("最优y:" + String.format("%.6f", bestY)); System.out.println("求解时间:" + (System.currentTimeMillis() - start) + "ms"); return positionArr; } /** * 目标函数 * * @param x * @param y * @return */ private double objectFunction(double x, double y) { //目标:在变量区间范围最小化 y=x^2+y^2-xy-10x-4y+60 return Math.pow(x, 2) + Math.pow(y, 2) - x * y - 10 * x - 4 * y + 60; } /** * 粒子类 */ class Particle { private double x; private double y; //x,y坐标的上下限 private double xMin; private double xMax; private double yMin; private double yMax; //x轴方向上的速度 private double xV; //y轴方向上的速度 private double yV; //该粒子找到的历史最优解 private double pBest; //该粒子找到的历史最优解对应的x和y private double bestX; private double bestY; public Particle(double xMin, double xMax, double yMin, double yMax, double xV, double yV, Random random) { this.xMin = xMin; this.xMax = xMax; this.yMin = yMin; this.yMax = yMax; this.xV = xV; this.yV = yV; //初始化粒子信息 this.initParticle(random); } /** * 初始化粒子信息 * 即初始化位置 */ public void initParticle(Random random) { this.x = random.nextDouble() * (this.xMax - this.xMin) + this.xMin; this.y = random.nextDouble() * (this.yMax - this.yMin) + this.yMin; // System.out.println("this.x:" + this.x + "," + "this.y:" + this.y); } 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 getxMin() { return xMin; } public void setxMin(double xMin) { this.xMin = xMin; } public double getxMax() { return xMax; } public void setxMax(double xMax) { this.xMax = xMax; } public double getyMin() { return yMin; } public void setyMin(double yMin) { this.yMin = yMin; } public double getyMax() { return yMax; } public void setyMax(double yMax) { this.yMax = yMax; } public double getxV() { return xV; } public void setxV(double xV) { this.xV = xV; } public double getyV() { return yV; } public void setyV(double yV) { this.yV = yV; } public double getpBest() { return pBest; } public void setpBest(double pBest) { this.pBest = pBest; } public double getBestX() { return bestX; } public void setBestX(double bestX) { this.bestX = bestX; } public double getBestY() { return bestY; } public void setBestY(double bestY) { this.bestY = bestY; } } }
测试
package com.dam.heuristic.pso.test; public class PsoMainRun { public static void main(String[] args) { PsoApi psoApi = new PsoApi(100, 2, 2, 3, 0.9, 1000); psoApi.solve(); } }
最优目标函数值:8.000000 最优x:8.000000 最优y:6.000000 求解时间:136ms
画图
package com.dam.heuristic.pso.test; import com.dam.heuristic.vns.test.VnsApi; 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; import java.io.File; import java.io.FileInputStream; import java.util.Arrays; public class PsoPaint extends Application { //当前的时间轴 private Timeline nowTimeline; //绘图位置坐标 private double[][][] positionArr; public static void main(String[] args) { launch(args); } @Override public void start(Stage primaryStage) throws Exception { 调用算法获取绘图数据 PsoApi psoApi = new PsoApi(500, 2, 2, 3, 0.9, 6000); this.positionArr = psoApi.solve(); 画图 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: #ecf1c3;"); 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, 0.05); 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[0].length; i++) { // System.out.println(Arrays.toString(this.positionArr[99][i])); // } 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, double speed) { for (int i = 0; i < this.positionArr[0].length; i++) { int finalI = i; KeyFrame keyFrame = new KeyFrame(Duration.seconds(i * speed), event -> drawAllCircle(paintBrush, finalI)); nowTimeline.getKeyFrames().add(keyFrame); } } }
粒子群的收敛过程可以参考下面的视频,刚开始时,粒子被随机放在x和y坐标都属于[-1000,1000]的任意位置,随着迭代次数的增加,粒子慢慢靠拢在一次,直到最后收敛于一个点。
Matlab作图
上面的图实在是太丑了,也是小编的JavaFx功底不咋行,下面改用Matlab进行绘图。
Java导出Excel数据
package com.dam.heuristic.pso.test; import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.XSSFCell; import org.apache.poi.xssf.usermodel.XSSFCellStyle; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; public class PsoMainRun { public static void main(String[] args) { PsoApi psoApi = new PsoApi(500, 2, 2, 1, 0.5, 1000); double[][][] position = psoApi.solve(); //创建WorkBook Workbook workbook = new XSSFWorkbook(); Sheet xData = workbook.createSheet("xData"); Sheet yData = workbook.createSheet("yData"); //获取每一代,每一个粒子的x坐标 CellStyle cellStyle = workbook.createCellStyle(); //设置居中 cellStyle.setAlignment(XSSFCellStyle.ALIGN_CENTER); for (int i = 0; i < position.length; i++) { Row xRow = xData.createRow(i); Row yRow = yData.createRow(i); for (int j = 0; j < position[0].length; j++) { Cell xCell = xRow.createCell(j); xCell.setCellValue(position[i][j][0]); //设置数据类型 xCell.setCellType(XSSFCell.CELL_TYPE_NUMERIC); //设置居中 xCell.setCellStyle(cellStyle); Cell yCell = yRow.createCell(j); yCell.setCellValue(position[i][j][1]); //设置数据类型 yCell.setCellType(XSSFCell.CELL_TYPE_NUMERIC); //设置居中 yCell.setCellStyle(cellStyle); } } try { FileOutputStream fileOutputStream = new FileOutputStream(new File("D:\\Desktop\\paintData.xlsx")); workbook.write(fileOutputStream); fileOutputStream.close(); System.out.println("存储文件完成"); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
数据样式
%% 读取数据 clear; clc; % 读取.mat数据 load xData load yData %% 绘制函数图像 figure % 确定画图区间 x = -1000:50:1000; y = -1000:50:1000; [x,y] = meshgrid(x,y); z = x.^2 + y.^2 - x.*y - 10*x - 4*y + 60; % 绘制网格 mesh(x,y,z) % 加上坐标轴的标签 xlabel('x'); ylabel('y'); zlabel('z'); % 冻结屏幕高宽比,使得一个三维对象的旋转不会改变坐标轴的刻度显示 axis vis3d % 不关闭图形,继续在上面画粒子 hold on %% 绘制粒子图形 % 获取行数、列数 [r,c] = size(xData); h=[]; for i = 1:r %获取每代所有粒子的x,y坐标 xRow = xData(i,:); yRow = yData(i,:); zRow = xRow.^2 + yRow.^2 - xRow.*yRow - 10*xRow - 4*yRow + 60; if i==1 % scatter3是绘制三维散点图的函数(这里返回h是为了得到图形的句柄,未来我们对其位置进行更新) h = scatter3(xRow,yRow,zRow,'*r'); set(h,'XData',xRow,'YData',yRow,'ZData',zRow); pause(0.5); else %间隔0.04秒再画下一代 pause(0.04); h.XData = xRow; h.YData = yRow; h.ZData = zRow; end end