遗传算法(详细解释和代码示例) - 指南

本文将详细介绍遗传算法,包括其概念、原理、实现和一些常见的变种。

一、什么是遗传算法(Genetic Algorithm)遗传算法(Genetic Algorithm, GA)是一种模拟自然选择和遗传机制的智能优化算法。它借鉴了“适者生存”的思想,通过不断迭代进化来寻找最优解,适用于复杂的、无法用传统数学方法直接求解的优化问题。

二、一些概念基因型(genotype)/ 表示(encoding):个体在算法内部的表示(例如二进制串、实值向量、排列等)。表现型(phenotype):解在困难中的真实含义(例如基因串解码为实际变量)。适应度(fitness):评价个体优劣的函数。种群(population):一组个体。GA 是基于种群的全局搜索方法。世代(generation):一次选择/交叉/变异后得到的新一代种群。三、操作过程关键流程就是:初始化 – 选择 – 交叉-- 变异 – 迭代…下面详细介绍。

初始化(编码):将问题的解用基因编码(如二进制或实数),随机生成一组候选解(种群)。定义适应度函数:定义一个目标函数,用来评价每个个体的优劣(即适应度)。选择:按照适应度高低选择优秀个体进入下一代,常用途径有轮盘赌、锦标赛选择等。交叉(Crossover):模拟生物基因重组,随机选取两个个体的部分基因交换,生成新个体。变异(Mutation):以较低概率随机改变个体的部分基因,增加种群的多样性,避免陷入局部最优。1234 迭代…迭代终止条件:可以设置适宜度阈值或者迭代数来控制迭代的终止。举个栗子:旅行商问题(TSP)问题描述:一个旅行商需要访问若干城市,每个城市只能访问一次,最终回到起点,要求总路程最短。这是一个组合优化问题,传统方法在城市数目很大时求解困难。

遗传算法求解思路:

编码:

用一个序列表示城市访问顺序,例如 [3, 1, 4, 2, 5] 表示先去3号城市,再去1号,以此类推。适应度函数:

计算序列对应的总路程,适应度可设为 1 / 路程,路程越短适应度越高。选择:

可以使用轮盘赌选择城市序列。交叉:可以使用部分映射交叉(PMX),两个父代的部分路径互换。变异:随机交换两个城市的位置,产生新路径。迭代:经过多代演化,路径逐渐优化,每次迭代都会产生一个最优路径,不断迭代,路径会趋于稳定。轮盘赌让就是轮盘赌就适应度高的被选择的可能性高。轮盘赌选择的公式如下:

pi=fi∑j=1nfj

p_i = \frac{f_i}{\sum_{j=1}^{n} f_j}pi​=∑j=1n​fj​fi​​

其中:

pip_ipi​ 是个体 iii被选中的概率fif_ifi​ 是个体 iii 的适应度nnn是种群中个体总数PS:“选择”的常见方法有:轮盘赌(Roulette wheel / fitness-proportionate)、锦标赛选择(Tournament)、排序选择(Rank)、精英保留(elitism)。

四、常见问题 & 调参建议种群大小:太小易早熟收敛,太大计算成本高。常见:50–500,取决于障碍和评估代价。交叉率 / 变异率:交叉常设高(0.6–0.9),变异低(0.01–0.2),但对实值编码可设置更高变异或自适应变异率。精英策略:保留若干最优个体避免退化(常保留 1–5% 或 1-5 个)。早熟收敛:用增加变异、保持/引入新个体、算子多样化或分群(niching)来避免。约束处理:罚函数(penalty)、可行性修复(repair)或采用专用算子。混合方法:GA + 局部搜索(memetic algorithm)常能显著提升解的精度。五、GA 的优缺点 / 适用场景优点:

全局搜索能力强、能处理非凸、多峰和离散问题;与障碍知识耦合低;并行化容易。缺点:

评估代价高(若适应度计算昂贵);参数敏感,收敛慢于专用局部方法(如牛顿、梯度法);难以保证精确最优(通常能找到“近优”解)。适用场景:组合优化(TSP、调度)、黑盒优化、多模态优化、超参数搜索、进化设计、神经网络权重进化等。

六、多目标遗传算法问题示例——以购买水果的多目标问题为例问题描述:你要去市场买水果,目标有两个:

最小化总花费(Cost)最大化总营养价值(Nutrition)市场上有三种水果:

水果单价 (元)营养值苹果35香蕉23橙子46假设你能够买任意数量的水果,目标是花费最少而营养值最高。

多目标特点这是典型的冲突目标问题:

买更多水果 → 营养值高,但花费也高买更少水果 → 花费低,但营养值低因此,这里没有唯一最优解,而是一组Pareto前沿解(提升一个目标就需要牺牲另一个目标)。

遗传算法思路个体表示:

用一个三元组 [苹果数量, 香蕉数量, 橙子数量] 表示一个解,比如 [2, 1, 3]。

适应度函数(两个目标):

f1 = 总花费 = 3*苹果 + 2*香蕉 + 4*橙子 → 目标:最小化f2 = 总营养值 = 5*苹果 + 3*香蕉 + 6*橙子 → 目标:最大化多目标选择:

使用 NSGA-II 或 SPEA2,选择既考虑花费又考虑营养值的个体,保持多样性。

交叉与变异:

