
麻雀搜索算法(SSA):如何通过模拟麻雀行为提升全局搜索能力?
在大自然的舞台上,麻雀或许是最不起眼的“演员”,但它们却有着令人惊叹的生存智慧。
今天,我们要聊的是一种从麻雀身上汲取灵感的神奇算法—麻雀搜索算法(SSA)。
深入探讨SSA算法的灵感来源、基本原理、算法流程以及如何通过代码实现它。
一、前言|麻雀搜索算法的来源
麻雀搜索算法(Sparrow Search Algorithm,SSA)是一种新型的群体智能优化算法,其灵感来源于麻雀的觅食和反捕食行为。
在自然界中,麻雀通常会分为以下几种角色:
- 发现者(Producer):负责寻找食物,为群体提供觅食方向。
- 加入者(Scrounger):跟随发现者获取食物。
- 侦察者(Scout):负责监视周围环境,当发现危险时发出警报并引导群体转移到安全区域。
这些行为体现了麻雀群体的分工协作和动态适应性,为算法的设计提供了基础。
麻雀搜索算法由东华大学的Xue和Shen于2020年首次提出。
自提出以来,该算法因其高效性和适应性受到了广泛关注,并在多个领域得到了应用。
例如电力负荷预测、无人机航迹规划、图像处理和神经网络参数优化等。
随着研究的深入,许多学者对麻雀搜索算法进行了改进,以提高其全局搜索能力和收敛速度。
例如,引入混沌初始化策略、动态惯性权重等改进方法。
这些改进使得麻雀搜索算法在解决复杂优化问题时表现更加出色。
二、原理|麻雀搜索算法的原理
麻雀搜索算法(SSA)通过模拟麻雀的群体觅食行为,成功地避免了局部最优,展现出强大的优化能力。
麻雀群体在觅食过程中表现出以下特点:
- 麻雀群体中存在分工,包括发现者(Producer)、加入者(Scrounger)和侦察者(Scout)。
- 发现者负责寻找食物丰富的区域,加入者跟随发现者获取食物,侦察者负责监测环境中的危险。
- 当发现危险时,侦察者会引导群体转移到安全区域,避免被捕食。
▲ 麻雀优化算法动态可视化
SSA通过模拟这些行为来实现优化,其核心在于通过全局搜索和局部搜索的平衡,以及反捕食机制来避免陷入局部最优。
01 数学原理|Theory
接下来,将详细地描述麻雀搜索算法的数学原理,包括发现者、加入者和侦察者的位置更新公式及其背后的逻辑。
3. 加入者的位置更新
加入者跟随发现者获取食物,其位置更新公式为:
4. 侦察者的位置更新
侦察者负责监测环境中的危险,其位置更新公式为:
02 算法流程|Process
麻雀搜索算法的开发流程描述如下:
三、实践|麻雀搜索算法应用
麻雀搜索算法是一种受麻雀觅食和反捕食行为启发的群体智能优化算法。
下面我将用Python实现一个简单的SSA案例,用于求解Ackley函数优化问题,并提供完整的实现代码和可视化。
01 问题定义|Definition
Ackley函数是一个常用的多峰测试函数,其全局最小值在原点(0,0,...,0)处,函数值为0。
该函数具有许多局部极小值,对优化算法是一个很好的测试。
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.animation import FuncAnimation
from IPython.display import HTML
import time
# Ackley函数
defackley(x):
a = 20
b = 0.2
c = 2 * np.pi
d = len(x)
sum_sq = sum([xi**2for xi in x])
sum_cos = sum([np.cos(c * xi) for xi in x])
term1 = -a * np.exp(-b * np.sqrt(sum_sq / d))
term2 = -np.exp(sum_cos / d)
return term1 + term2 + a + np.exp(1)
02 算法建模|Modeling
麻雀搜索算法的建模过程如下:
# 麻雀搜索算法
defsparrow_search_algorithm_with_history(obj_func, dim, lb, ub, max_iter, n_sparrows):
# 初始化
positions = np.random.uniform(lb, ub, (n_sparrows, dim))
fitness = np.array([obj_func(p) for p in positions])
# 历史记录
history = {
'positions': [positions.copy()],
'best_positions': [],
'best_fitness': [],
'worst_positions': []
}
# 找出当前最佳和最差
best_index = np.argmin(fitness)
worst_index = np.argmax(fitness)
best_position = positions[best_index].copy()
best_fitness = fitness[best_index]
worst_position = positions[worst_index].copy()
history['best_positions'].append(best_position.copy())
history['best_fitness'].append(best_fitness)
history['worst_positions'].append(worst_position.copy())
# 迭代参数
ST = 0.6# 安全阈值
PD = 0.7# 发现者比例
SD = 0.2# 警戒者比例
n_p = int(n_sparrows * PD)
n_s = int(n_sparrows * SD)
# 迭代过程
for t inrange(max_iter):
if t % 10 == 0or t == max_iter-1or t == 0:
print(f"迭代 {t+1:3d}/{max_iter} | 当前最佳适应度: {best_fitness:.6f}")
# 更新发现者位置
R2 = np.random.rand()
for i inrange(n_p):
if R2 < ST:
alpha = np.random.randn()
for d inrange(dim):
positions[i,d] *= np.exp(-alpha * t / max_iter)
else:
Q = np.random.randn()
for d inrange(dim):
positions[i,d] += Q * np.random.rand()
positions[i] = np.clip(positions[i], lb, ub)
# 更新跟随者位置
for i inrange(n_p, n_sparrows):
if i > n_sparrows / 2:
Q = np.random.randn()
for d inrange(dim):
positions[i,d] = Q * np.exp((worst_position[d] - positions[i,d]) / i**2)
else:
A = np.random.randint(-1, 2, dim)
A_plus = A.T @ np.linalg.pinv(A @ A.T)
for d inrange(dim):
positions[i,d] = best_position[d] + np.abs(positions[i,d] - best_position[d]) * A_plus[d]
positions[i] = np.clip(positions[i], lb, ub)
# 更新警戒者位置
for i inrange(n_s):
if fitness[i] > best_fitness:
beta = np.random.randn()
for d inrange(dim):
positions[i,d] = best_position[d] + beta * np.abs(positions[i,d] - best_position[d])
elif fitness[i] == best_fitness:
K = np.random.rand() * 2 - 1
for d inrange(dim):
positions[i,d] = positions[i,d] + K * (np.abs(positions[i,d] - worst_position[d]) /
(fitness[i] - worst_position[d] + 1e-50))
positions[i] = np.clip(positions[i], lb, ub)
# 重新计算适应度
fitness = np.array([obj_func(p) for p in positions])
# 更新全局最佳和最差
current_best_index = np.argmin(fitness)
current_worst_index = np.argmax(fitness)
if fitness[current_best_index] < best_fitness:
best_position = positions[current_best_index].copy()
best_fitness = fitness[current_best_index]
worst_position = positions[current_worst_index].copy()
# 记录历史
history['positions'].append(positions.copy())
history['best_positions'].append(best_position.copy())
history['best_fitness'].append(best_fitness)
history['worst_positions'].append(worst_position.copy())
return history
03 算法运行|Runing
接下来将进行参数设置、运行麻雀搜索算法。并实现一个动态可视化版本,展示随着最优适应度曲线的变化,目标函数搜索过程的动态演化。
# ==================================================
print("准备运行麻雀搜索算法...")
print("="*50)
print("麻雀搜索算法 (SSA) 开始运行")
print(f"麻雀数量: {30}")
print(f"最大迭代次数: {50}")
print(f"搜索空间维度: {2}")
print(f"搜索范围: [-5, 5]")
print("="*50)
time.sleep(1)
# 参数设置
dim = 2
lb = -5
ub = 5
max_iter = 50
n_sparrows = 30
# 运行算法并记录历史
history = sparrow_search_algorithm_with_history(ackley, dim, lb, ub, max_iter, n_sparrows)
# 最终结果
print("="*50)
print("优化完成!")
print(f"找到的最佳解: [{history['best_positions'][-1][0]:.4f}{history['best_positions'][-1][1]:.4f}]")
print(f"最佳适应度值: {history['best_fitness'][-1]:.6f}")
print("="*50)
time.sleep(1)
print("生成可视化结果...")
time.sleep(1)
# ==================================================
# 准备可视化数据
x = np.linspace(lb, ub, 100)
y = np.linspace(lb, ub, 100)
X, Y = np.meshgrid(x, y)
Z = np.zeros_like(X)
for i inrange(X.shape[0]):
for j inrange(X.shape[1]):
Z[i,j] = ackley([X[i,j], Y[i,j]])
# 创建图形
plt.rcParams['font.sans-serif'] = ['SimHei'] # 设置中文显示
plt.rcParams['axes.unicode_minus'] = False# 解决负号显示问题
fig = plt.figure(figsize=(18, 6), dpi=100)
fig.suptitle('麻雀搜索算法动态可视化', fnotallow=16)
# 统一子图尺寸
gs = fig.add_gridspec(2, 3, width_ratios=[1, 1, 1], height_ratios=[1, 1])
# 3D曲面图
ax1 = fig.add_subplot(gs[:, 0], projectinotallow='3d')
surf = ax1.plot_surface(X, Y, Z, cmap='viridis', alpha=0.6)
fig.colorbar(surf, ax=ax1, shrink=0.6, aspect=10, label='函数值')
scatter = ax1.scatter([], [], [], c='red', s=50, label='麻雀位置')
best_scatter = ax1.scatter([], [], [], c='blue', marker='*', s=200, label='最优解')
ax1.set_title('3D函数曲面与种群分布', fnotallow=12)
ax1.set_xlabel('x1', fnotallow=10)
ax1.set_ylabel('x2', fnotallow=10)
ax1.set_zlabel('f(x)', fnotallow=10)
ax1.legend(loc='upper right', fnotallow=8)
# 2D等高线图
ax2 = fig.add_subplot(gs[:, 1])
contour = ax2.contourf(X, Y, Z, levels=50, cmap='viridis')
fig.colorbar(contour, ax=ax2, shrink=0.6, aspect=10, label='函数值')
scatter2d = ax2.scatter([], [], c='red', s=30, label='麻雀位置')
best_scatter2d = ax2.scatter([], [], c='blue', marker='*', s=100, label='最优解')
ax2.set_title('2D等高线与种群分布', fnotallow=12)
ax2.set_xlabel('x1', fnotallow=10)
ax2.set_ylabel('x2', fnotallow=10)
ax2.legend(loc='upper right', fnotallow=8)
# 收敛曲线
ax3 = fig.add_subplot(gs[0, 2])
convergence_line, = ax3.plot([], [], 'b-', linewidth=2, label='最佳适应度')
current_point = ax3.scatter([], [], c='red', s=50, label='当前值')
ax3.set_title('适应度收敛曲线', fnotallow=12)
ax3.set_xlabel('迭代次数', fnotallow=10)
ax3.set_ylabel('适应度值', fnotallow=10)
ax3.grid(True, linestyle='--', alpha=0.6)
ax3.set_xlim(0, max_iter)
ax3.set_ylim(0, max(history['best_fitness']))
ax3.legend(loc='upper right', fnotallow=8)
# 参数显示
ax4 = fig.add_subplot(gs[1, 2])
ax4.axis('off')
info_text = ax4.text(0.1, 0.5, '', fnotallow=10, bbox=dict(facecolor='white', alpha=0.8))
plt.tight_layout()
# 动画更新函数
defupdate(frame):
# 更新3D图
current_pos = history['positions'][frame]
current_z = np.array([ackley(p) for p in current_pos])
scatter._offsets3d = (current_pos[:,0], current_pos[:,1], current_z)
best_pos = history['best_positions'][frame]
best_z = ackley(best_pos)
best_scatter._offsets3d = ([best_pos[0]], [best_pos[1]], [best_z])
# 更新2D图
scatter2d.set_offsets(current_pos)
best_scatter2d.set_offsets([best_pos])
# 更新收敛曲线
x_data = range(frame+1)
y_data = history['best_fitness'][:frame+1]
convergence_line.set_data(x_data, y_data)
current_point.set_offsets([[frame, history['best_fitness'][frame]]])
# 更新文本信息
info = f"迭代次数: {frame}\n"
info += f"最佳适应度: {history['best_fitness'][frame]:.6f}\n"
info += f"最佳位置: [{best_pos[0]:.4f}, {best_pos[1]:.4f}]\n"
info += f"麻雀数量: {len(current_pos)}\n"
info += f"发现者比例: 70%\n"
info += f"警戒者比例: 20%"
info_text.set_text(info)
return scatter, best_scatter, scatter2d, best_scatter2d, convergence_line, current_point, info_text
# 创建动画
ani = FuncAnimation(fig, update, frames=max_iter+1, interval=500, blit=True)
# 显示动画
plt.close()
HTML(ani.to_jshtml())
结果显示|结果可视化
尽管麻雀搜索算法具有许多优点,但在处理复杂优化问题时仍存在一些不足,例如全局搜索能力较弱、容易陷入局部最优等。
为此,研究人员提出了多种改进策略:
- 混沌初始化:利用混沌序列(如立方映射、Tent混沌序列)初始化种群,提高种群的多样性和分布均匀性。
- 引入其他算法机制:结合蝴蝶优化算法(BOA)、灰狼优化算法(GWO)等其他智能优化算法的机制,增强全局搜索能力。
- 动态惯性权重:引入动态惯性权重,平衡算法在迭代过程中的全局探索和局部搜索能力。
- 正余弦和柯西变异:在发现者位置更新中引入正余弦策略,在加入者位置中引入柯西变异,提高算法的全局寻优能力和收敛速度。
结语
麻雀搜索算法通过模拟麻雀的觅食和反捕食行为,利用发现者和追随者的协作机制,逐步逼近全局最优解。它不仅原理直观,而且实现简单,适用于多种复杂的优化问题。
本文转载自Fairy Girlhub,作者:Fairy Girlhub
