别再死记硬背了!用Python实战遗传算法中的轮盘赌选择(附完整代码)
用Python实战遗传算法中的轮盘赌选择:从理论到代码的深度解析
遗传算法作为模拟自然选择过程的优化方法,其核心操作之一便是选择机制。许多初学者在理解轮盘赌选择时容易陷入公式记忆的误区,而忽略了其背后的概率本质。本文将彻底拆解这一算法,通过可运行的Python代码和可视化分析,带您真正掌握这一关键技术的实现精髓。
1. 轮盘赌选择的数学本质与实现逻辑
轮盘赌选择(Roulette Wheel Selection)的命名源自赌场中的轮盘——每个个体被选中的概率与其适应度成正比,就像轮盘上不同大小的扇形区域。假设种群中有4个个体,其适应度分别为[4, 16, 36, 64],那么总适应度为120,各个体的选择概率分别为:
| 个体编号 | 适应度 | 选择概率 | 累积概率 |
|---|---|---|---|
| 1 | 4 | 3.33% | 3.33% |
| 2 | 16 | 13.33% | 16.66% |
| 3 | 36 | 30.00% | 46.66% |
| 4 | 64 | 53.33% | 100.00% |
实现时最关键的是将随机数映射到累积概率区间。例如随机数0.42落在46.66%-100%区间,因此会选择个体4。这个过程用Python实现仅需几行核心代码:
def roulette_wheel_selection(population, fitness_values):
total_fitness = sum(fitness_values)
probabilities = [f/total_fitness for f in fitness_values]
cum_probs = [sum(probabilities[:i+1]) for i in range(len(probabilities))]
selected = []
for _ in range(len(population)):
r = random.random()
for i, cum_prob in enumerate(cum_probs):
if r <= cum_prob:
selected.append(population[i])
break
return selected
注意:实际工程实现时会考虑数值稳定性问题,例如加入极小值ε防止除零错误
2. 完整实现与可视化分析
让我们构建一个完整的可运行示例,包含适应度计算、选择过程和结果可视化。以下代码使用matplotlib展示选择概率分布:
import random
import matplotlib.pyplot as plt
import numpy as np
# 初始种群
population = ['A', 'B', 'C', 'D']
fitness_values = [4, 16, 36, 64]
# 计算选择概率
total_fitness = sum(fitness_values)
probabilities = [f/total_fitness for f in fitness_values]
cum_probs = np.cumsum(probabilities)
# 可视化
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12,5))
# 概率分布饼图
ax1.pie(probabilities, labels=population, autopct='%1.1f%%')
ax1.set_title('Selection Probability Distribution')
# 累积概率条形图
x = range(len(population))
ax2.bar(x, probabilities, label='Individual Probability')
ax2.step(x, cum_probs, 'r-', where='post', label='Cumulative Probability')
ax2.set_xticks(x)
ax2.set_xticklabels(population)
ax2.set_ylim(0, 1.1)
ax2.legend()
ax2.set_title('Cumulative Probability Distribution')
plt.tight_layout()
plt.show()
运行后会生成两个关键图表:
- 左图直观显示每个个体被选中的概率占比
- 右图展示累积概率的阶梯变化,清晰标注选择边界
通过调整fitness_values数组,可以观察不同适应度分布下的选择压力变化。例如尝试[10,10,10,10]的均匀分布,或[1,1,1,100]的极端情况。
3. 工程实践中的常见问题与优化
实际应用中,原始轮盘赌算法可能遇到几个典型问题:
-
选择压力不足 :当适应度差异较小时,选择效果不明显
- 解决方案:使用指数缩放
adjusted_fitness = fitness^3
- 解决方案:使用指数缩放
-
早熟收敛 :超级个体过早主导种群
- 解决方案:采用线性缩放
adjusted_fitness = a*fitness + b
- 解决方案:采用线性缩放
-
负适应度处理 :某些问题可能产生负的适应度值
- 转换方法:
adjusted_fitness = fitness - min(fitness) + ε
- 转换方法:
改进后的选择算法实现:
def enhanced_selection(population, fitness_values, scaling='linear'):
if scaling == 'linear':
min_f = min(fitness_values)
max_f = max(fitness_values)
a = (1 - 0.1)/(max_f - min_f) if max_f != min_f else 1
b = 0.1 - a*min_f
adjusted_fitness = [a*f + b for f in fitness_values]
elif scaling == 'exponential':
adjusted_fitness = [f**3 for f in fitness_values]
return roulette_wheel_selection(population, adjusted_fitness)
性能优化方面,对于大规模种群(N>10000),可以考虑以下策略:
- 随机数排序法 :预先生成所有随机数并排序,将算法复杂度从O(N^2)降到O(N log N)
- 二分查找法 :对累积概率数组使用bisect模块的bisect_right函数
4. 与其他选择算法的对比实验
轮盘赌选择并非唯一选择策略,下表对比几种常见方法的特点:
| 算法类型 | 选择压力 | 多样性保持 | 实现复杂度 | 适用场景 |
|---|---|---|---|---|
| 轮盘赌选择 | 中等 | 中等 | 低 | 一般优化问题 |
| 锦标赛选择 | 可调 | 高 | 中 | 多目标优化 |
| 排序选择 | 稳定 | 低 | 低 | 适应度差异大时 |
| 稳态选择 | 低 | 高 | 高 | 需要保持多样性时 |
通过以下代码可以进行算法对比实验:
def tournament_selection(population, fitness_values, k=2):
selected = []
for _ in range(len(population)):
contestants = random.sample(list(zip(population, fitness_values)), k)
winner = max(contestants, key=lambda x: x[1])[0]
selected.append(winner)
return selected
# 测试不同算法的选择分布
def test_selection_algorithm(algorithm, n_trials=10000):
counts = {ind:0 for ind in population}
for _ in range(n_trials):
selected = algorithm(population, fitness_values)
for ind in selected:
counts[ind] += 1
return {k:v/n_trials/len(population) for k,v in counts.items()}
# 打印结果对比
print("轮盘赌选择概率:", test_selection_algorithm(roulette_wheel_selection))
print("锦标赛选择概率:", test_selection_algorithm(tournament_selection))
实验结果显示,锦标赛选择会放大高适应度个体的优势,而轮盘赌选择则保持更平滑的概率分布。在实际项目中,通常会组合使用多种选择策略,例如在进化早期使用轮盘赌增加多样性,后期切换为锦标赛选择加快收敛。
更多推荐
所有评论(0)