人工蜂群算法(ABC)

「开学季干货」:聚焦知识梳理与经验分享 10w+人浏览 603人参与

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()

        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值