小白学习群智能优化算法---遗传算法的简单实现

这个代码是用来实现x的平方在-10到10的定义域内的最大值
染色体中每个基因就是一个x
这里染色体设计比较简单 大部分会采用二进制编码的方式进行染色体设计
结果应该趋近100

适应度函数接口

public interface FitnessFunction {
    double calculateFitness(double [] individual);
}

适应度函数

public class Fitness implements FitnessFunction {

    //计算适应度
    @Override
    public double calculateFitness(double[] individual) {
        double sum = 0;
        double result = 0;
        for(double gene: individual){
            sum = gene * gene;//individual是染色体
            if(sum > result){
                result = sum;
            }
        }
        //返回的是基因平方中最大的那个
        return result;
    }
}

遗传算法的主要操作

public class GA {
    /**
     * 初始化种群
     * @param populationSize 种群大小
     * @param chromosomeLength 染色体长度
     * @param bounds 边界数组
     * @return 初始化的种群
     */
    public double[][] initializePopulation(int populationSize,int chromosomeLength,double[] bounds){
        //二维数组保存种群  第一维是个体 第二维是染色体
        double[][] population = new double[populationSize][chromosomeLength];
        Random rand = new Random();
        //对每个个体的染色体赋值
        for (int i = 0;i < populationSize;i++){
            for(int j = 0;j < chromosomeLength;j++){
                population[i][j] = bounds[0] + (bounds[1] - bounds[0]) * rand.nextDouble();//随机生成染色体值 不超过边界
            }
        }
        //返回初始种群
        return population;
    }

    /**
     * 计算适应度
     * @param population 种群
     * @param fitnessFunction 具体的适应度计算方法
     * @return 返回适应度数组  数组中保存的是传入种群的各个体适应度集合
     */
    public double[] evaluatePopulation(double[][] population,FitnessFunction fitnessFunction){
        //创建适应度数组
        double[] fitnessValues = new double[population.length];
        for (int i = 0;i < population.length;i++){
            fitnessValues[i] = fitnessFunction.calculateFitness(population[i]);//调用适应度计算方法计算适应度
        }
        return fitnessValues;
    }

    /**
     * 计算选择概率
     * @param fitnessValues 适应度数组
     * @return 一个选择概率数组  表示每个适应度对应的选择概率
     */
    public double[] caculateSelectionProbabilities(double[] fitnessValues){
        double sumFitness = 0;
        for (double fitness:fitnessValues){
            sumFitness += fitness;
        }
        double[] probabilities = new double[fitnessValues.length];//选择概率数组
        for (int i = 0;i < fitnessValues.length;i++){
            //当前适应度除总适应度作为选择概率
            //适应度越大 选择概率越大
            probabilities[i] = fitnessValues[i] / sumFitness;//计算选择概率
        }
        return probabilities;
    }

    /**
     * 计算累加选择概率  轮盘赌算法
     * @param probabilities 选择概率数组
     * @return 累计概率数组
     */
    public double[] caculateCumulativeProbabilities(double[] probabilities){
        //创建累计概率数组
        double[] cumulativeProbabilities = new double[probabilities.length];
        //第一项就是选择概率
        cumulativeProbabilities[0] = probabilities[0];
        for (int i = 1;i < probabilities.length;i++){
            //累计概率=上一个累计概率+当前选择概率
            cumulativeProbabilities[i] = cumulativeProbabilities[i-1] + probabilities[i];
        }
        return cumulativeProbabilities;
    }

    /**
     * 基于轮盘赌的选择
     * @param fitnessValues 适应度数组
     * @return 被选中的个体在种群中的索引数组
     */
    public int[] performSelection(double[] fitnessValues){
        //计算选择概率
        double[] probabilities = caculateSelectionProbabilities(fitnessValues);
        //计算累计概率
        double[] cumulativeProbabilities = caculateCumulativeProbabilities(probabilities);
        //创建选择索引数组
        int[] selectionIndices = new int[fitnessValues.length];
        Random rand = new Random();
        for (int i = 0;i < fitnessValues.length;i++){
            double randomNum = rand.nextDouble();
            for (int j = 0;j < cumulativeProbabilities.length;j++){
                if(randomNum <= cumulativeProbabilities[j]){
                    selectionIndices[i] = j;//被选中的个体在种群中的索引
                    break;
                }
            }
        }
        return selectionIndices;
     }

