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

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


算法的作用

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


算法步骤

1. 种群初始化

定义:

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

初始化方法:

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

种群大小:

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


2. 编码

定义:

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

常见的编码方式:

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

编码选择的影响:

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


3. 适应度计算

定义:

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

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

4. 选择

定义:

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

常见的选择方式:

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

5. 交叉

定义:

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

常见的交叉方式:

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

6. 变异

定义:

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


7. 终止条件

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

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

算法简单案例

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

scss 复制代码
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);
    }
}

算法总结

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

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

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

相关推荐
挣扎与觉醒中的技术人5 分钟前
【技术干货】三大常见网络攻击类型详解:DDoS/XSS/中间人攻击,原理、危害及防御方案
前端·网络·ddos·xss
zeijiershuai10 分钟前
Vue框架
前端·javascript·vue.js
写完这行代码打球去12 分钟前
没有与此调用匹配的重载
前端·javascript·vue.js
华科云商xiao徐12 分钟前
使用CPR库编写的爬虫程序
前端
狂炫一碗大米饭14 分钟前
Event Loop事件循环机制,那是什么事件?又是怎么循环呢?
前端·javascript·面试
IT、木易16 分钟前
大白话Vue Router 中路由守卫(全局守卫、路由独享守卫、组件内守卫)的种类及应用场景
前端·javascript·vue.js
顾林海17 分钟前
JavaScript 变量与常量全面解析
前端·javascript
程序员小续17 分钟前
React 组件库:跨版本兼容的解决方案!
前端·react.js·面试
乐坏小陈18 分钟前
2025 年你希望用到的现代 JavaScript 模式 【转载】
前端·javascript
生在地上要上天18 分钟前
从600行"状态地狱"到可维护策略模式:一次列表操作限制重构实践
前端