蝙蝠算法(BA)是由 Xin-She Yang 于 2010 年开发的一种高效生物启发式(bio-inspired)算法。
Echolocation of Bats
Behavior of Microbats
蝙蝠是一种神奇的动物。它们是唯一一种有翅膀的哺乳动物,它们也具有先进的回声定位能力。据估计,大约有 1000 种不同的蝙蝠种类,占所有哺乳动物种类的 20%。它们的大小从微小的凹脸蝠(bumblebee bats)(约 1.5 至 2 克)到翼展约 2 米,重量高达约 1 公斤的巨型蝙蝠。小蝙蝠(microbats)通常具有约 2.2 至 11 厘米的前臂长度。大多数蝙蝠在一定程度上都使用回声定位,在所有的这些蝙蝠中,小蝙蝠因广泛使用回声定位而著名,而狐蝠(megabats)则不。
大多数小蝙蝠是食虫动物。小蝙蝠使用一种称为回声定位的声纳来检测猎物,避开障碍物,并在黑暗中找到栖息的裂缝。这些蝙蝠发出非常响亮的声波脉冲,并监听从周围物体反射回来的回声。发出脉冲的特性与狩猎战略相关,依据猎物种类不同而改变。大多数蝙蝠使用短调频信号扫过一个八度音阶;其他的则经常使用恒定频信号进行回声定位。它们的信号带宽随着物种而变化,通常通过使用更多的谐波来增加。
研究表明,小蝙蝠使用了回声发射与检测的时延,两耳之间的时间差和回声响度的变化来建立周围环境的三维场景。它们可以侦测到目标的距离和方向,猎物的类型,甚至猎物的移动速度,如小昆虫。事实上,研究表明,蝙蝠似乎能够通过目标昆虫的翅膀颤动率引起的多普勒效应的变化来区分目标。
Acoustics of Echolocation
尽管每个脉冲的持续时间只有千分之几秒(最长可达 8 到 10 ms),但它的频率通常在 25 kHz 到 150 kHz 之间。尽管有些种类的蝙蝠可以发出高达 150 kHz 的高频声波,但大多数蝙蝠的典型频率范围在 25 kHz 到 100 kHz 之间。每个超声波脉冲可以(典型地)持续 5 到 20ms,小蝙蝠每秒可以发出大约 10 到 20 个这样的声波脉冲。当它们捕猎靠近猎物时,脉冲发射速率可以被加速到每秒约 200 个脉冲。这样的短时声音突发意味着蝙蝠具有奇妙的信号处理能力。事实上,研究表明,蝙蝠耳朵的等效积分时间通常约为 300 到 400 微秒。
由于空气中室温下声速的典型值为
令人惊讶的是,蝙蝠发出的脉冲可能高达 110 分贝(dB),幸运的是,脉冲处于超声波区。响度也随着目的不同而改变,从最响的搜寻猎物到相对安静飞向猎物。这种短脉冲的行程范围通常是几米,其取决于实际的频率。小蝙蝠可以设法避免如人头发丝一样薄的障碍物。
很显然,有些蝙蝠视力好,大多数蝙蝠也有非常敏感的嗅觉。在实际中,他们将所有的感官结合在一起,最大限度地发现猎物,平稳导航。但是,这里我们只关心回声定位以及相关的行为。
小蝙蝠的回声定位行为可以通过与待优化的目标函数相关联的方式来表达,这使得有可能制定新的优化算法。这里我们首先概述 BA 的基本表述,然后讨论它的实现。
Bat Algorithms
如果理想化小蝙蝠回声定位的一些特征,我们就可以开发出各种蝙蝠启发(bat-inspired)或蝙蝠算法了[32]。为了简单起见,我们使用以下近似或理想的规则:
- 所有的蝙蝠都用回声定位来感知距离,并且“知道”食物/猎物与背景障碍之间的差异。
- 蝙蝠在位置
xi 以速度vi 随机飞行,它们可以自动调整发出脉冲的频率(波长),并依据目标的接近程度调整脉冲发射率r∈[0,1] 。 - 虽然响度可以从多个方面改变,但我们假设响度从一个大(正)值
A0 变化到最小值Amin 。
另一个明显的简化是,估计时延和三维地形时不使用射线追踪(ray tracing)。尽管在计算几何应用中这可能是一个很好的特性,但在这里不使用这种简化,因为在多维时增加了计算量。
除了这些简化以外,我们还使用了以下近似值。通常,频率
对于给定的问题,我们可以使用任意波长来实现。在实际应用中,我们可以通过调整频率(波长)来调整范围。选择的范围(或最大波长)应与感兴趣区域的大小相当,然后再逐渐调整至较小的范围。而且,我们不一定必须调节波长本身。相反,可以通过改变频率来实现。这是因为
为简单起见,可以假设
基于以上近似和理想化规则,BA 的基本步骤可以总结为以下伪代码:
初始化蝙蝠种群 xi 和 vi (i=1,2,...,n)
初始化频率 fi,脉冲发射率 ri ,和响度 Ai
while (t < Max 迭代数)
通过调整频率产生新解
更新速度与位置/解 [公式(1) 到 (3)]
if (rand > ri)
从最佳解中选择一个
围绕选择的解产生一个局部解
end if
通过随机飞行产生新解
if (rand < Ai & f(xi) < f(x*))
接受新解
增加 ri 减少 Ai
end if
排列蝙蝠,找出当前最佳解 x*
end while
Pseudo code of the bat algorithm
Movement of Virtual Bats
在模拟中,我们使用虚拟蝙蝠。需要定义d-维搜索空间虚拟蝙蝠位置
其中
对于局部搜索部分,一旦从当前最佳解中选择了一个解,则使用随机游走对每个蝙蝠产生一个新解
其中
其中
蝙蝠速度和位置的更新可能与标准粒子群优化(PSO)程序有些相似,因为
Loudness and Pulse Emission
此外,响度
其中
schedule )的冷却因子相似。对于任何
在最简单的情况下,可以用
参数的选择需要试验。开始时,每只蝙蝠应具有不同的响度和脉冲发射率,这可以通过随机化来实现。例如,初始响度
仔细分析 BA,我们可以发现它可以捕捉到许多其他算法的特征。如果我们用随机参数代替频率
Implementation
从伪代码来看,用编程语言实现 BA 都是相对简单的。 为了便于可视化,我们用 Matlab 实现了它。
有许多用于验证新算法的标准测试函数。我们来看看一个简单的基准函数 egg crate 函数
eggcrate 函数(左)和最近 10 次迭代 40 只蝙蝠的位置
为了演示,我们将
% -------------------------------------------------------- %
% Bat-inspired algorithm for continuous optimization (demo)%
% Programmed by Xin-She Yang @Cambridge University 2010 %
% -------------------------------------------------------- %
function [best,fmin,N_iter]=bat_algorithm(para)
% Default parameters
if nargin<1, para=[10 0.25 0.5]; end
n=para(1); % Population size, typically 10 to 25
A=para(2); % Loudness (constant or decreasing)
r=para(3); % Pulse rate (constant or decreasing)
% This frequency range determines the scalings
Qmin=0; % Frequency minimum
Qmax=2; % Frequency maximum
% Iteration parameters
tol=10^(-5); % Stop tolerance
N_iter=0; % Total number of function evaluations
% Dimension of the search variables
d=3;
% Initial arrays
Q=zeros(n,1); % Frequency
v=zeros(n,d); % Velocities
% Initialize the population/solutions
for i=1:n
Sol(i,:)=randn(1,d);
Fitness(i)=Fun(Sol(i,:));
end
% Find the current best
[fmin,I]=min(Fitness);
best=Sol(I,:);
% Start the iterations -- Bat Algorithm
while (fmin>tol)
% Loop over all bats/solutions
for i=1:n
Q(i)=Qmin+(Qmin-Qmax)*rand;
v(i,:)=v(i,:)+(Sol(i,:)-best)*Q(i);
S(i,:)=Sol(i,:)+v(i,:);
% Pulse rate
if rand>r
S(i,:)=best+0.01*randn(1,d);
end
% Evaluate new solutions
Fnew=Fun(S(i,:));
% If the solution improves or not too loudness
if (Fnew<=Fitness(i)) & (rand<A)
Sol(i,:)=S(i,:);
Fitness(i)=Fnew;
end
% Update the current best
if Fnew<=fmin
best=S(i,:);
fmin=Fnew;
end
end
N_iter=N_iter+n;
end
% Output/display
disp(['Number of evaluations: ',num2str(N_iter)]);
disp(['Best = ',num2str(best),' fmin = ',num2str(fmin)]);
% Objective function -- Rosenbrock's 3D function
function z=Fun(u)
z=(1-u(1))^2+100*(u(2)-u(1)^2)^2+(1-u(3))^2;
BA 在许多测试函数上的精度和效率远优于其他算法,因为通常它提供了一个更快的收敛速度。
python version
#!/usr/bin/env python3
"""An implementation of Bat Algorithm
"""
import numpy as np
from numpy.random import random as rand
__author__ = "GreatX"
__copyright__ = "Copyright 2017"
__credits___ = ["GreatX"]
__license__ = "MIT"
__email__ = "[email protected]"
# Parameters setting
# objfun: objective function
# N_pop: population size, typically 10 to 40
# N_gen: number of generation
# A: loudness (constant or decreasing)
# r: pulse rate (constant or decreasing)
# This frequency range determines the scalings
# You should change these values if necessary
# Qmin: frequency minmum
# Qmax: frequency maxmum
# d: number of dimensions
# lower: lower bound
# upper: upper bound
def bat_algorithm(objfun, N_pop=20, N_gen=1000, A=0.5, r=0.5,
Qmin=0, Qmax=2, d=10, lower=-2, upper=2):
N_iter = 0 # Total number of function evaluations
# Limit bounds
Lower_bound = lower * np.ones((1,d))
Upper_bound = upper * np.ones((1,d))
Q = np.zeros((N_pop, 1)) # Frequency
v = np.zeros((N_pop, d)) # Velocities
S = np.zeros((N_pop, d))
# Initialize the population/soutions
# Sol = np.random.uniform(Lower_bound, Upper_bound, (N_pop, d))
# Fitness = objfun(Sol)
Sol = np.zeros((N_pop, d))
Fitness = np.zeros((N_pop, 1))
for i in range(N_pop):
Sol[i] = np.random.uniform(Lower_bound, Upper_bound, (1, d))
Fitness[i] = objfun(Sol[i])
# Find the initial best solution
fmin = min(Fitness)
Index = list(Fitness).index(fmin)
best = Sol[Index]
# Start the iterations
for t in range(N_gen):
# Loop over all bats/solutions
for i in range(N_pop):
# Q[i] = Qmin + (Qmin - Qmax) * np.random.rand
Q[i] = np.random.uniform(Qmin, Qmax)
v[i] = v[i] + (Sol[i] - best) * Q[i]
S[i] = Sol[i] + v[i]
# Apply simple bounds/limits
Sol[i] = simplebounds(Sol[i], Lower_bound, Upper_bound)
# Pulse rate
if rand() > r:
# The factor 0.001 limits the step sizes of random walks
S[i] = best + 0.001*np.random.randn(1, d)
# Evaluate new solutions
# print(i)
Fnew = objfun(S[i])
# Update if the solution improves, or not too loud
if (Fnew <= Fitness[i]) and (rand() < A):
Sol[i] = S[i]
Fitness[i] = Fnew
# update the current best solution
if Fnew <= fmin:
best = S[i]
fmin = Fnew
N_iter = N_iter + N_pop
print('Number of evaluations: ', N_iter)
print("Best = ", best, '\n fmin = ', fmin)
return best
def simplebounds(s, Lower_bound, Upper_bound):
Index = s > Lower_bound
s = Index * s + ~Index * Lower_bound
Index = s < Upper_bound
s = Index * s + ~Index * Upper_bound
return s
# u: array-like
def test_function(u):
a = u ** 2
return a.sum(axis=0)
if __name__ == '__main__':
# print(bat_algorithm(test_function))
bat_algorithm(test_function)
# l = [1, 2, 3, 4, 5]
# u = np.array(l)
# print(test_function(u))
# lb = np.array([1, 1, 1, 1])
# ub = np.array([2, 2, 2, 2])
# s = np.array([0.5, 3, 1.5, 1.5])
# print(simplebounds(s, lb, ub))