(前排提示:本文包含大量手撕代码和灵魂画图,建议搭配瓜子阅读效果更佳~)
一、这个数学题不简单!
咱们先来看一个魔鬼函数:
f(x,y,z) = sin(x²+y²+z²) / (x²+y²+z² + 1e-6) + 0.1*(x+y+z)
这个三维函数的最大值在哪里?传统微积分方法可能当场去世——求导方程复杂到怀疑人生!(别问我怎么知道的,头发就是这么没的)
举个栗子🌰:假设我们要找这个函数在区间[-5,5]³范围内的最大值,常规网格搜索法需要:
- 每个维度取100个点 → 总共100³=1,000,000次计算
- 每次计算耗时0.1ms → 总耗时100秒!
(此时一位遗传算法选手优雅路过)
二、遗传算法的魔法原理
(示意图,具体步骤往下看↓↓↓)
核心六步走:
- 基因编码:把解空间映射到染色体(比如二进制编码)
- 种群初始化:随机生成N个个体
- 适应度评估:计算每个个体的函数值
- 选择操作:保留优秀个体(轮盘赌/锦标赛)
- 交叉变异:产生新个体(基因重组)
- 迭代进化:直到满足终止条件
举个接地气的例子:假设要找全中国最高的人
- 编码:用GPS坐标表示位置
- 种群:随机选100个城市
- 适应度:量身高
- 选择:重点在北方地区找
- 变异:偶尔去南方找找惊喜
三、手把手写Python代码
import numpy as np
# 超参数设置(重点!!!)
POP_SIZE = 50 # 种群规模
DNA_SIZE = 24 # 每个维度8位二进制(3维×8=24)
CROSS_RATE = 0.8 # 交叉概率
MUTATION_RATE = 0.003
N_GENERATIONS = 200
X_BOUND = [-5, 5] # 取值范围
# 目标函数(三维版)
def F(x, y, z):
r = x**2 + y**2 + z**2
return np.sin(r)/(r + 1e-6) + 0.1*(x+y+z)
# 解码器:二进制转实数
def translateDNA(pop):
return pop.dot(2 ** np.arange(DNA_SIZE)[::-1]) / (2**DNA_SIZE-1) * \
(X_BOUND[1]-X_BOUND[0]) + X_BOUND[0]
# 适应度计算(重要!!!)
def get_fitness(pred):
return pred - np.min(pred) + 1e-6 # 确保非负
# 选择(轮盘赌法)
def select(pop, fitness):
idx = np.random.choice(np.arange(POP_SIZE), size=POP_SIZE,
replace=True, p=fitness/fitness.sum())
return pop[idx]
# 交叉(单点交叉)
def crossover(parent, pop):
if np.random.rand() < CROSS_RATE:
i_ = np.random.randint(0, POP_SIZE, size=1)
cross_points = np.random.randint(0, 2, DNA_SIZE).astype(np.bool)
parent[cross_points] = pop[i_, cross_points]
return parent
# 变异(位翻转)
def mutate(child):
for point in range(DNA_SIZE):
if np.random.rand() < MUTATION_RATE:
child[point] = 1 if child[point]==0 else 0
return child
# 主程序(开始表演!)
pop = np.random.randint(2, size=(POP_SIZE, DNA_SIZE))
for _ in range(N_GENERATIONS):
# 解码三维坐标
xyz = translateDNA(pop).reshape(-1,3)
x, y, z = xyz[:,0], xyz[:,1], xyz[:,2]
# 计算适应度
F_values = F(x, y, z)
fitness = get_fitness(F_values)
# 进化操作
pop = select(pop, fitness)
pop_copy = pop.copy()
for parent in pop:
child = crossover(parent, pop_copy)
child = mutate(child)
parent[:] = child
# 输出最佳解
best_xyz = translateDNA(pop)[np.argmax(F_values)]
print(f"最大值坐标:{best_xyz}, 函数值:{F(*best_xyz)}")
四、调参侠的生存指南
(血泪经验总结!!!)
- 种群大小:20-100效果较好,太大算得慢,太小早熟
- 变异率:0.001-0.1之间,太高变随机搜索,太低没创新
- 编码长度:每个维度8-12位,精度够用就好
- 早停策略:连续10代最优解不变就停(防过度拟合)
- 混合策略:最后用梯度下降微调(hybrid算法更香)
五、真实世界的应用场景
- 天线设计:华为用遗传算法优化5G天线形状
- 游戏AI:星际争霸的兵种搭配优化
- 物流调度:京东的快递路径规划
- 金融投资:基金组合的收益风险平衡
- 医疗诊断:癌症筛查的特征选择
六、说点掏心窝的话
遗传算法就像个聪明的懒汉——不需要知道问题的深层结构,只要定义好适应度函数,它就能在解空间里乱窜找最优解。虽然不一定每次都能找到全局最优,但在复杂非线性问题上表现惊艳!
不过要注意(敲黑板):
- 适合解空间大的问题
- 需要合理设置超参数
- 计算成本可能较高
- 对离散问题支持较好
下次遇到求极值头秃的时候,不妨试试这个"达尔文式"的优化方法~ 说不定会有意外惊喜!(毕竟连大自然都能演化出人类,找个函数最大值还不是小菜一碟?)