0.引言
人工蜂群算法(Artificial Bee Colony Algorithm, ABC)是一种基于蜜蜂觅食行为的群体智能优化算法,由Karaboga于2005年提出。算法模拟蜜蜂在自然界中通过分工协作寻找蜜源的过程,适用于解决连续空间优化问题,如函数优化、工程设计和机器学习等领域。
1.原理
ABC算法即是将一些不同常用环节的算法组合到一起,再套上个小蜜蜂🐝的外壳,新瓶装老酒!不过该算法中一些策略确实十分有效。
言归正传,介绍一下本算法中创造的几个名词,以及算法的背景:
- 侦察蜂:蜂群会派出一些“侦察蜂”到各处随机寻找花蜜。
- 发现蜜源:当一只侦察蜂找到一个丰富的蜜源时,它会飞回蜂巢,通过一种叫做“摇摆舞”的方式向同伴传递信息。舞蹈的持续时间、角度和活力编码了蜜源的三个关键信息:
方向:蜜源相对于太阳的位置。
距离:蜜源离蜂巢有多远。
质量:蜜源的花蜜量和甜度(质量)。
- 招募跟随蜂:其他工蜂(“跟随蜂”)观察到舞蹈后,会根据舞蹈的活力(即蜜源的质量)以一定的概率决定跟随哪个侦察蜂去采蜜。质量越高的蜜源,吸引力越大,被跟随的概率越高。
该算法以侦察蜂为引领,雇佣蜂负责维护,再由跟随蜂细致的探索,从而找到问题的解决办法。
换种我们能听懂的语言,侦察蜂可以理解为创造新解或者扰动阶段,雇佣蜂是解本身,吸引跟随蜂,而跟随蜂就是局部搜索阶段,解的质量越好,跟随蜂越多,搜索也就越细致。
这里区别一下三种蜂群,侦察蜂的主要职责是开发蜜源,可以作为初始解的生成,以及当一些雇佣蜂多次尝试失败后,可以暂时转型为侦察蜂,寻找新的蜜源;而雇佣蜂的主要职责是开发和守护,即使多次探索失败,吸引不到一只跟随蜂,也坚持探索,说不定下一次的探索就会挖到大蜜源,直到到达失败次数上限,耗光它的耐心,才会变成一只侦察蜂飞走;最后是跟随蜂,跟随蜂根据雇佣蜂发出的信号,谁的蜜多,大概率会选择谁,但只是概率,保证了算法的随机性和活力。当跟随蜂发现了更好的蜜源,相应的跟随蜂在下一轮迭代中,守护的蜜源更新为这个更好的蜜源。
2.算法流程
首先得约定几个参数和变量:
-
SN
: 蜜源的数量(也是雇佣蜂的数量)。 -
D
: 待优化问题的维度。 -
limit: 一个蜜源可以被连续开发的最大失败次数。如果超过此限度仍未改进,则该蜜源将被放弃。
-
MCN
(Maximum Cycle Number): 最大迭代次数。
2.1初始化
1.随机生成几个解
2.为每个解计算适应度
3.初始化一个计数器数组trial,记录每个解的探索次数
2.2 雇佣蜂阶段
每只雇佣蜂开发自身所处蜜源一次。
for 每一个雇佣蜂 i (i from 1 to SN):
1. 在 [-1, 1] 范围内随机选择一个随机数 φ。
2. 在 [1, D] 范围内随机选择一个维度 j。
3. 在除了 i 以外的蜜源中,随机选择一个蜜源 k (k ∈ {1, 2, ..., SN}, k ≠ i)。
4. 生成一个新候选蜜源 V_i:
v_ij = x_ij + φ * (x_ij - x_kj) # 只在第 j 维上发生变化,其他维度与 X_i 相同。
# 注意:如果 v_ij 超出了搜索边界,需要将其拉回边界。
5. 计算新蜜源 V_i 的适应度值 fitness(V_i)。
6. 【贪婪选择】:
if fitness(V_i) > fitness(X_i):
用 V_i 替换原来的蜜源 X_i
trial_i = 0 # 重置计数器,因为该蜜源被改进了
else:
保留原来的蜜源 X_i
trial_i = trial_i + 1 # 失败次数+1
2.3 跟随蜂阶段
轮盘赌选择与开发
for 每一只跟随蜂 (一共 SN 只跟随蜂):
a. 根据概率分布 p = [p1, p2, ..., p_SN] 运行一次轮盘赌,选出一个蜜源 X_i。
b. 一旦 X_i 被选中,跟随蜂就会像雇佣蜂一样,对 X_i 执行完全相同的邻域搜索操作(即上面的步骤1-6)
2.4 侦察蜂阶段
判断是否有失败次数超过limit的蜜源。如果有则重新探索蜜源。每次迭代最多只抛弃一个蜜源,即只查看max(trial_i)。抛弃方式较为温和。
检查所有 trial_i 计数器:
if max(trial_i) > limit:
找到那个 trial_i 值最大的蜜源 X_i(即被改进失败次数最多的)。
if 该蜜源 X_i 不是历史全局最优解: # 这是一个常见保护措施
放弃这个蜜源 X_i。
负责它的雇佣蜂转变为侦察蜂。
这只侦察蜂随机初始化一个全新的蜜源 Z 来替换 X_i:
Z_j = lower_bound_j + rand(0,1) * (upper_bound_j - lower_bound_j)
计算新蜜源 Z 的适应度值,重置 trial_i = 0。
3.问题解决
测试一个函数寻优,我们选择一个经典的多峰测试函数——Rastrigin函数来作为优化目标。这个函数有很多局部极小值。
import numpy as np
import matplotlib.pyplot as plt
# 1. 定义目标函数 - Rastrigin
def rastrigin(x):
"""
计算Rastrigin函数值。
全局最优解: x = [0, 0, ..., 0], f(x) = 0
"""
n = len(x)
return 10 * n + sum([(xi**2 - 10 * np.cos(2 * np.pi * xi)) for xi in x])
# 2. 适应度函数(因为我们在求最小值)
def calculate_fitness(func_value):
"""
将函数值转换为适应度值。
对于最小值问题,函数值越小,适应度应该越高。
"""
if func_value >= 0:
return 1 / (1 + func_value)
else:
return 1 + abs(func_value)
# 3. 人工蜂群算法主函数
def artificial_bee_colony(obj_func, dim, bounds, colony_size=20, limit=50, max_iter=100):
"""
人工蜂群算法实现
参数:
- obj_func: 目标函数
- dim: 问题维度
- bounds: 搜索边界 [(min, max), (min, max), ...]
- colony_size: 蜂群大小(蜜源数量=雇佣蜂数量=跟随蜂数量)
- limit: 最大尝试次数限制
- max_iter: 最大迭代次数
返回:
- global_best_solution: 全局最优解
- global_best_value: 全局最优值
- convergence_curve: 收敛曲线
"""
# 初始化变量
n = colony_size # 蜜源数量
convergence_curve = np.zeros(max_iter)
# 初始化蜜源
foods = np.zeros((n, dim))
for j in range(dim):
foods[:, j] = np.random.uniform(bounds[j][0], bounds[j][1], n)
# 计算初始适应度
obj_values = np.array([obj_func(x) for x in foods])
fitness_values = np.array([calculate_fitness(fv) for fv in obj_values])
# 初始化 trial 计数器
trial = np.zeros(n, dtype=int)
# 记录全局最优
best_index = np.argmin(obj_values)
global_best_solution = foods[best_index].copy()
global_best_value = obj_values[best_index]
# 主循环
for cycle in range(max_iter):
# 阶段一: 雇佣蜂阶段
for i in range(n):
# 随机选择维度
j = np.random.randint(0, dim)
# 随机选择另一个蜜源 (k ≠ i)
k = np.random.choice([idx for idx in range(n) if idx != i])
# 生成新候选解
phi = np.random.uniform(-1, 1)
new_solution = foods[i].copy()
new_solution[j] = foods[i][j] + phi * (foods[i][j] - foods[k][j])
# 边界处理
new_solution[j] = np.clip(new_solution[j], bounds[j][0], bounds[j][1])
# 评估新解
new_obj_value = obj_func(new_solution)
new_fitness = calculate_fitness(new_obj_value)
# 贪婪选择
if new_fitness > fitness_values[i]:
foods[i] = new_solution
obj_values[i] = new_obj_value
fitness_values[i] = new_fitness
trial[i] = 0
else:
trial[i] += 1
# 阶段二: 计算选择概率
total_fitness = np.sum(fitness_values)
if total_fitness > 0:
probabilities = fitness_values / total_fitness
else:
probabilities = np.ones(n) / n
# 跟随蜂阶段
for _ in range(n):
# 轮盘赌选择
selected_idx = np.random.choice(n, p=probabilities)
# 与雇佣蜂阶段相同的搜索过程
j = np.random.randint(0, dim)
k = np.random.choice([idx for idx in range(n) if idx != selected_idx])
phi = np.random.uniform(-1, 1)
new_solution = foods[selected_idx].copy()
new_solution[j] = foods[selected_idx][j] + phi * (foods[selected_idx][j] - foods[k][j])
new_solution[j] = np.clip(new_solution[j], bounds[j][0], bounds[j][1])
new_obj_value = obj_func(new_solution)
new_fitness = calculate_fitness(new_obj_value)
if new_fitness > fitness_values[selected_idx]:
foods[selected_idx] = new_solution
obj_values[selected_idx] = new_obj_value
fitness_values[selected_idx] = new_fitness
trial[selected_idx] = 0
else:
trial[selected_idx] += 1
# 阶段三: 侦察蜂阶段
if np.max(trial) > limit:
# 找到 trial 值最大的蜜源
abandoned_idx = np.argmax(trial)
# 确保不抛弃当前最优解
if not np.array_equal(foods[abandoned_idx], global_best_solution):
# 生成新的随机解
for j in range(dim):
foods[abandoned_idx][j] = np.random.uniform(bounds[j][0], bounds[j][1])
obj_values[abandoned_idx] = obj_func(foods[abandoned_idx])
fitness_values[abandoned_idx] = calculate_fitness(obj_values[abandoned_idx])
trial[abandoned_idx] = 0
# 更新全局最优
current_best_idx = np.argmin(obj_values)
if obj_values[current_best_idx] < global_best_value:
global_best_solution = foods[current_best_idx].copy()
global_best_value = obj_values[current_best_idx]
convergence_curve[cycle] = global_best_value
# 打印进度
if cycle % 20 == 0:
print(f"Iteration {cycle}: Best Value = {global_best_value:.6f}")
return global_best_solution, global_best_value, convergence_curve
# 4. 运行算法并可视化结果
if __name__ == "__main__":
# 设置参数
dim = 2 # 二维问题
bounds = [(-5.12, 5.12)] * dim # Rastrigin函数的推荐搜索范围
# 运行ABC算法
best_solution, best_value, convergence = artificial_bee_colony(
obj_func=rastrigin,
dim=dim,
bounds=bounds,
colony_size=30,
limit=100,
max_iter=200
)
print(f"\n优化结果:")
print(f"最优解: {best_solution}")
print(f"最优值: {best_value:.8f}")
print(f"理论最优值: 0.0")
# 绘制收敛曲线
plt.figure(figsize=(10, 6))
plt.plot(convergence, 'b-', linewidth=2)
plt.title('ABC Algorithm Convergence')
plt.xlabel('Iteration')
plt.ylabel('Best Function Value')
plt.grid(True)
plt.yscale('log') # 使用对数坐标更好地显示收敛过程
plt.show()