蝙蝠优化算法在 Ackley 函数优化中的改进实验
蝙蝠优化算法在 Ackley 函数优化中的改进实验
BA算法简介:
- 蝙蝠优化算法(Bat Algorithm, BA)是一种基于蝙蝠回声定位行为的元启发式算法,由 Xin-She Yang 在 2010 年提出。该算法模仿了蝙蝠通过回声定位找到猎物或避开障碍物的机制,以解决优化问题。蝙蝠算法因其简单性和有效性在许多优化问题中得到了广泛应用。
问题描述:
- 计算 \(Ackley\) 函数的最小值
\(Ackley\) 函数:
\(Ackley\) 函数是一个经典的连续、多峰测试函数,常用于评估优化算法的性能。其全局最小值为 0,位于坐标原点 \((0, 0, ..., 0)\) 处。该函数具有多个局部极小值,对算法的全局搜索能力和跳出局部最优的能力提出挑战。
对于一个 n 维向量 \((x_1, x_2, x_3, ..., x_n) Ackley\) 函数定义为:
优化前算法核心流程:
因篇幅限制代码放在仓库托管
初始化:
初始化一个蝙蝠种群,每个蝙蝠有以下属性:
- 速度(n 维向量)
- 响度(决定蝙蝠的位置随机扰动的上下限)
- 脉冲率(决定蝙蝠触发局部搜索的概率)
- 位置(n 维向量,代表候选解)
- 适应度(这里就是 \(Ackley\) 函数的值)
每次迭代:
-
遍历所有蝙蝠个体,计算每个个体的适应度值,找到当前种群中适应度最小的个体作为当前全局最优解
-
为每个蝙蝠随机生成飞行频率,结合当前蝙蝠位置、全局最优位置和频率更新蝙蝠的飞行速度,再通过速度更新蝙蝠的位置,超出搜索边界的位置做截断处理拉回合法范围
-
脉冲率触发局部搜索:生成随机数,若随机数大于当前蝙蝠的脉冲率,则在当前全局最优解附近,结合当前所有蝙蝠的平均响度做随机扰动,生成新的候选位置
-
计算新生成候选位置的适应度值
-
新解接受判断:生成随机数,若随机数小于当前蝙蝠的响度,且新位置的适应度优于原位置的适应度,则接受新位置作为该蝙蝠的当前位置,同时按照衰减系数降低该蝙蝠的响度,按照增强系数提升该蝙蝠的脉冲率
-
全局最优更新:如果新位置的适应度优于当前全局最优适应度,更新全局最优位置和全局最优适应度
-
判断是否达到最大迭代次数,未达到则进入下一轮迭代,达到则终止迭代输出全局最优解
改进方案:
优化一
引入自适应参数 G:
式中:\(\text{gen}\) 为当前迭代次数,\(\text{initgen}\) 为预设总迭代次数,系数 \(g\) 取值范围 \([0,1]\),随迭代推进单调递减。
使用新的位置更新公式:
//更新位置
ArrayRealVector newPosition = (ArrayRealVector) bat.getAnswer().add(newVelocity.mapMultiply(G));
随着迭代次数增加,自适应系数 G 逐渐减小,蝙蝠的飞行步长也相应减小,表示从“大步流星”的全局搜索,逐渐过渡到“小步慢走”的精细搜索。
使用新的局部扰动更新公式:
//在最优解附近随机游走
newPosition = (ArrayRealVector) globalBest.getAnswer().add(
epsilon.mapMultiply(avgLoudness).mapMultiply(G));
相比标准 BA 的局部搜索逻辑,新增动态衰减系数 G 控制扰动幅度:迭代前期 G 值大,扰动范围广,增强跳出局部最优的全局搜索能力;迭代后期 G 值小,扰动范围收窄,提升局部收敛精度。
优化二
引入离群度:
设精英集合为:
其中,\(\mathbf{x}_i\) 表示第 \(i\) 个精英个体的位置,\(m\) 为精英个体数量。
第 \(i\) 个精英个体的离群度定义为其与其他精英个体的平均欧氏距离(如果是离散问题可以转化为汉明距离):
展开为:
其中,\(D_i\) 越大,表示该精英个体与其他精英个体距离越远,离群程度越高。
改变选择最优个体的方式
对于\(E\)中每个蝙蝠个体\(\mathbf{x}_i\):
\(\hat{x}\)为归一化后的 \(Ackley\) 值,\(\hat{D_i}\) 为归一化后的离群度
最好的精英个体即为 \(Ackley\) 值与归一化后的离散度差值最小的那个:
可见这个新解更加具有多样性,但是在迭代后期离群度可能会影响对于最小值的判断,因此引出接下来的更新精英集合数的方法
更新精英集合数量
每次迭代更新精英集合数\(m\)
其中 \(inittratio\) 为提前设定的比值, \(popsize\) 为初始种群数量, \(A\) 为响度
这样精英集合数量随响度逐渐衰减,在迭代后期就能按照适应度正确的到达新位置
Java代码:
//新的选择精英逻辑,基于适应度排序并返回最优个体的副本
public static Batis findBest(Batis[] population) {
//按 ackleyValue 从小到大排序
Arrays.sort(population, Comparator.comparingDouble(b -> b.getAckleyValue()));
//取前 bestSize 个作为候选精英
int eliteSize = Math.min(bestSize, population.length);
Batis[] elites = Arrays.copyOf(population, eliteSize);
//对每个精英计算离群度,与其他所有精英的欧氏距离均值
for (int i = 0; i < elites.length; i++) {
double totalDistance = 0.0;
int count = 0;
for (int j = 0; j < elites.length; j++) {
if (i != j) {
totalDistance += RealVectorUtils.euclideanDistance(elites[i], elites[j], Batis::getAnswer);
count++;
}
}
//计算平均离群度,避免除零
double outlierDegree = (count > 0) ? totalDistance / count : 1.0;
elites[i].setOutlierDegree(outlierDegree);
}
//找到 ackley 和 outlierDegree 的最大最小值用于归一化
double minAckley = Double.MAX_VALUE;
double maxAckley = -Double.MAX_VALUE;
double minOutlier = Double.MAX_VALUE;
double maxOutlier = -Double.MAX_VALUE;
for (Batis bat : elites) {
minAckley = Math.min(minAckley, bat.getAckleyValue());
maxAckley = Math.max(maxAckley, bat.getAckleyValue());
minOutlier = Math.min(minOutlier, bat.getOutlierDegree());
maxOutlier = Math.max(maxOutlier, bat.getOutlierDegree());
}
//归一化后直接相减,找最小的
Batis bestBat = elites[0];
double minScore = Double.MAX_VALUE;
for (Batis bat : elites) {
//归一化
double ackleyNorm = (maxAckley > minAckley)
? (bat.getAckleyValue() - minAckley) / (maxAckley - minAckley)
: 0.5;
double outlierNorm = (maxOutlier > minOutlier)
? (bat.getOutlierDegree() - minOutlier) / (maxOutlier - minOutlier)
: 0.5;
double score = ackleyNorm - outlierNorm;
if (score < minScore) {
minScore = score;
bestBat = bat;
}
}
//返回副本避免引用污染
return new Batis(
RealVectorUtils.copy(bestBat.getAnswer()),
RealVectorUtils.copy(bestBat.getSpeed()),
bestBat.getLoudness(),
bestBat.getPulseRate(),
bestBat.getAckleyValue(),
bestBat.getOutlierDegree());
}
改进前后效率对比

可见原始算法在280代左右就陷入了局部最优,然而改进后的算法能够保持持续下降而不陷入局部最优,效果很好
浙公网安备 33010602011771号