獲取更多資訊,趕快關注上面的公衆號吧!
社會蜘蛛算法(Social Spider Algorithm ,SSA)
受社會蜘蛛的啓發,香港大學的James J.Q. Yua和Victor O.K. Lia於2015年提出了一種新的全局優化算法——社會蜘蛛算法(Social Spider Algorithm ,SSA),該算法主要基於社會蜘蛛的覓食策略,利用蜘蛛網的振動來確定獵物位置。本文首先了解社會蜘蛛的覓食行爲,然後給出詳細的算法流程,最後再Matlbab、Python、C++和Java實現,完整代碼請關注公衆號並後臺回覆"社會蜘蛛"獲取。
靈感
雖然我們平時生活中,經常見到的蜘蛛好像都是獨居的,但實際上還有一些是社會的,即羣居的,它們互相之間會以某種方式進行信息交互。它們會利用各種不同的捕食策略,但大多數都是通過感知振動來偵測獵物,蜘蛛對振動刺激非常敏感,如果振動在規定的頻率範圍內,蜘蛛就會像振動源發起攻擊。同時蜘蛛還能區分出振動是由獵物還是其他蜘蛛發出的。
社會蜘蛛的覓食行爲可以描述爲蜘蛛向食物源位置進發的協同移動,蜘蛛接收並分析蜘蛛網上傳播的振動,以確定食物來源的潛在方向,就是基於這種思想才提出了社會蜘蛛算法。
Social Spider Algorithm
在SSA中,將優化問題的搜索空間形式化爲超空間蜘蛛網,蜘蛛網上的每個位置表示優化問題的一個可行解,所有可行解在蜘蛛網上都有對應的位置,蜘蛛網還是振動的傳播媒介。每隻蜘蛛在蜘蛛網上都有自己的位置,解的質量或適應度是基於目標函數的,並表示爲在當前位置找到食物源的可能性。蜘蛛可以在蜘蛛網上自由移動,但是不能離開蜘蛛網,因爲蜘蛛網以外的區域爲優化問題的不可行解。當蜘蛛移動到一個新的位置,它就會產生振動並在蜘蛛網上傳播,每一個振動都持有一隻蜘蛛的信息,而其他蜘蛛在接收到振動後就可以獲得該信息。
蜘蛛
蜘蛛就是SSA中執行優化的代理,在算法初始階段,先將預定義數量的蜘蛛放置在蜘蛛網上,每隻蜘蛛都有一個存儲器,用於存儲以下個體信息:
- 在蜘蛛網上的位置。
- 當前位置的適應度。
- 上一次迭代時的目標振動。
- 自上一次改變其目標振動之後的迭代次數。
- 上一次迭代時的移動。
- 上一次迭代中用於導引移動的維度掩碼。
前兩類信息描述了的個體情況,而其他所有信息都涉及引導到新的位置。
根據觀察發現,蜘蛛對振動有非常準確的感覺。此外,它們可以區分在同一蜘蛛網上傳播的不同振動,並感知它們各自的強度。在SSA中,當蜘蛛到達一個與前一個不同的新位置時,它會產生振動。振動的強度與位置的適應度有關。振動會在蛛網上傳播,其他蜘蛛可以感覺到它。這樣,蜘蛛在同一個網絡上與他蜘蛛分享自己的個體信息,就形成了集體的社會知識。
振動
在SSA中振動是一個非常重要的概念,是區分SSA與其他元啓發式算法的主要特徵之一。在SSA中,使用振動的源位置和源強度這兩個屬性定義振動。源位置由優化問題的搜索空間進行定義,而振動強度的取值範圍爲。當蜘蛛移動到新的位置時,它就就會在新的位置上產生振動,將蜘蛛在使勁兒地位置定義爲,或者時間參數爲時直接簡化爲,進一步使用來表達當振動源在位置時,在時刻由位置上的蜘蛛感知到的振動強度。那麼就表示蜘蛛在源位置產生的振動強度。源位置的振動強度與其位置的適應度值有關,定義如下:
其中爲一足夠小的數,所有可能的適應度值都大於值(考慮最小化問題),上式的設計主要考慮了以下幾個問題:
- 優化問題中所有可能的振動強度都是正的。
- 具有較好的適應度值的位置,即最小化問題的較小值,比適應度值較差的位置具有更大的振動強度。
- 當解逼近全局最優時,振動強度不應過度增加,從而避免振動衰減機制失效。
作爲能量的一種形式,振動也會隨距離而衰減,這一物理現象在SSA的設計中有所體現。蜘蛛和蜘蛛之間的距離爲,並根據1-範數進行距離計算:
所有蜘蛛位置在每個維度上的標準差爲,那麼振動隨距離的衰減可以定義爲:
參數控制振動強度隨距離的衰減率,越大,振動衰減越弱。
搜索模式
在SSA中存在三個順序執行的階段:初始化、迭代和結束。
在初始化階段,算法定義了目標函數和求解空間,同時還指定算法參數的值,然後就創建初始蜘蛛種羣。在仿真過程中,種羣大小不變,所以分配固定大小的內存來存儲這些蜘蛛的信息,蜘蛛的位置在搜索空間中隨機生成,設置每隻蜘蛛在種羣中的初始目標振動在當前位置,且振動強度爲0。蜘蛛存儲的其他屬性也都初始化爲0。
在迭代階段,每次迭代中蜘蛛網上的蜘蛛都會移動到新的位置,並評估適應度值,每次迭代又可以分爲以下幾步:適應度評估、振動生成、掩碼改變、隨機行走和約束處理。
-
適應度評估。算法首先計算所有蜘蛛在不同位置的適應度值,更新全局最優解,每隻蜘蛛在每次迭代中只評估一次。
-
振動生成。根據式(1)這些蜘蛛在它們的位置上產生振動,然後使用式(3)模擬振動的傳播過程,在該過程中,每隻蜘蛛都將接收個來自其他蜘蛛產生的不同振動,其中爲蜘蛛種羣大小。這些被接收的振動信息包括振動源位置和衰減強度。使用表達這個振動,在接收到後,會選擇中最強的振動,然後比較該強度與內存中存儲的目標振動的強度,如果更大,則將存儲爲,同時設置自上次改變目標振動後的迭代次數重置爲0,否則保持不變,加1。使用和分別表示和的源位置,其中。
-
掩碼改變。接下來對向執行隨機行走,這裏利用了維度掩碼引導移動。每隻蜘蛛都有一個長度爲的0-1二進制向量,其中爲優化問題的維度。初始時,掩碼中所有的值爲0,但是在每次迭代中蜘蛛可以以的概率改變掩碼,其中爲用戶自定義的用於目標掩碼改變的概率,如果決定更改掩碼,那麼向量中的每一位都有的概率被賦值爲1,也是由用戶自定義的控制參數.掩碼的每個位都是獨立改變的,與前一個掩碼沒有任何關聯,如果所有的位都是0,掩碼的一個隨機值就會變成1。類似地,如果所有值都爲1,則將一個隨機位賦值爲0。維度掩碼確定後,基於該掩碼生成新的位置,其第個維度值計算如下:
其中爲內的隨機整數,代表蜘蛛維度掩碼的第個緯度值。對的兩個不同維度的隨機數是獨立生成的。 -
隨機行走。根據生成的,向該位置執行隨機行走:
其中表示元素相乘,爲[0,1]之間均勻分佈的隨機浮點型向量。在遵循之前,首先沿着上一次迭代的方向進行移動,而移動的距離是上一次移動的隨機一部分,然後纔是根據(0,1)的隨機因子在每個維度上向逼近,不同維度上的隨機因子也是獨立生成的。隨機行走之後,將該移動存儲在當前迭代中以備下次迭代使用。
- 約束處理。蜘蛛在行走過程中可能會移出蜘蛛網,從而導致違反約束,SSA中採用下面的方法保證邊界約束:
其中和分別爲搜索空間第維的上界和下界。
結束階段,重複執行上述迭代過程,直到滿足終止條件。終止條件可以是最大迭代次數、最大CPU運行時間、誤差率、最優適應度值不再改進最大迭代次數或者任意其他合適的準則。
通過以上三個階段,就構成了完整的SSA算法:
1:分配SSA參數值。
2:創建蜘蛛種羣並分配內存。
3:爲每隻蜘蛛初始化。
4:while 不滿足終止條件 do
5: for 中的每隻蜘蛛 do
6: 評估的適應度值。
7: 根據式(1)生成在當前位置的振動。
8: end for
9: for 中的每隻蜘蛛 do
10: 根據式(3)計算所有蜘蛛生成的振動的強度。
11: 從中選擇最強的振動。
12: if 大於 then
13: 將存儲爲。
14: end if
15: 更新。
16: 生成上的隨機數。
17: if then
18: 更新維度掩碼。
19: end if
20: 根據式(4)生成
21: 執行隨機行走(式(5))。
22: 處理違反的約束,
23: end for
24:end while
代碼實現
這部分提供了SSA的MATLAB、C++、Python和Java代碼,完整代碼請關注公衆號並後臺回覆"社會蜘蛛"獲取,其中部分Java代碼如下。
bestSpider = new Spider(new Position(this.dim, this.objectiveFun.getRange()));
bestSpider.setFitness(Double.MAX_VALUE);
initPop();
while (this.iterator < maxGen) {
// 計算適應度值和振動
calcFitnessAndVibration();
// 各個維度標準差的平均值
double[] averageArray = IntStream.range(0, this.dim).mapToDouble(dim -> {
return spiders.stream().mapToDouble(spider -> spider.getPosition().getPositionCode()[dim]).average()
.getAsDouble();
}).toArray();
double meanDeviation = IntStream.range(0, this.dim).mapToDouble(dim -> {
return Math.pow(spiders.stream()
.mapToDouble(
spider -> Math.pow(spider.getPosition().getPositionCode()[dim] - averageArray[dim], 2))
.sum() / this.spiderAmount, 0.5);
}).average().getAsDouble();
for (Spider spidera : spiders) {
// 計算傳播後的振動V
List<Double> V = new ArrayList<Double>();
for (Spider spiderb : spiders) {
double distance = 0;
if (spidera != spiderb) {
distance = MathUtil.calcManhattanDistance(spidera.getPosition().getPositionCode(),
spiderb.getPosition().getPositionCode());
}
double newIntensity = spiderb.getVibration() * Math.exp(-distance / (meanDeviation * ra));
V.add(newIntensity);
}
// 從V中選擇最大的振動
double vbest = V.stream().mapToDouble(v -> v).max().getAsDouble();
int bestInd = V.indexOf(vbest);
// 重新設置目標振動
if (vbest > spidera.getTargetVibration()) {
spidera.setTargetVibration(vbest);
spidera.setTargetPosition(spiders.get(bestInd).getPosition().getPositionCode());
// 重置Cs
spidera.setCs(0);
} else {
// 更新Cs
spidera.setCs(spidera.getCs() + 1);
}
double r = random.nextDouble();
// 很多代cs沒有更新時,就有更大可能更新掩碼
if (r > Math.pow(this.pc, spidera.getCs())) {
// 如果更改掩碼,則以pm的概率變爲1。
// 對於全部爲1或爲0時,隨機選擇某一維度進行更改
// long zeroLen = Arrays.stream(spidera.getMask()).filter(i -> i == 0).count();
// long oneLen = Arrays.stream(spidera.getMask()).filter(i -> i == 1).count();
// // 全爲1
// if (zeroLen == 0) {
// spidera.getMask()[random.nextInt(this.dim)] = 0;
// }
// // 全爲0
// if (oneLen == 0) {
// spidera.getMask()[random.nextInt(this.dim)] = 1;
// }
for (int i = 0; i < this.dim; i++) {
double rr = random.nextDouble();
// 以概率pm設置爲1
if (rr <= this.pm) {
spidera.getMask()[i] = 1;
} else {
spidera.getMask()[i] = 0;
}
}
}
// 計算pfollow
double[] pfollow = new double[this.dim];
for (int maskInd = 0; maskInd < spidera.getMask().length; maskInd++) {
// 從目標位置中選擇
if (spidera.getMask()[maskInd] == 0) {
pfollow[maskInd] = spidera.getTargetPosition()[maskInd];
} else {
// 從隨機選擇的蜘蛛中選擇
pfollow[maskInd] = spiders.get(random.nextInt(this.spiderAmount)).getPosition()
.getPositionCode()[maskInd];
}
}
// 隨機行走,計算下一次迭代的位置
double[] currPos = spidera.getPosition().getPositionCode();
double[] movement = IntStream.range(0, this.dim)
.mapToDouble(dim -> spidera.getMovenment()[dim] * random.nextDouble()
+ (pfollow[dim] - currPos[dim]) * random.nextDouble())
.toArray();
double[] newPos = IntStream.range(0, this.dim).mapToDouble(dim -> currPos[dim] + movement[dim])
.toArray();
spidera.getPosition().setPositionCode(newPos);
spidera.setMovenment(movement);
}
System.out.println("**********第" + iterator + "代最優解:" + bestSpider + "**********");
incrementIter();
}
程序運行結果如下: