【智能算法】AFSA人工鱼群算法求解无约束多元函数最值(Java代码实现)

本文涉及的产品
传统型负载均衡 CLB,每月750个小时 15LCU
应用型负载均衡 ALB,每月750个小时 15LCU
EMR Serverless StarRocks,5000CU*H 48000GB*H
简介: 【智能算法】AFSA人工鱼群算法求解无约束多元函数最值(Java代码实现)

@[toc]


前言

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

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

优化目标

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

求解结果

变量取值为:[7.996330803705318, 6.022624753356061]
最优解为:8.000608357127064

迭代结果可视化

在这里插入图片描述

算法流程

在这里插入图片描述

Java代码

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

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

    // 鱼对象
    class Fish {
        // 当前鱼的坐标(自变量数组)
        double[] curVars;
        // 当前自变量对应的目标函数值
        double curObjValue;
        // 适应度(解决最小化问题,所以适应度为目标函数值的倒数)
        double fit;
        // 全参构造
        public Fish(double[] curVars, double curObjValue, double fit) {
            this.curVars = curVars;
            this.curObjValue = curObjValue;
            this.fit = fit;
        }
    }

    // 算法参数
    // 变量个数
    int varNum = 2;
    // 最大迭代次数
    int maxGen = 500;
    // 鱼群中鱼的个数
    int fishNum = 300;
    // 每次的最大觅食次数
    int preyCnt = 20;
    // 鱼的最大感知距离
    double visual = 50;
    // 可接受的拥挤程度
    double crowdedRate = 0.6;
    // 步长数组(各个维度的步长)
    double[] stepArr = new double[]{6, 6};
    // 变量的上下界
    double[] ub = new double[]{1000, 1000};
    double[] lb = new double[]{-1000, -1000};
    // 随机数对象
    Random random = new Random();
    // 鱼群
    Fish[] fishes;
    // 最佳的鱼
    Fish bestFish;
    // 记录迭代过程
    public double[][][] positionArr;
    // 当前记录的行数
    int r;

    // 求解主函数
    public void solve() {
        // 初始化鱼群
        initFishes();
        // 开始迭代
        for (int t = 0; t < maxGen; t++) {
            // 聚群行为
            AFSwarm();
            report();
            // 追尾
            AFFollow();
            report();
            // 觅食
            AFPrey();
            report();
        }
        for (Fish fish : fishes) {
            if (fish.fit > bestFish.fit) {
                bestFish = fish;
            }
        }
        // 输出最好的结果
        System.out.println("变量取值为:" + Arrays.toString(bestFish.curVars));
        System.out.println("最优解为:" + bestFish.curObjValue);
    }

    // 移动行为(在鱼群的移动过程中,当一条鱼或几条鱼找到食物时,附近的伙伴会迅速移动并到达食物)
    void AFFollow() {
        for (int i = 0; i < fishes.length; i++) {
            // 探索视野内的鱼有多少条,并找到最大适应值的鱼
            int friendCount = 0;
            Fish maxFish = copyFish(fishes[i]);
            for (int j = 0; j < fishes.length; j++) {
                if (i != j && getDistance(fishes[i], fishes[j]) <= visual) {
                    friendCount++;
                    if (fishes[j].fit > maxFish.fit) {
                        maxFish = copyFish(fishes[j]);
                    }
                }
            }
            // 如果周围不拥挤,且两个鱼的目标函数不相等
            if ((double) friendCount / fishNum <= crowdedRate && Math.abs(maxFish.curObjValue - fishes[i].curObjValue) > 0.0000001) {
                // 尝试往最大适应值位置游
                Fish tempFish = copyFish(fishes[i]);
                double distance = getDistance(maxFish, tempFish);
                for (int m = 0; m < varNum; m++) {
                    double move = (maxFish.curVars[m] - tempFish.curVars[m]) / (distance) * stepArr[m] * random.nextDouble();
                    moveFish(tempFish, m, move);
                }
                tempFish.curObjValue = getObjValue(tempFish.curVars);
                tempFish.fit = 1 / tempFish.curObjValue;
                if (tempFish.fit > fishes[i].fit) {
                    fishes[i] = tempFish;
                } else {
                    // 否则,进行觅食
                    AFPrey(i);
                }
            } else {
                // 否则,进行觅食
                AFPrey(i);
            }
        }
    }

    // 聚群行为(鱼群在移动的过程中会自然地成群聚集,这是一种生活习惯,可以保证群体的存在,避免危险)
    void AFSwarm() {
        for (int i = 0; i < fishes.length; i++) {
            // 探索视野内的鱼有多少条,并计算周围鱼群的中心
            double[] midPoint = new double[varNum];
            int friendCount = 0;
            for (int j = 0; j < fishes.length; j++) {
                if (i != j && getDistance(fishes[i], fishes[j]) <= visual) {
                    friendCount++;
                    for (int m = 0; m < varNum; m++) {
                        midPoint[m] += fishes[j].curVars[m];
                    }
                }
            }
            // 计算中心
            for (int m = 0; m < midPoint.length; m++) {
                midPoint[m] /= friendCount;
            }
            double objValue = getObjValue(midPoint);
            // 获取中心的鱼
            Fish midFish = new Fish(midPoint, objValue, 1 / objValue);
            // 如果中心不拥挤
            if ((double) friendCount / fishNum <= crowdedRate) {
                // 尝试往中心位置游
                Fish tempFish = copyFish(fishes[i]);
                double distance = getDistance(midFish, tempFish);
                for (int m = 0; m < varNum; m++) {
                    double move = (midPoint[m] - tempFish.curVars[m]) / (distance) * stepArr[m] * random.nextDouble();
                    moveFish(tempFish, m, move);
                }
                tempFish.curObjValue = getObjValue(tempFish.curVars);
                tempFish.fit = 1 / tempFish.curObjValue;
                if (tempFish.fit > fishes[i].fit) {
                    fishes[i] = tempFish;
                } else {
                    // 否则,进行觅食
                    AFPrey(i);
                }
            } else {
                // 否则,进行觅食
                AFPrey(i);
            }
        }
    }

    // 觅食行为(这是鱼类对食物的基本生物学行为。一般来说,鱼通过视觉感知水中食物的浓度来决定向哪移动,然后选择移动的方向)
    void AFPrey(int i) {
        // 尝试觅食preyCnt次
        for (int j = 0; j < preyCnt; j++) {
            Fish tempFish = copyFish(fishes[i]);
            for (int m = 0; m < varNum; m++) {
                moveFish(tempFish, m, visual * (random.nextDouble() - 0.5) * 2);
            }
            tempFish.curObjValue = getObjValue(tempFish.curVars);
            tempFish.fit = 1 / tempFish.curObjValue;
            if (tempFish.fit > fishes[i].fit) {
                double distance = getDistance(tempFish, fishes[i]);
                // 觅食成功,向成功方向移动
                for (int m = 0; m < varNum; m++) {
                    double move = (tempFish.curVars[m] - fishes[i].curVars[m]) / (distance) * stepArr[m] * random.nextDouble();
                    moveFish(fishes[i], m, move);
                }
                fishes[i].curObjValue = getObjValue(fishes[i].curVars);
                fishes[i].fit = 1 / fishes[i].curObjValue;
            } else {
                // 觅食失败,随机移动
                for (int m = 0; m < varNum; m++) {
                    double move = stepArr[m] * (random.nextDouble() - 0.5) * 2;
                    moveFish(tempFish, m, move);
                }
                tempFish.curObjValue = getObjValue(tempFish.curVars);
                tempFish.fit = 1 / tempFish.curObjValue;
                if(tempFish.fit > fishes[i].fit){
                    fishes[i] = tempFish;
                }
            }
        }
    }
    void AFPrey(){
        for (int i = 0; i < fishes.length; i++) {
            AFPrey(i);
        }
    }

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

    // 初始化鱼群
    private void initFishes() {
        positionArr = new double[3*maxGen][fishNum][varNum];
        fishes = new Fish[fishNum];
        for (int i = 0; i < fishNum; i++) {
            fishes[i] = getRandomFish();
            if (i == 0 || bestFish.fit < fishes[i].fit) {
                bestFish = copyFish(fishes[i]);
            }
        }
    }

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

    // 求两条鱼之间的距离
    double getDistance(Fish f1, Fish 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);
    }

    // 求两点之间的距离
    double getDistance(double p1, double p2) {
        return Math.sqrt(Math.pow(p1 - p2, 2));
    }

    // 获取一个随机生成的鱼
    Fish getRandomFish() {
        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 Fish(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;
    }

    // 复制鱼
    Fish copyFish(Fish old) {
        return new Fish(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 {

        // 调用算法获取绘图数据
        AFSA_Solve solver = new AFSA_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);
        }
    }

}
相关实践学习
SLB负载均衡实践
本场景通过使用阿里云负载均衡 SLB 以及对负载均衡 SLB 后端服务器 ECS 的权重进行修改,快速解决服务器响应速度慢的问题
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
目录
相关文章
|
4天前
|
算法
分享一些提高二叉树遍历算法效率的代码示例
这只是简单的示例代码,实际应用中可能还需要根据具体需求进行更多的优化和处理。你可以根据自己的需求对代码进行修改和扩展。
|
15天前
|
SQL 监控 Java
Java连接池技术的最新发展,包括高性能与低延迟、智能化管理与监控、扩展性与兼容性等方面
本文探讨了Java连接池技术的最新发展,包括高性能与低延迟、智能化管理与监控、扩展性与兼容性等方面。同时,结合最佳实践,介绍了如何选择合适的连接池库、合理配置参数、使用监控工具及优化数据库操作,以实现高效稳定的数据库访问。示例代码展示了如何使用HikariCP连接池。
9 2
|
16天前
|
算法 测试技术 开发者
在Python开发中,性能优化和代码审查至关重要。性能优化通过改进代码结构和算法提高程序运行速度,减少资源消耗
在Python开发中,性能优化和代码审查至关重要。性能优化通过改进代码结构和算法提高程序运行速度,减少资源消耗;代码审查通过检查源代码发现潜在问题,提高代码质量和团队协作效率。本文介绍了一些实用的技巧和工具,帮助开发者提升开发效率。
18 3
|
15天前
|
分布式计算 Java 开发工具
阿里云MaxCompute-XGBoost on Spark 极限梯度提升算法的分布式训练与模型持久化oss的实现与代码浅析
本文介绍了XGBoost在MaxCompute+OSS架构下模型持久化遇到的问题及其解决方案。首先简要介绍了XGBoost的特点和应用场景,随后详细描述了客户在将XGBoost on Spark任务从HDFS迁移到OSS时遇到的异常情况。通过分析异常堆栈和源代码,发现使用的`nativeBooster.saveModel`方法不支持OSS路径,而使用`write.overwrite().save`方法则能成功保存模型。最后提供了完整的Scala代码示例、Maven配置和提交命令,帮助用户顺利迁移模型存储路径。
|
25天前
|
Java
Java Set以其“不重复”的特性,为我们提供了一个高效、简洁的处理唯一性约束数据的方式。
【10月更文挑战第16天】在Java编程中,Set接口确保集合中没有重复元素,每个元素都是独一无二的。HashSet基于哈希表实现,提供高效的添加、删除和查找操作;TreeSet则基于红黑树实现,不仅去重还能自动排序。通过这两个实现类,我们可以轻松处理需要唯一性约束的数据,提升代码质量和效率。
34 2
|
28天前
|
存储 缓存 算法
如何通过优化算法和代码结构来提升易语言程序的执行效率?
如何通过优化算法和代码结构来提升易语言程序的执行效率?
|
27天前
|
算法 Java 数据处理
从HashSet到TreeSet,Java集合框架中的Set接口及其实现类以其独特的“不重复性”要求,彻底改变了处理唯一性约束数据的方式。
【10月更文挑战第14天】从HashSet到TreeSet,Java集合框架中的Set接口及其实现类以其独特的“不重复性”要求,彻底改变了处理唯一性约束数据的方式。本文深入探讨Set的核心理念,并通过示例代码展示了HashSet和TreeSet的特点和应用场景。
17 2
|
28天前
|
搜索推荐
插入排序算法的讲解和代码
【10月更文挑战第12天】插入排序是一种基础的排序算法,理解和掌握它对于学习其他排序算法以及数据结构都具有重要意义。你可以通过实际操作和分析,进一步深入了解插入排序的特点和应用场景,以便在实际编程中更好地运用它。
|
1月前
|
Java
让星星⭐月亮告诉你,jdk1.8 Java函数式编程示例:Lambda函数/方法引用/4种内建函数式接口(功能性-/消费型/供给型/断言型)
本示例展示了Java中函数式接口的使用,包括自定义和内置的函数式接口。通过方法引用,实现对字符串操作如转换大写、数值转换等,并演示了Function、Consumer、Supplier及Predicate四种主要内置函数式接口的应用。
24 1
|
2月前
|
Java
java基础(11)函数重载以及函数递归求和
Java支持函数重载,即在同一个类中可以声明多个同名方法,只要它们的参数类型和个数不同。函数重载与修饰符、返回值无关,但与参数的类型、个数、顺序有关。此外,文中还展示了如何使用递归方法`sum`来计算两个数之间的和,递归的终止条件是当第一个参数大于第二个参数时。
32 1
java基础(11)函数重载以及函数递归求和