<section id="nice" data-tool="mdnice编辑器" data-website="https://www.mdnice.com" s

99 阅读6分钟

遗传算法是一种基于达尔文“适者生存”理论和孟德尔遗传规律的计算模型。它通过模拟生物进化的过程,在种群中进行选择、交叉和变异操作,从而逐步优化目标函数。遗传算法的核心思想是通过群体代的方式,不断生成更适应环境的个体,最终找到最优解或接近最优解的个体。


算法的作用

遗传算法主要用于解决优化问题,尤其是在传统优化方法难以奏效的情况下。它通过模拟自然界中的进化过程,逐步逼近最优解或满意解。


算法步骤

1. 种群初始化

定义:
种群初始化是算法开始时,随机或者按照某种规则生成一组候选解。

初始化方法:

  • 随机初始化:
    最常见的方法,通过随机生成满足约束条件的个体来构建初始种群。这种方法简单易实现,但种群质量一般不高。
  • 启发式初始化:
    结合问题领域的先验知识或启发式规则生成初始种群,从而提高种群的质量和收敛速度。然而,对于某些问题可能产生经验性错误。
  • 混合初始化:
    将随机初始化和启发式初始化结合,可以平衡种群多样性和质量。

种群大小:

根据问题规模调整种群大小。较大的种群可以提供更好的搜索能力,但也增加了计算成本,因此需要在两者之间进行权衡。


2. 编码

定义:
编码是将问题的解表示为遗传算法可操作的形式。

常见的编码方式:

  • 二进制编码:
    将解表示为二进制字符串。优点是实现简单,易于进行交叉和变异操作;缺点是可能存在精度缺失和编码长度过长的问题。
  • 实数编码:
    直接用实数表示解的各个维度(如向量问题)。优点是没有精度问题,适合连续优化问题;缺点是交叉和变异操作相对复杂。
  • 排列编码:
    用于解决排列问题。优点是适合解决排列相关问题;缺点是需要特殊的设计以保证解的合法性。
  • 字符编码:
    使用字符或符号表示解。
  • 混合编码:
    结合多种编码方式,适用于复杂的优化问题。

编码选择的影响:

不同的编码方式对遗传算法的操作(如交叉、变异)有不同的适应性,需根据具体问题选择合适的编码方式。


3. 适应度计算

定义:
适应度是对遗传算法中每个个体的“优劣”进行量化评估的一个数值。

  • 适应度函数:
    衡量个体优劣的标准,通常是目标函数的直接或间接映射。适应度值越高,表示该个体越优秀。

4. 选择

定义:
选择是从当前种群中挑选出优秀的个体。

常见的选择方式:

  • 轮盘赌选择:
    根据个体的适应度值分配一个“选择概率”,适应度越高,被选中的概率越大。缺点是可能导致种群中某些个体占据主导地位,降低种群多样性。
  • 锦标赛选择:
    随机从种群中抽取若干个个体(称为一个小的“锦标赛”),从中选择适应度最高的个体。优点是简单高效,且可以通过调整锦标赛规模控制选择压力。
  • 排名选择:
    根据个体的适应度排名而非绝对适应度值来分配选择概率,排名越高,被选中的概率越大。优点是避免了适应度值差距过大导致的选择压力过高问题。
  • 随机遍历抽样:
    在轮盘赌选择的基础上改进,通过等间距采样提高效率并减少随机性带来的偏差。
  • 精英保留策略:
    直接将当前种群中适应度最高的个体复制到下一代,确保优秀解不会因遗传操作而丢失。通常与其他选择方法结合使用。

5. 交叉

定义:
交叉是将两个父代个体的部分基因交换,生成新的子代个体。

常见的交叉方式:

  • 单点交叉:
    在随机位置交换两个父代的部分基因。
  • 多点交叉:
    在多个位置交换基因。
  • 均匀交叉:
    每个基因位独立以一定概率决定是否交换。
  • 算术交叉: 对实数编码的父代基因进行线性组合 (demo使用的就是这种,简单)

6. 变异

定义:
变异是对个体的某些基因进行随机改变,增加种群的多样性。变异的概率通常较低。


7. 终止条件

遗传算法的终止条件包括但不限于以下几种:

  • 个体的适应度达到给定的阈值。
  • 最优个体适应度和群体适应度不再上升。
  • 迭代次数达到预设的代数。

算法简单案例

以下是一个简单的遗传算法实现,用于求解函数 f(x) = x + 10 * sin(5x) + 7 * cos(4x) 的最大值。


