文章目录
一、为什么优化问题总让人头大?
大家在工程和科研中是不是经常遇到这样的场景:要在一堆参数里找到最佳组合,但试了梯度下降/穷举法都跪了?(特别是当目标函数长得像心电图的时候!)这时候就该祭出我们今天的主角——遗传算法(Genetic Algorithm)了!
二、遗传算法的神奇之处
这个算法的灵感来自达尔文的进化论(没想到吧!)。想象一下,我们把解空间里的每个解看作一个生物个体,通过"适者生存"的法则,让优秀的解相互"交配"产生后代。经过N代进化后,就能得到近似最优解!
举个栗子🌰:
假设我们要找函数 f(x) = x² 在 [0,31] 的最大值。传统方法要算32次,但用遗传算法可能10代就搞定!
三、手把手实现7大步骤
Step1️⃣ 基因编码(二进制大法好!)
把变量转成二进制串,比如:
- 数字17 → 10001(5位编码)
- 数字5 → 00101
def encode(x, length=5):
return format(x, '0{}b'.format(length))
Step2️⃣ 初始化种群(随机才有惊喜!)
随机生成初始解,保持多样性是关键!种群太小容易早熟,太大算得慢。经验值:20-100个
import random
population = [random.randint(0,31) for _ in range(20)]
Step3️⃣ 适应度评估(优胜劣汰时刻!)
计算每个个体的适应度(这里直接取函数值):
fitness = [x**2 for x in population]
Step4️⃣ 选择操作(轮盘赌超有趣!)
按适应度占比选父母,像抽奖转盘:
def selection(population, fitness):
total = sum(fitness)
pick = random.uniform(0, total)
current = 0
for i in range(len(population)):
current += fitness[i]
if current > pick:
return population[i]
Step5️⃣ 交叉操作(爱情结晶诞生!)
随机选交叉点交换基因片段,这里用单点交叉:
def crossover(parent1, parent2):
point = random.randint(1,4)
mask = (1 << point) - 1
child1 = (parent1 & ~mask) | (parent2 & mask)
child2 = (parent2 & ~mask) | (parent1 & mask)
return child1, child2
Step6️⃣ 变异操作(基因突变惊喜!)
以一定概率翻转某个bit位(变异率通常0.001-0.01):
def mutation(child):
if random.random() < 0.01:
bit = 1 << random.randint(0,4)
return child ^ bit
return child
Step7️⃣ 迭代进化(见证奇迹的时刻!)
把上述步骤循环起来:
for generation in range(100):
# 评估适应度
# 选择父母
# 交叉变异
# 生成新种群
四、实战效果大公开!
用下面的参数跑10次:
- 种群大小:20
- 最大代数:50
- 交叉率:0.8
- 变异率:0.01
运行结果示例:
第1代最佳解:27 → 729
第10代最佳解:31 → 961(已达最大值!)
五、调参小贴士(血泪经验!)
-
种群多样性是王道!如果发现所有个体都一样了(早熟),赶紧:
- 增大变异率(但别超过0.1!)
- 改用锦标赛选择法
-
收敛太慢怎么办?
- 试试精英保留策略(保留每代最优个体)
- 调整适应度比例(指数缩放很香!)
-
二进制编码局限大?试试:
- 实数编码(适合连续变量)
- 排列编码(适合TSP问题)
六、高级玩法预告
学会了基础版,还可以尝试:
- 多目标优化(NSGA-II算法)
- 动态环境适应(增加物种多样性)
- 混合算法(GA+神经网络=王炸!)
七、完整代码大放送
# 省略代码部分,完整版见GitHub仓库...
最后说两句
遗传算法最大的魅力就是——你永远不知道下一代会进化出什么神仙解!(就像生命本身的奇迹)赶紧动手试试,说不定能找到意想不到的最优解哦!遇到问题欢迎评论区交流,看到必回~