交叉:两个父代交换一部分水果数量变异:随机增加或减少某个水果数量结果:遗传算法会输出一组Pareto前沿解,例如:

[1,2,2] → 花费 15 元,营养值 18[2,1,3] → 花费 20 元,营养值 25[3,0,3] → 花费 21 元,营养值 27不断迭代,这个解会趋于稳定。代码:

# 安装最新版 pymoo

# pip install -U pymoo

import numpy as np

from pymoo.core.problem import Problem

from pymoo.algorithms.moo.nsga2 import NSGA2

from pymoo.operators.sampling.rnd import IntegerRandomSampling

from pymoo.operators.crossover.sbx import SBX

from pymoo.operators.mutation.pm import PolynomialMutation

from pymoo.optimize import minimize

from pymoo.visualization.scatter import Scatter

# ------------------------------

# 定义多目标问题

# ------------------------------

class FruitProblem

(Problem):

def __init__(self):

super().__init__(n_var=3, # 3种水果

n_obj=2, # 两个目标

n_constr=0, # 没有约束

xl=0, # 最小值

xu=10, # 最大值

type_var=int) # 整数变量

def _evaluate(self, x, out, *args, **kwargs):

apples = x[:, 0]

bananas = x[:, 1]

oranges = x[:, 2]

# 目标1:总花费(最小化)

cost = 3*apples + 2*bananas + 4*oranges

# 目标2:总营养值(最大化 -> 负值最小化)

nutrition = -(5*apples + 3*bananas + 6*oranges)

out["F"] = np.column_stack([cost, nutrition])

# ------------------------------

# 设置算法

# ------------------------------

algorithm = NSGA2(

pop_size=50,

sampling=IntegerRandomSampling(),

crossover=SBX(prob=0.9, eta=15),

mutation=PolynomialMutation(prob=0.2, eta=20), # 普通多项式变异可用于整数

eliminate_duplicates=True

)

# ------------------------------

# 求解

# ------------------------------

problem = FruitProblem()

res = minimize(problem,

algorithm,

('n_gen', 50),

verbose=True)

# ------------------------------

# 输出结果

# ------------------------------

print("Pareto 前沿解 (水果数量):")

for x in res.X:

print(x.astype(int))

print("\n对应目标值(总花费, 总营养值):")

for f in res.F:

print(f[0], -f[1]) # nutrition 取负回原值

七、代码核心模块总结:初始化适应度评估器(可能并行)选择算子交叉算子变异算子(替换/精英模块)(统计/日志/可视化)终止条件九、一些高级技巧并行评估:如果单次适应度昂贵(例如要跑仿真),用 multiprocessing 或集群并行评估个体。DEAP 可以通过重新设置 toolbox.register("map", pool.map) 来并行。混合(hybrid):在每代或若干代后对最优个体用局部搜索(如 LBFGS、Nelder-Mead)微调(memetic)。自适应参数:变异率 / 交叉率随代数或多样性动态调整,可以避免早熟。多目标:使用 NSGA-II / SPEA2(DEAP、pymoo 支持)。约束处理:用罚函数、修复算子或采用可行性优先策略。日志与可视化:保存 best fitness、avg fitness、diversity(例如基因方差)用于判断是否早熟收敛。十、调试与验证先在已知的基准函数(Sphere, Rastrigin, Rosenbrock)上测试你的 GA,验证能否达到合理解。用不同随机种子多次运行,统计结果(均值、方差),看稳定性。可视化种群的适应度曲线、基因分布,发现是否出现模式退化(多样性丢失)。十一、遗传优化1. Dirichlet (狄利克雷)初始化原理:在遗传算法中,有时候个体的基因必须满足两个条件:

元素 ≥ 0;所有权重的总和 = 1(归一化约束)。传统 GA 初始化常常直接随机生成,再做归一化。这会导致:

有些权重几乎全在某个通道 → 初代分布不均匀;有些点甚至跑到不可行区,还要丢弃或强行拉回。优势:

可行解;就是初代种群全分布均匀,不会一开始就“偏心”;2. 修复器原理:在交叉和变异时,遗传算法会随机“打乱/调整”基因,但这样做频繁会让个体跑出可行域:

出现负数(例如 -0.2 权重);总和 ≠ 1(例如 1.3 或 0.7)。传统途径要么丢掉这些个体,要么惩罚它们的适应度。问题是:

丢掉会浪费搜索结果;

惩罚会拖慢收敛。修复函数:

把所有负的权重直接截断成 0;

然后把剩下的数再归一化,使总和=1。

优势:进化更平稳;减少算法在不可行解上“瞎折腾”。

3. 多轮随机重启原理:种群全挤到局部最优附近。就是遗传算法很容易“早熟收敛”,就多轮随机重启的思路很简单:

跑到一定代数后使整个种群“清零”,重新随机生成一批新的个体(但会保留部分精英解);优势:增强全局搜索能力;避免卡在局部最优;

4. 早停机制(Early Stopping)原理:在很多优化问题里,到了后期种群改进相当慢——每一代的最优值几乎不再变化。若是继续迭代,只是在浪费时间和算力。

早停的做法是设定两个条件:

耐心窗口(patience):比如 50 代内没有改善,就停;最小改变量(min_delta):只有改进大于这个阈值才算有效进步。一旦条件触发,算法提前终止。

优势:避免冗余计算;