    /**
     * 选择生成新种群
     * @param population 旧种群
     * @param selectionIndices 选择索引
     * @return
     */
    public double[][] selectPopulation(double[][] population,int[] selectionIndices){
        //创建新的种群
        double[][] selectedPopulation = new double[population.length][population[0].length];
        for (int i = 0;i < selectionIndices.length;i++){
            //selectionIndices[i]]是一个下标 把旧种群的个体赋给新种群
            selectedPopulation[i] = population[selectionIndices[i]];//根据索引选择
        }
        return selectedPopulation;//返回选择生成后的种群
    }

    /**
     * 随机交叉
     * @param population 传入一个初始种群
     * @return 返回一个后代种群
     */
    //随机选择交叉点,通过每两个个体交叉来产生后代种群
    public double[][] performCrossover(double[][] population){
        //创建后代种群
        double[][] offspringPopulation = new double[population.length][population[0].length];
        Random rand = new Random();

        for (int i = 0;i < population.length;i += 2){
            //获取父代个体的索引 每次获取俩
            int parentIndex1 = i;
            int parentIndex2 = (i+1) % population.length;
            //获取父代的染色体序列
            double[] parent1 = population[parentIndex1];
            double[] parent2 = population[parentIndex2];

            //随机选择染色体中的交叉点
            int crossoverPoint = rand.nextInt(parent1.length);

            //创建子代的染色体序列
            double[] offspring1 = new double[parent1.length];
            double[] offspring2 = new double[parent1.length];

            for(int j = 0;j < parent1.length;j++){
                //交叉点之前  子代和父代对应   交叉点之后,子代染色体来自另一个父代的染色体
                //比如父代1是12345  父代2是67890
                // 交叉点是3的位置 那子代1是12890 子代2是67345
                if(j < crossoverPoint){
                    offspring1[j] = parent1[j];
                    offspring2[j] = parent2[j];
                }else{
                    offspring1[j] = parent2[j];
                    offspring2[j] = parent1[j];
                }
            }
            offspringPopulation[i] = offspring1;
            offspringPopulation[i + 1] = offspring2;
        }
        return offspringPopulation;
    }

    /**
     * 变异操作
     * @param population 种群
     * @param bounds 边界数组
     */
    public void performMutation(double[][] population,double[] bounds){
        Random rand = new Random();

        for(int i = 0;i < population.length;i++){
            for (int j = 0;j < population[i].length;j++){
                if(rand.nextDouble() < 0.01){
                    //变异率为0.01 随机生成新的染色体值
                    population[i][j] = bounds[0] + (bounds[1] - bounds[0]) * rand.nextDouble();
                }
            }
        }
    }

    /**
     * 获取最佳适应度
     * @param population 种群
     * @param fitnessFunction 适应度函数
     * @return
     */
    public double getBestFitnessValue(double[][] population,FitnessFunction fitnessFunction){
        double bestFitness = Double.NEGATIVE_INFINITY;//初始最佳适应度为最大
        for (int i = 0;i < population.length;i++){
            double fitness = fitnessFunction.calculateFitness(population[i]);
            if(fitness >bestFitness){
                bestFitness = fitness;//更新最佳适应度
            }
        }
        return bestFitness;
    }

