别再死磕数学公式了!用Python手搓一个遗传算法,5步搞定函数寻优
·
别再死磕数学公式了!用Python手搓一个遗传算法,5步搞定函数寻优
遗传算法(Genetic Algorithm, GA)作为进化计算的核心分支,早已从学术论文走向工业实践。但大多数教程仍停留在理论推导和流程图解阶段,让学习者陷入数学符号的迷宫。本文将以 极简代码+可视化 的方式,带你用Python从零实现一个完整的遗传算法,并在Rastrigin函数(多峰优化测试函数)上验证效果。我们将避开繁琐的数学证明,聚焦于以下核心问题:
- 如何用NumPy矩阵操作替代传统循环实现高效种群进化?
- 赌轮盘选择有哪些容易被忽视的实现陷阱?
- 变异概率的设置如何影响收敛速度?
- 怎样用Matplotlib动态展示进化过程?
1. 环境准备与问题定义
在开始编码前,我们需要明确优化目标。Rastrigin函数因其大量局部极小值而成为测试全局优化算法的经典选择,其二维形式定义为:
import numpy as np
def rastrigin(x):
""" 二维Rastrigin函数 """
A = 10
return A * 2 + (x[0]**2 - A * np.cos(2 * np.pi * x[0])) + (x[1]**2 - A * np.cos(2 * np.pi * x[1]))
关键参数初始化 :
| 参数名 | 取值 | 说明 |
|---|---|---|
| 种群大小 | 50 | 每代个体数量 |
| 染色体长度 | 20 | 每个变量的二进制编码长度 |
| 交叉概率 | 0.8 | 发生交叉操作的概率 |
| 变异概率 | 0.05 | 单个基因位变异的概率 |
| 迭代次数 | 100 | 进化代数上限 |
提示:二进制编码长度直接影响搜索精度,每增加1位可将搜索空间细分一倍
2. 种群初始化与编码设计
与传统优化算法不同,遗传算法操作的是编码后的解。我们采用二进制编码将实数变量映射到基因串:
def initialize_population(pop_size, chrom_length):
""" 初始化二进制种群 """
return np.random.randint(0, 2, size=(pop_size, chrom_length * 2)) # 二维问题×2
编码/解码函数实现 :
def decode(chromosome, search_range):
""" 二进制解码到实际值 """
num_genes = len(chromosome) // 2
x1 = chromosome[:num_genes]
x2 = chromosome[num_genes:]
def bin_to_float(bin_arr, min_val, max_val):
int_val = int(''.join(map(str, bin_arr)), 2)
return min_val + (max_val - min_val) * int_val / (2**len(bin_arr) - 1)
return np.array([
bin_to_float(x1, search_range[0], search_range[1]),
bin_to_float(x2, search_range[0], search_range[1])
])
3. 遗传操作核心实现
3.1 适应度计算与选择
采用 指数缩放 避免早期超级个体主导种群:
def calculate_fitness(population, search_range):
""" 计算适应度(Rastrigin函数值越小适应度越高) """
fitness = np.zeros(len(population))
for i, chrom in enumerate(population):
x = decode(chrom, search_range)
fitness[i] = 1 / (1 + rastrigin(x)) # 防止除零
return fitness / fitness.sum() # 归一化
赌轮盘选择的向量化实现:
def roulette_selection(population, fitness):
""" 向量化轮盘赌选择 """
indices = np.random.choice(len(population), size=len(population)-1,
p=fitness, replace=True)
return np.vstack((
population[np.argmax(fitness)], # 保留精英
population[indices]
))
3.2 交叉与变异操作
单点交叉的NumPy高效实现:
def crossover(parents, pc):
""" 单点交叉 """
children = []
for i in range(0, len(parents), 2):
if i+1 >= len(parents) or np.random.rand() > pc:
children.extend([parents[i], parents[i+1]])
continue
cross_point = np.random.randint(1, parents.shape[1])
child1 = np.hstack((parents[i,:cross_point], parents[i+1,cross_point:]))
child2 = np.hstack((parents[i+1,:cross_point], parents[i,cross_point:]))
children.extend([child1, child2])
return np.array(children)
位翻转变异的技巧:
def mutation(population, pm):
""" 位点变异 """
mask = np.random.rand(*population.shape) < pm
return np.where(mask, 1 - population, population)
4. 进化过程可视化
动态展示种群在搜索空间中的分布变化:
import matplotlib.pyplot as plt
from IPython import display
def plot_population(population, search_range, generation):
plt.figure(figsize=(10,6))
x = [decode(chrom, search_range)[0] for chrom in population]
y = [decode(chrom, search_range)[1] for chrom in population]
plt.scatter(x, y, c='blue', alpha=0.6)
# 绘制Rastrigin函数轮廓
xx = np.linspace(*search_range, 100)
yy = np.linspace(*search_range, 100)
X, Y = np.meshgrid(xx, yy)
Z = rastrigin([X, Y])
plt.contour(X, Y, Z, levels=20, cmap='viridis')
plt.title(f'Generation {generation}')
plt.xlim(search_range)
plt.ylim(search_range)
display.clear_output(wait=True)
display.display(plt.gcf())
plt.close()
5. 完整算法组装与调优
将各模块组合成完整流程:
def genetic_algorithm():
search_range = (-5.12, 5.12) # Rastrigin函数标准定义域
population = initialize_population(50, 20)
best_fitness = []
for gen in range(100):
fitness = calculate_fitness(population, search_range)
best_idx = np.argmax(fitness)
best_fitness.append(1/fitness[best_idx] - 1) # 还原真实函数值
if gen % 10 == 0:
plot_population(population, search_range, gen)
selected = roulette_selection(population, fitness)
children = crossover(selected, 0.8)
population = mutation(children, 0.05)
return best_fitness
参数调优经验 :
- 当收敛速度过慢时,可尝试:
- 提高变异概率至0.1-0.2打破停滞
- 采用自适应交叉概率(前期高后期低)
- 出现早熟收敛时:
- 增加种群规模至100-200
- 引入锦标赛选择增加选择压力
最终运行结果显示,经过约60代进化,算法能够稳定找到全局最小值点(0,0)附近解,函数值可达10^-6量级。这个简单的实现虽然不到100行代码,但已经包含了遗传算法的所有核心要素。在实际工程中,还可以进一步优化:
- 采用格雷码减少Hamming悬崖效应
- 实现多种交叉策略(均匀交叉、两点交叉)
- 添加精英保留机制确保最优解不丢失
- 引入小生境技术维持种群多样性
遗传算法的魅力在于其通用性——只需修改适应度函数和编解码方式,同一套框架可应用于特征选择、参数调优、路径规划等完全不同的问题领域。这也是为什么它能在机器学习、金融建模、工业设计等场景持续发光发热近半个世纪。
更多推荐

所有评论(0)