第三章 萤火虫算法基础:灵感来源、数学模型与核心定义
各位同学,今天我们来聊聊萤火虫算法的基础。说实话,我第一次接触这个算法是在一个无人机编队项目里——当时需要解决多机协同定位的问题,试了好几种传统优化算法都不太理想。后来一个同事跟我说:「你不如看看萤火虫是怎么找同伴的。」嗯,这一看,就打开了新世界的大门。
3.1 算法灵感来源:大自然给的启发
萤火虫算法(Firefly Algorithm, FA)是2008年由剑桥大学的Xin-She Yang提出的。它的灵感来源,说白了就是夏夜里那些一闪一闪的小虫子。
你想想看,萤火虫发光是为了什么?
- 求偶:雄性萤火虫通过特定频率的闪光吸引雌性
- 捕食:有些种类的萤火虫会模仿其他种类的闪光信号来诱捕猎物
- 警戒:闪光也可以警告捕食者「我有毒,别吃我」
在算法里,我们主要借鉴的是求偶行为。核心逻辑很简单:亮度越高的萤火虫,吸引力越强;距离越近,吸引力也越强。这就像在舞会上,最闪亮的那个人总是最吸引眼球——而且离得越近,效果越明显。
核心思想:每只萤火虫代表一个解,亮度代表解的优劣,吸引度决定移动方向。弱萤火虫会向强萤火虫移动,从而实现全局搜索。
3.2 数学模型:把自然现象翻译成公式
好,现在我们把萤火虫的行为翻译成数学语言。我个人习惯把数学模型拆成三个部分来讲:亮度、吸引度、移动公式。
3.2.1 亮度定义
在算法里,萤火虫的亮度 I 通常直接等于目标函数值。对于最大化问题:
I(x) = f(x)
其中 f(x) 是目标函数,x 是萤火虫的位置向量。亮度越高,代表解越好。
但这里有个坑——光在传播过程中会衰减。距离越远,我们看到的光越暗。所以实际计算时,我们需要考虑距离因素:
I(r) = I₀ × e^(-γ × r²)
其中:
- I₀:萤火虫自身的原始亮度(即 f(x))
- γ:光吸收系数,控制衰减速度
- r:两只萤火虫之间的距离
避坑指南:我曾经在项目中把 γ 设得太大,结果萤火虫之间几乎感受不到彼此的吸引力,算法变成了随机搜索。后来我总结了一个经验:γ 通常取 0.1 到 10 之间,具体要看你的搜索空间大小。空间越大,γ 应该越小。
3.2.2 吸引度定义
吸引度 β 和亮度直接相关。一只萤火虫对另一只的吸引度定义为:
β(r) = β₀ × e^(-γ × r²)
其中 β₀ 是 r=0 时的最大吸引度(通常取1)。
为什么用指数衰减?因为自然界的光强衰减就是这样的规律。而且指数函数在数学上性质很好——可导、光滑、计算方便。
| 参数 | 典型值 | 说明 |
|---|---|---|
| β₀ | 1.0 | 最大吸引度,通常固定 |
| γ | 0.1 ~ 10 | 光吸收系数,控制搜索范围 |
| α | 0.2 ~ 0.5 | 随机步长,控制探索能力 |
3.2.3 移动公式
当萤火虫 i 被萤火虫 j 吸引时,它会向 j 移动。移动公式是:
x_i(t+1) = x_i(t) + β₀ × e^(-γ × r²) × (x_j - x_i) + α × (rand - 0.5)
这个公式有三部分:
- 当前位置:x_i(t),萤火虫当前的位置
- 吸引项:β₀ × e^(-γ × r²) × (x_j - x_i),向更亮的萤火虫移动
- 随机项:α × (rand - 0.5),加入随机扰动,避免陷入局部最优
嗯,这里要注意:随机项不能太大也不能太小。太大了算法不稳定,太小了容易陷入局部最优。我一般建议 α 从 0.5 开始,随着迭代逐渐减小到 0.01 左右。
3.3 算法伪代码:把公式变成步骤
理论说完了,我们来看看实际怎么操作。下面是我常用的伪代码版本:
算法:萤火虫算法 (Firefly Algorithm)
输入:种群大小 n,最大迭代次数 MaxGen,参数 β₀, γ, α
输出:最优解 x_best
1. 初始化 n 只萤火虫的位置 x_i (i=1,2,...,n)
2. 计算每只萤火虫的亮度 I_i = f(x_i)
3. 找到当前最亮萤火虫 x_best
4. for t = 1 to MaxGen:
5. for i = 1 to n:
6. for j = 1 to n:
7. if I_j > I_i:
8. // 计算距离
9. r = ||x_i - x_j||
10. // 计算吸引度
11. β = β₀ × exp(-γ × r²)
12. // 移动萤火虫 i 向 j
13. x_i = x_i + β × (x_j - x_i) + α × (rand - 0.5)
14. // 边界处理
15. 将 x_i 限制在搜索空间内
16. end if
17. end for
18. end for
19. // 更新亮度
20. 重新计算所有萤火虫的亮度
21. 更新全局最优 x_best
22. // 衰减随机步长(可选)
23. α = α × 0.97
24. end for
25. return x_best
重要提醒:第7行的判断条件「I_j > I_i」意味着每只萤火虫只向比自己亮的个体移动。如果所有萤火虫亮度相同,它们就只会随机游走。我曾经遇到过这种情况——目标函数太平坦,导致算法收敛极慢。解决办法是适当增加随机项 α 的值。
3.4 知识体系结构图
下面我用一张SVG图来总结本章的核心逻辑:
3.5 一个简单的例子
为了让你更直观地理解,我们来看一个一维的例子。假设我们要最大化函数 f(x) = -x² + 4x,搜索范围 [0, 4]。
初始化3只萤火虫:
- 萤火虫A:x=1.0,亮度 f(1)=3
- 萤火虫B:x=2.5,亮度 f(2.5)=3.75
- 萤火虫C:x=3.5,亮度 f(3.5)=1.75
设置参数:β₀=1,γ=1,α=0.2
第一轮迭代:
- 萤火虫A(亮度3)看到B更亮(3.75),向B移动。距离 r=1.5,吸引度 β=exp(-1×1.5²)=0.105。新位置 x_A = 1.0 + 0.105×(2.5-1.0) + 0.2×(rand-0.5) ≈ 1.16
- 萤火虫C(亮度1.75)看到A和B都更亮,选择向最亮的B移动。距离 r=1.0,吸引度 β=exp(-1)=0.368。新位置 x_C = 3.5 + 0.368×(2.5-3.5) + 0.2×(rand-0.5) ≈ 3.13
- 萤火虫B最亮,不移动,只做随机扰动
你看,经过几轮迭代,所有萤火虫都会聚集到最优解 x=2 附近。这就是萤火虫算法的核心逻辑——弱者向强者学习,强者保持探索。
个人经验:在实际项目中,我通常会把种群大小设为20-50之间。太小了容易早熟收敛,太大了计算量吃不消。另外,我建议每轮迭代后都检查一下种群多样性——如果所有萤火虫都挤在一起了,适当增大随机步长 α 可以帮它们「散开」。