    /**
     * 获取最佳个体(即答案所在的个体)
     * @param population 种群
     * @param fitnessFunction 适应度函数
     * @return
     */
    public double[] getBestIndividual(double[][] population,FitnessFunction fitnessFunction){
        double bestFitness = Double.NEGATIVE_INFINITY;
        double[] bestIndividual = null;//最佳个体初始化为null

        for(int i = 0;i < population.length;i++){
            double fitness = fitnessFunction.calculateFitness(population[i]);
            if(fitness > bestFitness){
                bestFitness = fitness;
                bestIndividual = population[i];//更新最佳个体
            }
        }
        return bestIndividual;
    }
}

选择操作的说明:

caculateSelectionProbabilities(double[] fitnessValues):
这个方法计算了每个个体被选中的概率。它通过将每个个体的适应度除以总适应度来计算每个个体的选择概率。适应度越大,选择概率越高。

caculateCumulativeProbabilities(double[] probabilities):
这个方法计算了累积选择概率,也就是轮盘赌算法中每个个体在轮盘上的扇区的范围。它通过累加每个个体的选择概率来实现。
这个累积概率数组的最后一个值应该接近于1。

performSelection(double[] fitnessValues):
这是基于轮盘赌的选择方法。它首先计算了选择概率和累积概率,然后使用随机数在轮盘上进行选择。
对于每个随机数,都会在累积概率数组中找到第一个大于等于该随机数的值,然后选择对应的个体索引。

selectPopulation(double[][] population,int[] selectionIndices):
这个方法根据选择的索引来选择生成新的种群。它创建了一个新的种群数组,并根据选择的索引从旧种群中复制对应的个体到新种群中。

为什么要计算累积概率?因为轮盘赌算法是基于随机数的选择方法,需要将每个个体的选择概率映射到轮盘上的扇区,而累积概率数组就是用来确定每个个体在轮盘上的扇区范围的。

为什么要生成索引?因为在选择过程中,我们需要知道哪些个体被选中了,所以需要将选中的个体的索引记录下来。

例子:假设有一个种群,其中包含4个个体,它们的适应度分别为[0.1, 0.3, 0.2, 0.4]。
首先,caculateSelectionProbabilities 方法将计算选择概率数组为[0.1, 0.3, 0.2, 0.4]。
然后,caculateCumulativeProbabilities 方法将计算累积概率数组为[0.1, 0.4, 0.6, 1.0]。
接下来,performSelection 方法将根据累积概率数组进行选择,例如生成的随机数为0.25,则对应的个体索引为1。
最后,selectPopulation 方法将根据选择的索引生成新的种群,例如选择了索引1,则新种群中的第一个个体将是旧种群中的第二个个体。
通过这样的选择过程,种群中适应度较高的个体被选中的概率更大,从而增加了它们被保留到下一代的机会。

主函数:

public class Main {
    public static void main(String[] args) {
    double result;
    double[] position;
    double[] bounds = {-10,10};// 搜索空间的边界
    int populationSize = 20;// 种群大小
    int chromosomeLength = 30;// 染色体长度
    int maxIterations = 1000;// 最大迭代次数

    GA ga = new GA();
    FitnessFunction fitnessFunction = new Fitness();
    double[][] population = ga.initializePopulation(populationSize,chromosomeLength,bounds);
    for (int i = 0;i < maxIterations;i++){
        //计算适应度
        double[] fitnessValue = ga.evaluatePopulation(population,fitnessFunction);
        //根据适应度选择  索引
        int[] selectionIndices = ga.performSelection(fitnessValue);
        //根据索引选择新种群
        double[][] selectedPopulation = ga.selectPopulation(population,selectionIndices);
        //交叉
        double[][] offspringPopulation = ga.performCrossover(selectedPopulation);
        //变异
        ga.performMutation(offspringPopulation,bounds);
        //更新种群
        population = offspringPopulation;
    }
    //计算最佳适应度
    result = ga.getBestFitnessValue(population,fitnessFunction);
    //获得最佳个体
    position = ga.getBestIndividual(population,fitnessFunction);

        System.out.println("result:" + result);
        System.out.println("Position:" + Arrays.toString(position));
    }
}

作者刚刚开始学习这些内容,有什么问题请大家多多指教啦。

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值