数学建模常用算法:人工鱼群算法(AFAS)求解二元函数最小值+限定x,y范围测试【java实现--详细注释+Matlab绘制小鱼游动过程】

简介: 数学建模常用算法:人工鱼群算法(AFAS)求解二元函数最小值+限定x,y范围测试【java实现--详细注释+Matlab绘制小鱼游动过程】

一、代码

鱼类

package com.dam.heuristic.afas.test;
import java.io.Serializable;
import java.util.Random;
/**
 * 人工鱼
 */
public class Fish implements Serializable {
    //记录位置的维度(x,y,z,……)
    private int dimension;
    //记录位置
    private double[] position;
    //鱼的适应度
    private double fitness;
    //位置每个维度坐标的区间
    private double[][] axisScopeArr;
    //目标函数是否为取最大值
    private boolean isGetMax;
    public Fish(int dimension, double[][] axisScopeArr, boolean isGetMax) {
        this.dimension = dimension;
        this.position = new double[this.dimension];
        this.axisScopeArr = axisScopeArr;
        this.isGetMax = isGetMax;
        this.fitness = 0;
    }
    /**
     * 初始化鱼的位置及适应度
     */
    public void initFishMessage() {
        Random random = new Random();
        //初始化位置
        for (int i = 0; i < this.dimension; i++) {
            position[i] = random.nextDouble() * (this.axisScopeArr[i][1] - this.axisScopeArr[i][0]) + this.axisScopeArr[i][0];
        }
        //初始化适应度
        this.fitness = new ObjectFunction().objectFunction(position,this.isGetMax);
    }
    public int getDimension() {
        return dimension;
    }
    public void setDimension(int dimension) {
        this.dimension = dimension;
    }
    public double[] getPosition() {
        return position;
    }
    public void setPosition(double[] position) {
        this.position = position;
    }
    public double getFitness() {
        return fitness;
    }
    public void setFitness(double fitness) {
        this.fitness = fitness;
    }
}


目标函数类


package com.dam.heuristic.afas.test;
public class ObjectFunction {
    /**
     * 目标函数
     * 由于所举例子的目标函数的目标是最小化,适应度则是越大越好,因此这里需要取负值
     *
     * @param position
     * @param isGetMax 是否获取函数的最大值
     * @return
     */
    public double objectFunction(double[] position, boolean isGetMax) {
        //目标:在变量区间范围最小化 y=x^2+y^2-xy-10x-4y+60
        double value = Math.pow(position[0], 2) + Math.pow(position[1], 2) - position[0] * position[1] - 10 * position[0] - 4 * position[1] + 60;
        if (isGetMax == false) {
            return -value;
        } else {
            return value;
        }
    }
}

算法类