import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class GeneticAlgorithm {

    // 目标函数
    public static double fitnessFunction(double x) {
        return x + 10 * Math.sin(5 * x) + 7 * Math.cos(4 * x);
    }

    // 初始化种群
    public static List<Double> initializePopulation(int popSize, double lowerBound, double upperBound) {
        List<Double> population = new ArrayList<>();
        Random random = new Random();
        for (int i = 0; i < popSize; i++) {
            population.add(lowerBound + random.nextDouble() * (upperBound - lowerBound));
        }
        return population;
    }

    // 轮盘赌选择
    public static List<Double> selection(List<Double> population, List<Double> fitnesses) {
        double totalFitness = 0;
        for (double fitness : fitnesses) {
            totalFitness += fitness;
        }

        List<Double> selectedParents = new ArrayList<>();
        Random random = new Random();
        for (int i = 0; i < 2; i++) {
            double r = random.nextDouble() * totalFitness;
            double cumulativeFitness = 0;
            for (int j = 0; j < population.size(); j++) {
                cumulativeFitness += fitnesses.get(j);
                if (cumulativeFitness >= r) {
                    selectedParents.add(population.get(j));
                    break;
                }
            }
        }
        return selectedParents;
    }

    // 算术交叉
    public static List<Double> crossover(List<Double> parents, double alpha) {
        double parent1 = parents.get(0);
        double parent2 = parents.get(1);

        double child1 = alpha * parent1 + (1 - alpha) * parent2;
        double child2 = (1 - alpha) * parent1 + alpha * parent2;

        List<Double> children = new ArrayList<>();
        children.add(child1);
        children.add(child2);
        return children;
    }

    // 高斯变异
    public static double mutation(double individual, double mutationRate, double stdDev, double lowerBound, double upperBound) {
        Random random = new Random();
        if (random.nextDouble() < mutationRate) {
            individual += random.nextGaussian() * stdDev;
            individual = Math.max(lowerBound, Math.min(individual, upperBound)); // 确保个体在定义域内
        }
        return individual;
    }

    // 遗传算法主函数
    public static void geneticAlgorithm(int popSize, int generations, double mutationRate, double alpha) {
        double lowerBound = 0, upperBound = 10;
        double precision = 0.01;

        // 初始化种群
        List<Double> population = initializePopulation(popSize, lowerBound, upperBound);

        for (int generation = 0; generation < generations; generation++) {
            // 计算适应度值
            List<Double> fitnesses = new ArrayList<>();
            for (double individual : population) {
                fitnesses.add(fitnessFunction(individual));
            }

            // 找到当前最优解
            double bestFitness = Double.MIN_VALUE;
            double bestIndividual = 0;
            for (int i = 0; i < fitnesses.size(); i++) {
                if (fitnesses.get(i) > bestFitness) {
                    bestFitness = fitnesses.get(i);
                    bestIndividual = population.get(i);
                }
            }

            System.out.printf("Generation %d: Best x = %.4f, Fitness = %.4f%n", generation, bestIndividual, bestFitness);

            // 新一代种群
            List<Double> newPopulation = new ArrayList<>();

            while (newPopulation.size() < popSize) {
                // 选择
                List<Double> parents = selection(population, fitnesses);

                // 交叉
                List<Double> offspring = crossover(parents, alpha);

                // 变异
                for (double child : offspring) {
                    double mutatedChild = mutation(child, mutationRate, precision, lowerBound, upperBound);
                    newPopulation.add(mutatedChild);
                }
            }

            // 更新种群(保留最优解)
            population = newPopulation.subList(0, popSize);
            population.set(0, bestIndividual); // 强制保留最优解
        }

        // 输出最终结果
        double finalBestFitness = Double.MIN_VALUE;
        double finalBestIndividual = 0;
        for (double individual : population) {
            double fitness = fitnessFunction(individual);
            if (fitness > finalBestFitness) {
                finalBestFitness = fitness;
                finalBestIndividual = individual;
            }
        }

        System.out.printf("Final Result: Best x = %.4f, Fitness = %.4f%n", finalBestIndividual, finalBestFitness);
    }

    public static void main(String[] args) {
        int popSize = 50// 种群大小
        int generations = 100// 迭代次数
        double mutationRate = 0.1// 变异率
        double alpha = 0.6// 交叉参数

        geneticAlgorithm(popSize, generations, mutationRate, alpha);
    }
}

算法总结

遗传算法通过模拟生物进化机制,为解决复杂优化问题提供了有效途径。其核心特点包括:

  • 群体搜索策略: 避免单点搜索的局限性。
  • 优胜劣汰机制: 保证搜索方向的有效性。
  • 随机操作组合: 平衡探索与开发。

遗传算法具有较强的鲁棒性和通用性,适用于多种类型的优化问题。但在实际应用中,需要根据问题的特点调整参数和操作方式,以获得最佳性能。