• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
xtr-hub
博客园    首页    新随笔    联系   管理    订阅  订阅

蝙蝠优化算法在 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\) 函数定义为:

\[f(\mathbf{x}) = -20 \exp\left( -0.2 \sqrt{\frac{1}{n} \sum_{i=1}^n x_i^2} \right) - \exp\left( \frac{1}{n} \sum_{i=1}^n \cos(2\pi x_i) \right) + 20 + e \]


优化前算法核心流程:

因篇幅限制代码放在仓库托管

初始化:

初始化一个蝙蝠种群,每个蝙蝠有以下属性:

  • 速度(n 维向量)
  • 响度(决定蝙蝠的位置随机扰动的上下限)
  • 脉冲率(决定蝙蝠触发局部搜索的概率)
  • 位置(n 维向量,代表候选解)
  • 适应度(这里就是 \(Ackley\) 函数的值)

每次迭代:

  1. 遍历所有蝙蝠个体,计算每个个体的适应度值,找到当前种群中适应度最小的个体作为当前全局最优解

  2. 为每个蝙蝠随机生成飞行频率,结合当前蝙蝠位置、全局最优位置和频率更新蝙蝠的飞行速度,再通过速度更新蝙蝠的位置,超出搜索边界的位置做截断处理拉回合法范围

  3. 脉冲率触发局部搜索:生成随机数,若随机数大于当前蝙蝠的脉冲率,则在当前全局最优解附近,结合当前所有蝙蝠的平均响度做随机扰动,生成新的候选位置

  4. 计算新生成候选位置的适应度值

  5. 新解接受判断:生成随机数,若随机数小于当前蝙蝠的响度,且新位置的适应度优于原位置的适应度,则接受新位置作为该蝙蝠的当前位置,同时按照衰减系数降低该蝙蝠的响度,按照增强系数提升该蝙蝠的脉冲率

  6. 全局最优更新:如果新位置的适应度优于当前全局最优适应度,更新全局最优位置和全局最优适应度

  7. 判断是否达到最大迭代次数,未达到则进入下一轮迭代,达到则终止迭代输出全局最优解


改进方案:

优化一

引入自适应参数 G:

\[g = \sqrt{1 - \frac{\text{gen}}{\text{initgen}}} \]

式中:\(\text{gen}\) 为当前迭代次数,\(\text{initgen}\) 为预设总迭代次数,系数 \(g\) 取值范围 \([0,1]\),随迭代推进单调递减。


使用新的位置更新公式:

\[x_{i}^{t+1} = x_{i}^{t} + G \times v_{i}^{t+1} \]

//更新位置
ArrayRealVector newPosition = (ArrayRealVector) bat.getAnswer().add(newVelocity.mapMultiply(G));

随着迭代次数增加,自适应系数 G 逐渐减小,蝙蝠的飞行步长也相应减小,表示从“大步流星”的全局搜索,逐渐过渡到“小步慢走”的精细搜索。


使用新的局部扰动更新公式:

\[\mathbf{x}_i^{\text{new}} = \mathbf{x}^* + G \cdot \bar{A} \cdot \boldsymbol{\epsilon} \]

//在最优解附近随机游走
newPosition = (ArrayRealVector) globalBest.getAnswer().add(
                            epsilon.mapMultiply(avgLoudness).mapMultiply(G));

相比标准 BA 的局部搜索逻辑,新增动态衰减系数 G 控制扰动幅度:迭代前期 G 值大,扰动范围广,增强跳出局部最优的全局搜索能力;迭代后期 G 值小,扰动范围收窄,提升局部收敛精度。


优化二

引入离群度:

设精英集合为:

\[E = \{ \mathbf{x}_1, \mathbf{x}_2, \cdots, \mathbf{x}_m \} \]

其中,\(\mathbf{x}_i\) 表示第 \(i\) 个精英个体的位置,\(m\) 为精英个体数量。

第 \(i\) 个精英个体的离群度定义为其与其他精英个体的平均欧氏距离(如果是离散问题可以转化为汉明距离):

\[D_i = \frac{1}{m-1} \sum_{\substack{j=1 \\ j \ne i}}^{m} \left\| \mathbf{x}_i - \mathbf{x}_j \right\|_2 \]

展开为:

\[D_i = \frac{1}{m-1} \sum_{\substack{j=1 \\ j \ne i}}^{m} \sqrt{ \sum_{k=1}^{n} (x_{ik} - x_{jk})^2 } \]

其中,\(D_i\) 越大,表示该精英个体与其他精英个体距离越远,离群程度越高。


改变选择最优个体的方式

对于\(E\)中每个蝙蝠个体\(\mathbf{x}_i\):

\[\hat{x_i}=\frac{x_{i} - x_{\text{min}}}{x_{\text{max}} - x_{\text{min}}} \]

\[\hat{D_i}=\frac{D_{i} - D_{\text{min}}}{D_{\text{max}} - D_{\text{min}}} \]

\(\hat{x}\)为归一化后的 \(Ackley\) 值,\(\hat{D_i}\) 为归一化后的离群度

最好的精英个体即为 \(Ackley\) 值与归一化后的离散度差值最小的那个:

\[\mathbf{x}^*=\arg\min_{\mathbf{x}_i \in E}\left(\hat{x}_i - \hat{D}_i\right) \]

可见这个新解更加具有多样性,但是在迭代后期离群度可能会影响对于最小值的判断,因此引出接下来的更新精英集合数的方法


更新精英集合数量

每次迭代更新精英集合数\(m\)

\[m_{0} = initratio * popsize \]

\[m_j = \frac{\bar{A}}{A_0}m_{j-1} \]

其中 \(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代左右就陷入了局部最优,然而改进后的算法能够保持持续下降而不陷入局部最优,效果很好

posted on 2026-06-08 23:21  xtr-hub  阅读(19)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3