package com.dam.heuristic.afas.test;
import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
public class AfasApi {
    //鱼群中鱼的数量
    private int fishNum;
    //尝试次数,该数越大,鱼的觅食能力越强,收敛速度越快
    private int tryNum;
    //维度
    private int dimension;
    //鱼的移动步长
    private double stepLen;
    //拥挤度因子(delta=1/(random.nextDouble()*maxFishNum):maxFishNum为鱼的视野范围内最大的鱼的总数量)
    //当delta越大时,拥挤程度越小,鱼更难聚在一起,因此容易跳出局部最优,但是也会让其他鱼不想过来,造成找到的解不能精确逼近最优解
    private double delta;
    //鱼的视野范围
    private double visual;
    //迭代次数
    private int genNum;
    //目标函数是否为取最大值
    private boolean isGetMax;
    //位置每个维度坐标的区间
    private double[][] axisScopeArr;
    public AfasApi(int fishNum, int tryNum, double stepLen, double delta, double visual, int genNum, boolean isGetMax, double[][] axisScopeArr) {
        this.fishNum = fishNum;
        this.tryNum = tryNum;
        this.dimension = axisScopeArr[0].length;
        this.stepLen = stepLen;
        this.delta = delta;
        this.visual = visual;
        this.genNum = genNum;
        this.isGetMax = isGetMax;
        this.axisScopeArr = axisScopeArr;
    }
    public double[][][] solve() {
        变量声明
        long start = System.currentTimeMillis();
        //鱼群
        Fish[] fishArr;
        //最好的鱼
        Fish bestFish;
        //存储觅食、聚群、追尾所找到的最优鱼
        Fish[] nextFishArr;
        //随机数工具类
        Random random = new Random();
        //目标函数类
        ObjectFunction objectFunction = new ObjectFunction();
        //存储每一代鱼所在位置
        double[][][] positionArr = new double[this.genNum][this.fishNum][this.dimension];
        初始化鱼群
        //初始化最优鱼
        bestFish = new Fish(this.dimension, this.axisScopeArr, isGetMax);
        //初始化最优鱼的适应度为很小的值,说明当前最优鱼不咋的
        bestFish.setFitness(-Double.MAX_VALUE);
        fishArr = new Fish[this.fishNum];
        for (int i = 0; i < this.fishNum; i++) {
            //创建鱼
            fishArr[i] = new Fish(this.dimension, this.axisScopeArr, isGetMax);
            //初始化鱼的位置及适应度
            fishArr[i].initFishMessage();
            //更新初始最优鱼
            if (fishArr[i].getFitness() > bestFish.getFitness()) {
                bestFish = (Fish) deepClone(fishArr[i]);
            }
        }
        //初始化(觅食、聚群、追尾)所找到的最优鱼数组,三种方式
        nextFishArr = new Fish[3];
        求解
        for (int t = 0; t < this.genNum; t++) {
            for (int i = 0; i < this.fishNum; i++) {
//                System.out.println("找位置--------------------------------------------------------------------------");
                //自己出门觅觅食,万一找到什么好地方就爽了
                this.foraging(fishArr, nextFishArr, i, random, objectFunction, 0);
                //找到第i条鱼视野范围内的小鱼群
                List<Fish> fishNearingFishIList = this.findFishArrNearFishI(i, fishArr);
                //如果鱼群的中间吃的比较多,又不太挤,就跟着去看看
                this.cluster(fishArr, nextFishArr, i, fishNearingFishIList, random, objectFunction);
                //看一下哪条小鱼仔吃得比较好,旁边的地方又不太挤,就跟着去看看
                this.follow(fishArr, nextFishArr, i, fishNearingFishIList, random, objectFunction);
                //汇总一下信息,根据所找到的位置选择出最好的位置,再决定去不去
                this.upDateFishPositionAndBestFish(fishArr, nextFishArr, i, bestFish);
                positionArr[t][i] = fishArr[i].getPosition();
            }
        }
        //如果函数求的是最小值,要取负值,因为算法是根据将目标函数取负号并以目标为求最大值来进行求解
        double fitness = this.isGetMax == false ? -bestFish.getFitness() : bestFish.getFitness();
        System.out.println("最优目标函数值:" + String.format("%.6f", fitness));
        System.out.println("最优位置:" + Arrays.toString(bestFish.getPosition()));
        System.out.println("求解时间:" + (System.currentTimeMillis() - start) + "ms");
        return positionArr;
    }
    /**
     * 觅食
     *
     * @param index 由于聚群,追尾时找不到好位置的话,也要觅食,用index来控制所找位置的填充参数
     */
    private void foraging(Fish[] fishArr, Fish[] nextFishArr, int i, Random random, ObjectFunction objectFunction, int index) {
        //该鱼的下一可能位置
        double[] nextPositionOfThisFish = new double[this.dimension];
        //存储所寻找的最优位置
        Fish bestPositionOfThisFish = new Fish(this.dimension, this.axisScopeArr, isGetMax);
        bestPositionOfThisFish.setFitness(-Double.MAX_VALUE);
        // 进行this.tryNum次尝试觅食,选择一个比较好的位置
        for (int j = 0; j < this.tryNum; j++) {
            //让鱼在视野范围内随意走动一下
            for (int k = 0; k < this.dimension; k++) {
                nextPositionOfThisFish[k] = fishArr[i].getPosition()[k] + (2 * (random.nextDouble()) - 1) * this.visual;
            }
            //修正一下位置
            this.fixPosition(nextPositionOfThisFish, this.axisScopeArr);
            //计算一下所走动到的位置的食物如何,即适应度如何
            double nextPositionFitness = objectFunction.objectFunction(nextPositionOfThisFish, this.isGetMax);
            if (nextPositionFitness > fishArr[i].getFitness()) {
                double distance = this.getDistanceOfTwoPositions(fishArr[i].getPosition(), nextPositionOfThisFish);
                for (int k = 0; k < this.dimension; k++) {
                    //fishArr[i].getPosition()[k]:鱼的当前坐标
                    //(nextPositionOfThisFish.getPosition()[k] - fishArr[i].getPosition()[k]) / distance:该方向走的步长/两位置距离(可以结合直角三角形的投影关系进行理解)
                    //this.stepLen * random.nextDouble():虽然一步可以走这么远,但是鱼不一定要竭尽全力地游,* random.nextDouble()可以让鱼在自己的大长腿范围内随机走一段距离
                    bestPositionOfThisFish.getPosition()[k] =
                            fishArr[i].getPosition()[k] +
                                    ((nextPositionOfThisFish[k] - fishArr[i].getPosition()[k]) / distance)
                                            * this.stepLen * random.nextDouble();
                }
                //设置新位置的适应度
                bestPositionOfThisFish.setFitness(objectFunction.objectFunction(bestPositionOfThisFish.getPosition(), this.isGetMax));
            }
        }
        //存储觅食找到的最优位置
        nextFishArr[index] = bestPositionOfThisFish;
//        System.out.println("通过觅食找到更优位置" + bestPositionOfThisFish.getFitness());
    }
    /**
     * 聚群
     *
     * @param fishArr
     * @param nextFishArr
     * @param i
     * @param random
     * @param objectFunction
     */
    private void cluster(Fish[] fishArr, Fish[] nextFishArr, int i, List<Fish> fishNearingFishIList, Random random, ObjectFunction objectFunction) {
        //存储所寻找的最优位置
        Fish bestPositionOfThisFish = new Fish(this.dimension, this.axisScopeArr, this.isGetMax);
        bestPositionOfThisFish.setFitness(-Double.MAX_VALUE);
        //鱼群的中心位置
        double[] centerPosition = new double[this.dimension];
        for (int k = 0; k < this.dimension; k++) {
            centerPosition[k] = 0;
        }
        if (fishNearingFishIList.size() > 0) {
            //计算小鱼群的中心位置
            for (int j = 0; j < fishNearingFishIList.size(); j++) {
                for (int k = 0; k < this.dimension; ++k) {
                    centerPosition[k] += fishNearingFishIList.get(j).getPosition()[k];
                }
            }
            //人工鱼的中心位置(各维度位置的平均值)
            for (int k = 0; k < this.dimension; k++) {
                centerPosition[k] /= fishNearingFishIList.size();
            }
            //修正一下位置
            this.fixPosition(centerPosition, this.axisScopeArr);
            //求中心位置的适应度
            double centerPositionFitness = objectFunction.objectFunction(centerPosition, this.isGetMax);
            //求中心位置离第i条鱼的距离
            double distance = this.getDistanceOfTwoPositions(fishArr[i].getPosition(), centerPosition);
            //centerPositionFitness / fishNearingFishIList.size() > this.delta * fishArr[i].getFitness():看一下要去的地方性价比怎么样
            //就算那里伙食很好,住宿条件不太行(鱼太多,贼拥挤),小鱼鱼也懒得去
            if (centerPositionFitness > fishArr[i].getFitness()
                    && centerPositionFitness / fishNearingFishIList.size() > this.delta * fishArr[i].getFitness()) {
                for (int k = 0; k < this.dimension; k++) {
                    bestPositionOfThisFish.getPosition()[k] = fishArr[i].getPosition()[k] +
                            ((centerPosition[k] - fishArr[i].getPosition()[k]) / distance)
                                    * this.stepLen * random.nextDouble();
                }
                //设置新位置的适应度
                bestPositionOfThisFish.setFitness(objectFunction.objectFunction(bestPositionOfThisFish.getPosition(), this.isGetMax));
                //存储聚群找到的最优位置
                nextFishArr[1] = bestPositionOfThisFish;
//                System.out.println("通过聚群找到更优位置" + bestPositionOfThisFish.getFitness());
            } else {
                //找到的中心位置不咋地,不如自己出去觅食
                this.foraging(fishArr, nextFishArr, i, random, objectFunction, 1);
            }
        } else {
            //附近找不到鱼群,只能继续觅食了
            this.foraging(fishArr, nextFishArr, i, random, objectFunction, 1);
        }
    }
    /**
     * 追尾行为
     *
     * @param fishArr
     * @param nextFishArr
     * @param i
     * @param random
     * @param objectFunction
     * @throws IOException
     */
    private void follow(Fish[] fishArr, Fish[] nextFishArr, int i, List<Fish> fishNearingFishIList, Random random, ObjectFunction objectFunction) {
        //存储所寻找的最优位置
        Fish bestPositionOfThisFish = new Fish(this.dimension, this.axisScopeArr, isGetMax);
        bestPositionOfThisFish.setFitness(-Double.MAX_VALUE);
        if (fishNearingFishIList.size() > 0) {
            //存储最优邻居鱼
            Fish bestNeighbourFish = new Fish(this.dimension, this.axisScopeArr, isGetMax);
            //存储最优邻居鱼对应的索引
            int bestNeighbourFishIndex = i;
            //找邻居中适应度最高的那条鱼
            for (int j = 0; j < fishNearingFishIList.size(); j++) {
                if (fishNearingFishIList.get(j).getFitness() > bestNeighbourFish.getFitness()) {
                    bestNeighbourFish.setPosition(fishNearingFishIList.get(j).getPosition().clone());
                    bestNeighbourFishIndex = j;
                }
            }
            //修正一下坐标
            this.fixPosition(bestNeighbourFish.getPosition(), this.axisScopeArr);
            if (bestNeighbourFish.getFitness() < fishArr[i].getFitness()) {
                //邻居鱼不咋地,还是自己去觅食吧
                this.foraging(fishArr, nextFishArr, i, random, objectFunction, 2);
            } else {
                //找到最优邻居鱼旁边的鱼群
                List<Fish> fishArrNearBestNeighbourFish = this.findFishArrNearFishI(bestNeighbourFishIndex, fishArr);
                if (fishArrNearBestNeighbourFish.size() > 0 &&
                        bestNeighbourFish.getFitness() / fishArrNearBestNeighbourFish.size() > this.delta * fishArr[i].getFitness()) {
                    //求第i条鱼和最优邻居鱼之间的距离
                    double distance = this.getDistanceOfTwoPositions(fishArr[i].getPosition(), bestNeighbourFish.getPosition());
                    for (int k = 0; k < this.dimension; k++) {
                        bestPositionOfThisFish.getPosition()[k] = fishArr[i].getPosition()[k]
                                + ((bestNeighbourFish.getPosition()[k] - fishArr[i].getPosition()[k]) / distance)
                                * this.stepLen * random.nextDouble();
                    }
                    //设置新位置的适应度
                    bestPositionOfThisFish.setFitness(objectFunction.objectFunction(bestPositionOfThisFish.getPosition(), this.isGetMax));
                    //存储追尾找到的最优位置
                    nextFishArr[2] = bestPositionOfThisFish;
//                    System.out.println("通过追尾找到更优位置" + bestPositionOfThisFish.getFitness());
                } else {
                    this.foraging(fishArr, nextFishArr, i, random, objectFunction, 2);
                }
            }
        } else {
            this.foraging(fishArr, nextFishArr, i, random, objectFunction, 2);
        }
    }
    /**
     * 更新第i条鱼的位置,同时更新最优的鱼
     *
     * @param fishArr
     * @param nextFishArr
     * @param i
     * @param bestFish
     */
    private void upDateFishPositionAndBestFish(Fish[] fishArr, Fish[] nextFishArr, int i, Fish bestFish) {
        for (int j = 0; j < nextFishArr.length; j++) {
            if (nextFishArr[j].getFitness() > fishArr[i].getFitness()) {
                fishArr[i].setPosition(nextFishArr[j].getPosition());
                fishArr[i].setFitness(nextFishArr[j].getFitness());
            }
            if (nextFishArr[j].getFitness() > bestFish.getFitness()) {
//                System.out.println("更新最优解");
                bestFish.setPosition(nextFishArr[j].getPosition());
                bestFish.setFitness(nextFishArr[j].getFitness());
            }
        }
    }
    /**
     * 找一下附近有哪些鱼鱼
     *
     * @param i
     * @return
     */
    private List<Fish> findFishArrNearFishI(int i, Fish[] fishArr) {
        List<Fish> fishNearingFishIList = new ArrayList<>();
        for (int j = 0; j < this.fishNum; j++) {
            if (this.getDistanceOfTwoPositions(fishArr[i].getPosition(), fishArr[j].getPosition()) < this.visual) {
                fishNearingFishIList.add(fishArr[j]);
            }
        }
        return fishNearingFishIList;
    }
    /**
     * 获取两个位置之间的距离
     *
     * @param position1
     * @param position2
     */
    private double getDistanceOfTwoPositions(double[] position1, double[] position2) {
        double distance = 0;
        for (int i = 0; i < position1.length; i++) {
            distance += Math.pow((position1[i] - position2[i]), 2);
        }
        return Math.sqrt(distance);
    }
    /**
     * 对位置进行限制,限制再变量区间内
     *
     * @param position
     */
    private void fixPosition(double[] position, double[][] axisScopeArr) {
        for (int i = 0; i < position.length; i++) {
            //让坐标处于区间内
            if (position[i] > axisScopeArr[i][1]) {
                position[i] = axisScopeArr[i][1];
            } else if (position[i] < axisScopeArr[i][0]) {
                position[i] = axisScopeArr[i][0];
            }
        }
    }
    /**
     * 深拷贝对象
     *
     * @param srcObject
     * @return
     */
    private Object deepClone(Object srcObject) {
        ByteArrayOutputStream bos = null;
        ObjectOutputStream oos = null;
        ByteArrayInputStream bis = null;
        ObjectInputStream ois = null;
        Object result = null;
        try {
            bos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bos);
            //将对象写到流里
            oos.writeObject(srcObject);
            //从流里读回来
            bis = new ByteArrayInputStream(bos.toByteArray());
            ois = new ObjectInputStream(bis);
            result = ois.readObject();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                bos.close();
                oos.close();
                bis.close();
                ois.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return result;
    }
}


