人工鱼群算法(Artificial Fish School,AFS)是由国内李晓磊博士等人提出的一种新型仿生群智能优化算法,李博士从分析鱼类的活动出发,采用有别于传统的设计方法,以自下而上的设计思想,应用基于行为的智能方法,提出了一种新的鱼群模式。
真实的鱼类个体,其感知周围环境的变化是通过视觉或味觉实现的,此外,观察鱼类的行为,可以发现鱼类会有以下几种行为表现:
(1)觅食行为。一般情况下鱼在水中随机地自由游动,当发现食物时,则会向食物逐渐增多的方向快速游去。
(2)聚群行为:鱼在游动过程中为了保证自身的生存和躲避危害会自然地聚集成群,鱼聚群时所遵守的规则有三条:分隔规则:尽量避免与临近伙伴过于拥挤;对准规则:尽量与临近伙伴的平均方向一致;内聚规则:尽量朝临近伙伴的中心移动。
(3)追尾行为:当鱼群中的一条或几条鱼发现食物时,其临近的伙伴会尾随其快速到达食物点。
(4)随机行为:单独的鱼在水中通常都是随机游动的,这是为了更大范围地寻找食物点或身边的伙伴。
鱼类在觅食或者寻找同伴的过程中,会根据对环境信息的感知,在这些行为中不停地进行转换,以达到寻找食物或同伴的目的。
1.算法描述
通常一片水域里富含营养的地方,就是鱼类数目最多的地方,人工鱼群算法就是根据这一特点,模拟单条鱼的觅食、聚群、追尾、随机行为,来实现寻找全局最优值的目的。
在解决函数优化问题时,人工鱼个体可用向量X(x1.x2…)表示,称为一个解(即决策变量),该解向量对应的目标函数值为Y=f(X),在算法中表示为人工鱼当前状态的食物浓度。
在描述人工鱼模拟鱼类的具体行为之前,首先定义几个相关参数:
N:水域中人工鱼的数目
visual:人工鱼的视力范围,即人工鱼的感知距离
δ:拥挤度因子,用来调节鱼群的拥挤度
step:人工鱼的最大运动步长
trynumber:人工鱼在移动前的最大试探次数
以寻找目标函数最大值为例,人工鱼群算法中人工鱼寻优的几种行为描述如下:
(1)觅食行为:在算法寻优过程中,若人工鱼当前状态为X,可在其感知距离visual内随机的选择一个状态X1,比较两状态对应的目标函数值,如果满足f(X)<f(X1),则向该方向前进一步,反之,在感知距离内重新选择一个状态X2,若尝试trynumber次后,仍达不到前进条件,则随机移动一步。人工鱼Xi向Xj状态前进一步所处的Xnext可按下式计算:
(2)随机行为:人工鱼个体在水域中自主的随机游动的过程,此过程也是人工鱼自主探索新的食物丰富区域的途径,在算法中有利于摆脱局部最优,帮助算法找到全局最优解。
(3)聚群行为:人工鱼个体要向人工鱼数量较多的区域聚集,设计人工鱼的聚群行为要遵循两个原则:一是要使人工鱼个体尽量向周围伙伴的中心方向移动;二是要尽量避免鱼群过于拥挤。设人工鱼当前状态为X,探索当前感知距离内的伙伴数目为n,中心位置为Xc,如果n/N<δ,表示伙伴中心Xc处有比较多的食物且伙伴之间不太拥挤,如果人工鱼的伙伴中心状态比其当前状态目标函数值更优,则该人工鱼可以向伙伴的中心位置Xc前进一步,否则执行觅食行为。
(4)追尾行为:当人工鱼发现其周围伙伴处食物丰富时,会尾随处在最优状态的伙伴,向其所在方向靠近。若人工鱼当前状态为X,让其探索其感知距离内的状态最优的伙伴Xbest,如果满足f(x)<f(Xbest),并且Xbest的感知距离内的伙伴数目n/N<δ,表示最优伙伴Xbest处有较多的食物且伙伴之间不太拥挤,则该人工鱼向最优伙伴的方向前进一步,否则执行觅食行为。
2.算法步骤
step1.设置迭代次数计数器iter,最大迭代次数Maxiter;设定人工鱼个体数目N,表示人工鱼状态的个体维数d;定义人工鱼算法相关参数,即人工鱼的感知距离visual、拥挤度因子δ、人工鱼每次移动前的最大试探次数trynumber,最大运动步长step。
step2.鱼群初始化,在定义域内随机生成N个解。
step3.对每条鱼i都分别执行:(1)追尾行为(包含觅食行为);(2)聚群行为(包含觅食行为),比较人工鱼原状态与探测到的新状态目标函数值,若新状态更优,则更新人工鱼状态为新状态,否则取原状态。
step4.记录当前所有与的最佳状态及寻找到的最优值。
step5.更新迭代器次数iter = iter + 1,若满足当前迭代次数iter>Maxiter,则搜索停止,输出全局最优结果,否则执行step3.
3.示例
例: max Z = X1 ** 2 + X2 ** 2 - 12 * X1 + 16 * X2 (-5 <= X1 <= 5, -5 <= X2 <= 5)
#人工鱼群算法
import random as rd
from math import sqrt
'''ex:max Z = X1 ** 2 + X2 ** 2 - 12 * X1 + 16 * X2
subject to : -5 <= X1 <= 5
-5 <= X2 <= 5 '''
def objFunction(x1,x2): #目标函数
return x1 ** 2 + x2 ** 2 - 12 * x1 + 16 * x2
def subject(fish):
if fish[0] < -5 or fish[0] > 5:
if abs(fish[0] + 5) < abs(fish[0] - 5):
fish[0] = -5
else:
fish[0] = 5
if fish[1] < -5 or fish[1] > 5:
if abs(fish[1] + 5) < abs(fish[1] - 5):
fish[1] = -5
else:
fish[1] = 5
def move(fish,objFish): #fish向objFish移动一步
if objFish[0] == fish[0]:
fish[1] += (objFish[1] - fish[1]) / abs(objFish[1] - fish[1]) * step * rd.random()
elif objFish[1] == fish[1]:
fish[0] += (objFish[0] - fish[0]) / abs(objFish[0] - fish[0]) * step * rd.random()
else:
tan = abs(objFish[0] - fish[0]) / abs(objFish[1] - fish[1])
x = (objFish[0] - fish[0]) / abs(objFish[0] - fish[0]) * step * rd.random()
fish[0] += x
fish[1] += (objFish[1] - fish[1]) / abs(objFish[1] - fish[1]) * x / tan
subject(fish)
def getCrowdFac(fish): #人工鱼感知旁边的伙伴
nearByFish = []
for everyFish in fishCluster:
if sqrt((fish[0] - everyFish[0]) ** 2 + (fish[1] - everyFish[1]) ** 2) < visual:
nearByFish.append(everyFish)
if len(nearByFish) / len(fishCluster) < crowdFac:
return True,nearByFish,len(nearByFish) / len(fishCluster)
else:
return False,nearByFish,len(nearByFish) / len(fishCluster)
def forage(fish): #觅食行为
judge = False
for i in range(tryNumber):
x1new = rd.uniform(fish[0] - visual, fish[0] + visual)
x2new = rd.uniform(fish[1] - visual, fish[1] + visual)
if objFunction(fish[0],fish[1]) < objFunction(x1new,x2new): #(x1,x2)向(x1new,x2new)移动一步
move(fish,[x1new,x2new])
judge = True
break
if not judge: #随机移动
x3,x4 = 100,100
while sqrt((x3 - fish[0]) ** 2 + (x4 - fish[1]) ** 2) >step:
x3 = fish[0] + rd.uniform(-1, 1) * step
x4 = fish[1] + rd.uniform(-1, 1) * step
fish[0] = x3
fish[1] = x4
subject(fish)
# def rand(fish): #随机行为
# fish[0] += rd.uniform(-1, 1) * visual
# fish[1] += rd.uniform(-1, 1) * visual
def clustering(fish): #聚群行为
judge,nearByFish,nearByCrowdFac = getCrowdFac(fish)
if judge:
if nearByCrowdFac < crowdFac:
x1new,x2new = 0,0
for fishi in nearByFish:
x1new += fishi[0]
x2new += fishi[1]
x1new /= len(nearByFish)
x2new /= len(nearByFish)
if objFunction(fish[0],fish[1]) < objFunction(x1new,x2new):
move(fish,[x1new,x2new])
else:
forage(fish)
else:
pass
def rearEnd(fish): #追尾行为
judge,nearByFish,nearByCrowdFac = getCrowdFac(fish)
xMax = -float("inf")
nearByBestFish = []
for fishi in nearByFish:
if objFunction(fishi[0],fishi[1]) > xMax:
xMax = objFunction(fishi[0],fishi[1])
nearByBestFish = fishi
if objFunction(fish[0],fish[1]) < objFunction(nearByBestFish[0],nearByBestFish[1]):
judgeNew,bestNearByFish,nearByBestFishCrowdFac = getCrowdFac(nearByBestFish)
if nearByBestFishCrowdFac < crowdFac:
move(fish,nearByBestFish)
else:
forage(fish)
def getBest(fishCluster):
bestObj = -float("inf")
bestFsih = []
for everyFish in fishCluster:
if objFunction(everyFish[0],everyFish[1]) > bestObj:
bestObj = objFunction(everyFish[0],everyFish[1])
bestFsih = everyFish
return bestFsih,bestObj
iterx, maxIterx = 0, 10
N = 30
visual = 2
crowdFac = 0.3
tryNumber = 6
step = 0.1
if __name__ == '__main__':
fishCluster = []
for i in range(N):
x1 = rd.uniform(-5, 5)
x2 = rd.uniform(-5, 5)
fishCluster.append([x1,x2])
while iterx < maxIterx:
for fish in fishCluster:
rearEnd(fish)
clustering(fish)
bestFsih,bestObj = getBest(fishCluster)
iterx += 1
print(bestFsih,bestObj)