人工魚群算法超詳細解析附帶JAVA代碼
01 前言
本著學習的心態,還是想把這個算法寫一寫,給大家科普一下的吧。
02 人工魚群算法
2.1 定義
人工魚群算法為山東大學副教授李曉磊2002年從魚找尋食物的現象中表現的種種移動尋覓特點中得到啟發而闡述的仿生學優化方案。在一片水域中,魚往往能自行或尾隨其他魚找到營養物質多的地方,因而魚生存數目最多的地方一般就是本水域中營養物質最多的地方,人工魚群算法就是根據這一特點,通過構造人工魚來模仿魚群的覓食、聚群及追尾行為,從而實現尋優。人工魚擁有以下幾種典型行為:
(1)覓食行為:一般情況下魚在水中隨機地自由遊動,當發現食物時,則會向食物逐漸增多的方向快速遊去。
(2)聚群行為:魚在遊動過程中為了保證自身的生存和躲避危害會自然地聚集成群,魚聚群時所遵守的規則有三條:分隔規則:盡量避免與臨近夥伴過於擁擠;對準規則:盡量與臨近夥伴的平均方向一致;內聚規則:盡量朝臨近夥伴的中心移動。
(3)追尾行為:當魚群中的一條或幾條魚發現食物時,其臨近的夥伴會尾隨其快速到達食物點。
(4)隨機行為:單獨的魚在水中通常都是隨機遊動的,這是為了更大範圍地尋找食物點或身邊的夥伴。
2.2 算法具體過程
人工魚群算法實現的步驟:
初始化設置,包括種群規模N、每條人工魚的初始位置、人工魚的視野Visual、步長step、擁擠度因子δ、重復次數Trynumber;
- 計算初始魚群各個體的適應值,取最優人工魚狀態及其值賦予給公告牌;
- 對每個個體進行評價,對其要執行的行為進行選擇,包括覓食Pray、聚群Swarm、追尾Follow和評價行為bulletin;
- 執行人工魚的行為,更新自己,生成新魚群;
- 評價所有個體。若某個體優於公告牌,則將公告牌更新為該個體;
當公告牌上最優解達到滿意誤差界內或者達到叠代次數上限時算法結束,否則轉步驟3。
2.3 算法流程圖
2.4 算法偽代碼
procedure code : Artificial_Fishswarm_Algorithm ::AF_init(); while the result isnot satisfied do switch(::AF_evaluate()) case value1: ::AF_follow(); case value2: ::AF_swarm(); default: ::AF_prey(); end switch ::AF_move(); get_result(); end while end Artificial_FFishswarm_Algorithm
03 參數解析
人工魚群算法有5個基本參數:群規模N、人工魚的視野Visual、步長Step、擁擠度因子δ、重復次數Trynumber。
- 視野Visual:
動物的觀察力是及其深奧的,它可以很快的洞察到周邊的物體,魚類的視野中分為連續型視野和離散型視野兩類,應用如下方法實現虛擬人工魚的視覺:圖2.1(a)表示具有連續型視野的一條假設的人工魚個體,它能看到的區域 Visual 為以現在位置 Xi為圓心一定距離為半徑的圓形區域,地點 Xj為它在一個時候巡視到的視點種另一地方,如果這個地點的食物量比之前地方的多,就決定去這個地方前進步長的隨機數達到地點 Xnext;人工魚的離散型視野為與節點位置 Xi 相鄰且相通的所有節點,如圖 2.1(b)所示,根據判斷邊的代價來選擇下一步位置 Xnext。
由於視野對算法中個行為都有較大影響,因此,它的變化對收斂性能影響也比較復雜。當視野範圍較小時,人工魚的覓食行為和隨機行為比較突出;視野範圍較大時,人工魚的追尾行為和聚群行為將變得比較突出,相應的算法的復雜度也會有所上升。總的來說:視野越大,越容易使人工魚發現全局最優解並收斂。
步長Step:對於固定步長,隨著步長的增加,收斂的速度得到了一定的加速,但在超過一定的範圍後,有使得收斂速度減緩,步長過大時會出現震蕩現象而大大影響收斂速度。采用隨機步長的方式在一定程度上防止了震蕩現象的發生,並使得該參數的敏感度大大降低了,但最快的收斂速度還是最優固定步長的收斂速度,所以,對於特定的優化問題,我們可以考慮采用合適的固定步長或者變尺度方法來提高收斂速度。
群規模N:人工魚的數目越多,跳出局部最優解的能力越強,同時,收斂的速度也越快。當然,付出的代價就是算法每次叠代的計算量也越大,因此,在使用過程中,滿足穩定收斂的前提下,應當盡可能的減少個體數目。
- 嘗試次數Trynumber:嘗試次數越多,人工魚的覓食行為能力越強,收斂的效率也越高。在局部極值突出的情況下,應該適當的減少以增加人工魚隨機遊動的概率,克服局部最優解。
擁擠度因子δ:在求極大值問題中,δ=1/(αnmax),α∈(0,1]δ=1/(αnmax),α∈(0,1];在求極小值問題中,δ=αnmax,α∈(0,1]δ=αnmax,α∈(0,1]。其中α為極值接近水平, nmax為期望在該鄰域內聚集的最大人工魚數目。擁擠度因子與nf相結合,通過人工魚是否執行追尾和聚群行為對優化結果產生影響。以極大值為例(極小值的情況正好與極大值相反),δ越大,表明允許的擁擠程度越小,人工魚擺脫局部最優解的能力越強;但是收斂速度會有所減緩,這主要因為人工魚在逼近最優解的同時,會因避免過分擁擠而隨機走開或者受其他人工魚的排斥作用,不能精確逼近極值點。可見,雖然δ的引入避免了人工魚過度擁擠而陷入局部最優解,但是另一方面,該參數會使得位於極值點附件的人工魚之間存在相互排斥的影響,而難以想極值點精確逼近。所以,對於某些局部極值不是很嚴重的具體問題,可以忽略擁擠的因素,從而在簡化算法的同時也加快算法的收斂速度和提高結果的精確程度。
小結起來就是:
1.群規模:N越大收斂越快,越容易尋得全局最優解,但是計算量越大;
2.感知範圍:視野越大,越易發現全局最優解;
3.步長:決定收斂速度;
4.擁擠因子:適當選擇可避免局部最優
5.重復次數:越大收斂越快,可調整隨機遊走概率,克服局部最優解。
04 四種基本行為
4.1 覓食行為
這是魚趨向食物的一種活動,一般認為它是通過視覺或味覺來感知水中的食物量或食物濃度來選擇行動的方向。設置人工魚當前狀態,並在其感知範圍內隨機選擇另一個狀態,如果得到的狀態的目標函數大於當前的狀態,則向新選擇得到的狀態靠近一步,反之,重新選取新狀態,判斷是否滿足條件,選擇次數達到一定數量後,如果仍然不滿足條件,則隨機移動一步。
4.2 聚群行為
大量或少量的魚聚集成群,進行集體覓食和躲避敵害,這是它們在進化過程中形成的一種生存方式。人工魚探索當前鄰居內的夥伴數量,並計算夥伴的中心位置,然後把新得到的中心位置的目標函數與當前位置的目標函數相比較,如果中心位置的目標函數優於當前位置的目標函數並且不是很擁擠,則當前位置向中心位置移動一步,否則執行覓食行為。魚聚群時會遵守兩條規則:一是盡量向鄰近夥伴的中心移動,二是避免過分擁擠。
4.3 追尾行為
當某一條魚或幾條魚發現食物時,它們附近的魚會尾隨而來,導致更遠處的魚也會尾隨過來。人工魚探索周圍鄰居魚的最優位置,當最優位置的目標函數值大於當前位置的目標函數值並且不是很擁擠,則當前位置向最優鄰居魚移動一步,否則執行覓食行為。
4.4 隨機行為
它是覓食行為的一個缺省行為,指人工魚在視野內隨機移動。當發現食物時,會向食物逐漸增多的方向快速的移動。?
05 行為選擇
公告牌是記錄最優人工魚個體狀態的地方。每條人工魚在執行完一次叠代後將自身當前狀態與公告牌中記錄的狀態進行比較,如果優於公告牌中的狀態則用自身狀態更新公告牌中的狀態,否則公告牌的狀態不變。當整個算法的叠代結束後,公告牌的值就是最優解。
行為評價是用來反映魚自主行為的一種方式,在解決優化問題時選用兩種方式評價:一種是選擇最優行為執行;另一種是選擇較優方向。對於解決極大值問題,可以使用試探法,即模擬執行群聚、追尾等行為,然後評價行動後的值選擇最優的來執行,缺省的行為為覓食行為。
一般通過試探法,模擬執行上述幾種行為,評價後選擇最大者實行;
06 終止條件
叠代終止條件:通常的方法是判斷連續多次所得值得均方差小魚允許的誤差;或判斷聚集於某個區域的人工魚的數目達到某個比例;或連續多次所得的均值不超過已尋找的極值;或限制最大叠代次數。若滿足終止條件,則輸出公告牌的最優記錄;否則繼續叠代。
07 實現代碼
7.1 主函數
package AFAS_PACK;
import java.io.IOException;
public class mainTest {
/**
* @param args
* @throws IOException
* @author sun
*/
public static void main(String[] args) throws IOException {
//int fishNum, int tryTime, int dim, double step, double delta, double visual
System.out.println("begin");
AFAS run = new AFAS(10,5,2,5,0.2,10);
run.doAFAS(40 );//括號內為叠代次數
}
}
7.2 人工魚
package AFAS_PACK;
import java.io.IOException;
public class Fish {
public int dim; //每條魚的維度
public int[] x; //每條魚的具體多維坐標
public double fit; //魚的適應值,濃度
public int visaul; //每條魚的視野
public final double[] H = new double[256];
public final double[] W = new double[256];
public Fish(int dim, int visaul) throws IOException {
super();
this.dim = dim;
this.visaul = visaul;
x = new int[dim];
for(int i=0;i<dim;i++)
x[i] = (int) Math.floor(256*Math.random());
fit = 0;
//init();
}
/*getfit = newfunction(this.x[0],this.x[1]);*/
public double distance(Fish f)
{
double a = 0;
for(int i=0;i<dim;i++)
{
if(this.x[i]-f.x[i]==0)
a = 0.00001;
else
a += (this.x[i]-f.x[i])*(this.x[i]-f.x[i]);
}
return Math.sqrt(a);
}
public double newfunction(int[] w) throws IOException {
return -(w[0]*w[0]-160*w[0]+640+w[1]*w[1]-260*w[1]+16900);
}
}
7.3 AFAS算法部分
package AFAS_PACK;
import java.io.IOException;
import java.util.Date;
public class AFAS {
//魚群數目
private int fishNum;
//嘗試次數
private int tryTime;
//維度
private int dim;
//人工魚移動步長
private int step;
//擁擠度因子
private double delta;
//視野範圍
private int visual;
//人工魚群、範圍內最佳魚,遍歷時的下一條魚
Fish[] fish;
Fish bestfish;
Fish[] nextfish;
//遍歷索引
int index;
double[][] vector;
private int[] choosed;
//範圍內魚群數目 fishCount
public int scopelength;
public AFAS(){
}
public AFAS(int fishNum, int tryTime, int dim, int step, double delta, int visual) throws IOException
{
super();
this.fishNum = fishNum;
this.tryTime = tryTime;
this.dim = dim;
this.step = step;
this.delta = delta;
this.visual = visual;
fish = new Fish[fishNum];
nextfish = new Fish[3];
vector = new double[fishNum][dim];
choosed = new int[fishNum];
index = 0;
init();
}
public void doAFAS(int num) throws IOException
{
long startTime = new Date().getTime();
double a = 0.0;
int count = 1; //計算查找次數
int len = 0;
while(count<=num)
{
for(int i=0; i<fishNum; i++)
{
prey(i);
swarm(i);
follow(i);
bulletin(i);
System.out.println("第"+count+"遍第"+i+"條魚結束");
}
System.out.println(count+"當前最優值:"+bestfish.fit);
for(int i=0; i<dim; i++)
{
System.out.print("位置"+(i+1)+": "+bestfish.x[i]);
}
System.out.println();
count++;
System.out.println("step:"+step+" visaul:"+visual);
}
System.out.println("最優值:"+bestfish.fit);
for(int i=0; i<dim; i++)
{
System.out.print("位置"+(i+1)+": "+bestfish.x[i]);
}
long endTime = new Date().getTime();
System.out.println("本程序運行計時: "+(endTime-startTime)+" 毫秒。");
}
private void bulletin(int i) throws IOException {
Fish maxfish = new Fish(dim,visual);
maxfish = nextfish[0];
for(int j=0;j<3;j++)
{
if(nextfish[j].fit>maxfish.fit && nextfish[j].x[0]!=0 && nextfish[j].x[1]!=0)
{
maxfish = nextfish[j];
}
}
if(maxfish.fit<fish[i].fit)
{
return ;
}
fish[i] = maxfish;
if(maxfish.fit>bestfish.fit)
bestfish = maxfish;
}
private void follow(int i) throws IOException {
nextfish[2] = new Fish(dim,visual);
Fish minfish = new Fish(dim,visual); // 中心位置
minfish = fish[i];
Fish[] scope = getScopefish(i);
int key = i;
if(scope!=null)
{
for(int j=0;j<scope.length;j++)
{
if(scope[j].fit<minfish.fit)
{
minfish = scope[j];
key = j;
}
}
if(minfish.fit>=fish[i].fit)
prey(i);
else{
Fish[] newScope = getScopefish(key);
if(newScope!=null)
{
if(newScope.length*minfish.fit<delta*fish[i].fit)
{
double dis = fish[i].distance(minfish);
for(int k=0;k<dim;k++)
{
nextfish[2].x[k] = (int) (fish[i].x[k]+(minfish.x[k]-fish[i].x[k])*step*Math.random()/dis);
}
nextfish[2].fit = nextfish[2].newfunction(nextfish[2].x);
}
else prey(i);
}
else prey(i);
}
}
else prey(i);
}
private void swarm(int i) throws IOException { //swam start
nextfish[1] = new Fish(dim,visual);
int[] center = new int[dim]; // 中心位置
for(int j=0;j<dim;j++)
center[j] = 0;
Fish[] scope = getScopefish(i);
if(scope!=null)
{
for(int j=0;j<scope.length;j++) // 計算人工魚的中心位置
{
for( i=0; i<dim; ++i )
center[i] += scope[j].x[i];
}
for( i=0; i<dim; i++ )
center[i] /= scope.length; // 人工魚的中心位置
//滿足條件
double dis=0.0;
Fish centerfish = new Fish(dim,visual);
centerfish.x = center;
centerfish.fit = centerfish.newfunction(centerfish.x);
dis = fish[i].distance(centerfish);
if(centerfish.fit>fish[i].fit && scope.length*centerfish.fit<delta*fish[i].fit)
{
for(int j=0;j<dim;j++)
{
nextfish[1].x[j] = (int) (fish[i].x[j]+(centerfish.x[j]-fish[i].x[j])*step*Math.random()/dis);
}
nextfish[1].fit = nextfish[1].newfunction(nextfish[1].x);
}
else prey(i);
}
else prey(i);
} //swam end
private void prey(int i) throws IOException { //prey start
Fish newfish = new Fish(dim,visual);
newfish.fit = 0;
nextfish[0] = new Fish(dim,visual);
for(int k=0; k<tryTime; k++ ) // 進行try_number次嘗試
{
for(int j=0; j<dim; j++ )
{
newfish.x[j] = (int) ((2*(Math.random())-1)*visual);
}
newfish.fit = newfish.newfunction(newfish.x);
if( newfish.fit > fish[i].fit )
{
double dis = fish[i].distance(newfish);
for(int j=0; j<dim; j++ )
{
nextfish[0].x[j] = (int) (fish[i].x[j]+(newfish.x[j]-fish[i].x[j])*step*Math.random()/dis);
}
nextfish[0].fit =nextfish[0].newfunction(nextfish[0].x);
}
else
{
for(int j=0; j<dim; j++)
{
nextfish[0].x[j] = (int) (fish[i].x[j]+visual*(2*(Math.random())-1));
nextfish[0].fit = nextfish[0].newfunction(nextfish[0].x);
}
}
}
}
private Fish[] getScopefish(int i) {
int num = 0;
for(int j=0;j<fishNum;j++)
{
choosed[j] = -1;
if(fish[i].distance(fish[j])<visual)
{
choosed[j] = i;
num++;
}
}
if(num!=0)
{
Fish[] scope = new Fish[num];
int k = 0;
for(int j=0;j<fishNum;j++)
{
if(choosed[j]!=-1)
scope[k++] = fish[choosed[j]];
}
return scope;
}
return null;
} //prey end
private void init() throws IOException {
for(int i=0;i<fishNum;i++)
{
fish[i] = new Fish(dim,visual);
fish[i].fit = fish[i].newfunction(fish[i].x);
}
bestfish = new Fish(dim,visual);
bestfish.fit = -999999;
}
}
人工魚群算法超詳細解析附帶JAVA代碼