二、测试

package com.dam.heuristic.afas.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 AfasMainRun {
    public static void main(String[] args) {
        //变量的区间
        double[][] axisScopeArr = {
                {-1000, 1000},
                {-1000, 1000}
        };
        AfasApi afasApi = new AfasApi(50, 50, 5, 0.2, 5, 1000, false, axisScopeArr);
        double[][][] position = afasApi.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();
        }
    }
}


最优目标函数值:8.000012
最优位置:[8.00392710826662, 6.001480250477476]
求解时间:283ms


三、Matlab绘图

具体绘图方法可以参照文章数学建模常用算法:粒子群算法(PSO)求解二元函数最小值+限定x,y范围测试【java实现–详细注释+Matlab绘制粒子群飞行过程】

ac192cff74d346198dd2101a4b32499d.gif


目录
相关文章
|
2月前
|
传感器 算法 计算机视觉
基于肤色模型和中值滤波的手部检测算法FPGA实现,包括tb测试文件和MATLAB辅助验证
该内容是关于一个基于肤色模型和中值滤波的手部检测算法的描述,包括算法的运行效果图和所使用的软件版本(matlab2022a, vivado2019.2)。算法分为肤色分割和中值滤波两步,其中肤色模型在YCbCr色彩空间定义,中值滤波用于去除噪声。提供了一段核心程序代码,用于处理图像数据并在FPGA上实现。最终,检测结果输出到&quot;hand.txt&quot;文件。
|
10天前
|
机器学习/深度学习 人工智能 算法
基于DCT和扩频的音频水印嵌入提取算法matlab仿真
本文介绍了结合DCT和扩频技术的音频水印算法,用于在不降低音质的情况下嵌入版权信息。在matlab2022a中实现,算法利用DCT进行频域处理,通过扩频增强水印的隐蔽性和抗攻击性。核心程序展示了水印的嵌入与提取过程,包括DCT变换、水印扩频及反变换步骤。该方法有效且专业,未来研究将侧重于提高实用性和安全性。
|
4天前
|
数据采集 算法 数据可视化
MATLAB、R用改进Fuzzy C-means模糊C均值聚类算法的微博用户特征调研数据聚类研究
MATLAB、R用改进Fuzzy C-means模糊C均值聚类算法的微博用户特征调研数据聚类研究
11 1
|
14天前
|
文字识别 算法 计算机视觉
图像倾斜校正算法的MATLAB实现:图像倾斜角检测及校正
图像倾斜校正算法的MATLAB实现:图像倾斜角检测及校正
21 0
|
15天前
|
机器学习/深度学习 人工智能 数据可视化
西安石油大学数学建模校赛培训(2)matlab的使用
西安石油大学数学建模校赛培训(2)matlab的使用
42 0
|
17天前
|
机器学习/深度学习 算法
【MATLAB】GA_ELM神经网络时序预测算法
【MATLAB】GA_ELM神经网络时序预测算法
288 9
|
22天前
|
算法 安全 Java
java代码 实现AES_CMAC 算法测试
该代码实现了一个AES-CMAC算法的简单测试,使用Bouncy Castle作为安全提供者。静态变量K定义了固定密钥。`Aes_Cmac`函数接受密钥和消息,返回AES-CMAC生成的MAC值。在`main`方法中,程序对给定的消息进行AES-CMAC加密,然后模拟接收ECU的加密结果并进行比较。如果两者匹配,输出&quot;验证成功&quot;,否则输出&quot;验证失败&quot;。辅助方法包括将字节转为16进制字符串和将16进制字符串转为字节。
|
2月前
|
算法
m基于log-MPA检测算法的SCMA通信链路matlab误码率仿真
MATLAB 2022a仿真实现了稀疏码多址接入(SCMA)算法,该算法利用码本稀疏性实现多用户高效接入。每个用户从码本中选取码字发送,接收端采用Log-MPA算法进行多用户检测。由于MAP检测计算复杂度高,故采用Log-MPA降低复杂性。仿真展示了不同迭代次数(1, 5, 10, 30)对误码率(BER)的影响,通过比较各次迭代的BER曲线,研究算法性能与迭代次数的关系。
25 0
|
1天前
|
Java
【Java探索之旅】我与Java的初相识(完):注释,标识符,关键字
【Java探索之旅】我与Java的初相识(完):注释,标识符,关键字
5 0
|
Java 数据安全/隐私保护 C++
Java - 注释、标识符、关键字
Java - 注释、标识符、关键字
82 0
Java - 注释、标识符、关键字