(源碼)羣體智能優化算法之社會蜘蛛算法(Social Spider Algorithm ,SSA)

在這裏插入圖片描述
獲取更多資訊,趕快關注上面的公衆號吧!

社會蜘蛛算法(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中執行優化的代理,在算法初始階段,先將預定義數量的蜘蛛放置在蜘蛛網上,每隻蜘蛛ss都有一個存儲器,用於存儲以下個體信息:

  • ss在蜘蛛網上的位置。
  • ss當前位置的適應度。
  • 上一次迭代時ss的目標振動。
  • ss上一次改變其目標振動之後的迭代次數。
  • 上一次迭代時ss的移動。
  • 上一次迭代中ss用於導引移動的維度掩碼。

前兩類信息描述了ss的個體情況,而其他所有信息都涉及引導ss到新的位置。

根據觀察發現,蜘蛛對振動有非常準確的感覺。此外,它們可以區分在同一蜘蛛網上傳播的不同振動,並感知它們各自的強度。在SSA中,當蜘蛛到達一個與前一個不同的新位置時,它會產生振動。振動的強度與位置的適應度有關。振動會在蛛網上傳播,其他蜘蛛可以感覺到它。這樣,蜘蛛在同一個網絡上與他蜘蛛分享自己的個體信息,就形成了集體的社會知識。

振動

在SSA中振動是一個非常重要的概念,是區分SSA與其他元啓發式算法的主要特徵之一。在SSA中,使用振動的源位置和源強度這兩個屬性定義振動。源位置由優化問題的搜索空間進行定義,而振動強度的取值範圍爲[0,+)[0,+\infty)。當蜘蛛移動到新的位置時,它就就會在新的位置上產生振動,將蜘蛛aa在使勁兒tt地位置定義爲Pa(t)\boldsymbol{P}_{a}(t),或者時間參數爲tt時直接簡化爲Pa\boldsymbol{P}_{a},進一步使用I(Pa,Pb,t)I\left(\boldsymbol{P}_{a}, \boldsymbol{P}_{b}, t\right)來表達當振動源在位置Pa\boldsymbol{P}_{a}時,在時刻tt由位置Pb\boldsymbol{P}_{b}上的蜘蛛感知到的振動強度。那麼I(Ps,Ps,t)I\left(\boldsymbol{P}_{s}, \boldsymbol{P}_{s}, t\right)就表示蜘蛛ss在源位置產生的振動強度。源位置的振動強度與其位置f(Ps)f\left(\boldsymbol{P}_{s}\right)的適應度值有關,定義如下:

I(Ps,Ps,t)=log(1f(Ps)C+1)(1)I\left(\boldsymbol{P}_{s}, \boldsymbol{P}_{s}, t\right)=\log \left(\frac{1}{f\left(\boldsymbol{P}_{s}\right)-C}+1\right)\tag{1}
其中CC爲一足夠小的數,所有可能的適應度值都大於CC值(考慮最小化問題),上式的設計主要考慮了以下幾個問題:

  • 優化問題中所有可能的振動強度都是正的。
  • 具有較好的適應度值的位置,即最小化問題的較小值,比適應度值較差的位置具有更大的振動強度。
  • 當解逼近全局最優時,振動強度不應過度增加,從而避免振動衰減機制失效。

作爲能量的一種形式,振動也會隨距離而衰減,這一物理現象在SSA的設計中有所體現。蜘蛛aa和蜘蛛bb之間的距離爲D(Pa,Pb)D(\boldsymbol{P}_{a},\boldsymbol{P}_{b}),並根據1-範數進行距離計算:
D(Pa,Pb)=PaPb1(2)D\left(\boldsymbol{P}_{a}, \boldsymbol{P}_{b}\right)=\left\|\boldsymbol{P}_{a}-\boldsymbol{P}_{b}\right\|_{1}\tag{2}

所有蜘蛛位置在每個維度上的標準差爲σ\boldsymbol{\sigma},那麼振動隨距離的衰減可以定義爲:

I(Pa,Pb,t)=I(Pa,Pa,t)×exp(D(Pa,Pb)σ×ra)(3)I\left(\boldsymbol{P}_{a}, \boldsymbol{P}_{b}, t\right)=I\left(\boldsymbol{P}_{a}, \boldsymbol{P}_{a}, t\right) \times \exp \left(-\frac{D\left(\boldsymbol{P}_{a}, \boldsymbol{P}_{b}\right)}{\overline{\boldsymbol{\sigma}} \times r_{a}}\right)\tag{3}

參數ra(0,)r_a\in(0,\infty)控制振動強度隨距離的衰減率,rar_a越大,振動衰減越弱。

搜索模式

在SSA中存在三個順序執行的階段:初始化、迭代和結束。

在初始化階段,算法定義了目標函數和求解空間,同時還指定算法參數的值,然後就創建初始蜘蛛種羣。在仿真過程中,種羣大小不變,所以分配固定大小的內存來存儲這些蜘蛛的信息,蜘蛛的位置在搜索空間中隨機生成,設置每隻蜘蛛在種羣中的初始目標振動在當前位置,且振動強度爲0。蜘蛛存儲的其他屬性也都初始化爲0。

在迭代階段,每次迭代中蜘蛛網上的蜘蛛都會移動到新的位置,並評估適應度值,每次迭代又可以分爲以下幾步:適應度評估、振動生成、掩碼改變、隨機行走和約束處理。

  • 適應度評估。算法首先計算所有蜘蛛在不同位置的適應度值,更新全局最優解,每隻蜘蛛在每次迭代中只評估一次。

  • 振動生成。根據式(1)這些蜘蛛在它們的位置上產生振動,然後使用式(3)模擬振動的傳播過程,在該過程中,每隻蜘蛛ss都將接收pop|pop|個來自其他蜘蛛產生的不同振動,其中pop|pop|爲蜘蛛種羣大小。這些被接收的振動信息包括振動源位置和衰減強度。使用VV表達這pop|pop|個振動,在接收到VV後,ss會選擇VV中最強的振動vsbestv_s^{best},然後比較該強度與內存中存儲的目標振動vstarv_s^{tar}的強度,如果vsbestv_s^{best}更大,則ssvsbestv_s^{best}存儲爲vstarv_s^{tar},同時設置自上次改變目標振動後的迭代次數csc_s重置爲0,否則vstarv_s^{tar}保持不變,csc_s加1。使用Psi\boldsymbol{P}_s^iPsstar\boldsymbol{P}_s^{star}分別表示VVvtarv_{tar}的源位置,其中i={1,2,,pop}i=\{1,2, \cdots,|p o p|\}

  • 掩碼改變。接下來對ssvstarv_s^{tar}執行隨機行走,這裏利用了維度掩碼引導移動。每隻蜘蛛都有一個長度爲DD的0-1二進制向量m\boldsymbol{m},其中DD爲優化問題的維度。初始時,掩碼中所有的值爲0,但是在每次迭代中蜘蛛ss可以以1pccs1-p_c^{c_s}的概率改變掩碼,其中pc(0,1)p_c\in(0,1)爲用戶自定義的用於目標掩碼改變的概率,如果決定更改掩碼,那麼向量中的每一位都有pmp_m的概率被賦值爲1,pm(0,1)p_m\in(0,1)也是由用戶自定義的控制參數.掩碼的每個位都是獨立改變的,與前一個掩碼沒有任何關聯,如果所有的位都是0,掩碼的一個隨機值就會變成1。類似地,如果所有值都爲1,則將一個隨機位賦值爲0。維度掩碼確定後,基於該掩碼生成新的位置Psfo\boldsymbol{P}_s^{fo},其第ii個維度值計算如下:
    Ps,ifo={Ps,itarms,i=0Ps,irms,i=1(4)P_{s, i}^{f o}=\left\{\begin{array}{ll} P_{s, i}^{t a r} & m_{s, i}=0 \\ P_{s, i}^{r} & m_{s, i}=1 \end{array}\right.\tag{4}
    其中rr[1,pop][1,|pop|]內的隨機整數,ms,im_{s, i}代表蜘蛛ss維度掩碼m\boldsymbol{m}的第ii個緯度值。ms,i=1m_{s,i}=1的兩個不同維度的隨機數rr是獨立生成的。

  • 隨機行走。根據生成的Psfo\boldsymbol{P}_s^{fo}ss向該位置執行隨機行走:

Ps(t+1)=Ps+(PsPs(t1))×r+(PsfoPs)R(5)\boldsymbol{P}_{s}(t+1)=\boldsymbol{P}_{s}+\left(\boldsymbol{P}_{s}-\boldsymbol{P}_{s}(t-1)\right) \times r+\left(\boldsymbol{P}_{s}^{f o}-\boldsymbol{P}_{s}\right) \odot \boldsymbol{R}\tag{5}

其中\odot表示元素相乘,R\boldsymbol{R}爲[0,1]之間均勻分佈的隨機浮點型向量。在遵循Psfo\boldsymbol{P}_{s}^{f o}之前,ss首先沿着上一次迭代的方向進行移動,而移動的距離是上一次移動的隨機一部分,然後纔是ss根據(0,1)的隨機因子在每個維度上向Psfo\boldsymbol{P}_s^{fo}逼近,不同維度上的隨機因子也是獨立生成的。隨機行走之後,ss將該移動存儲在當前迭代中以備下次迭代使用。

  • 約束處理。蜘蛛在行走過程中可能會移出蜘蛛網,從而導致違反約束,SSA中採用下面的方法保證邊界約束:

Ps,i(t+1)={(xiPs,i)×r if Ps,i(t+1)>xi(Ps,ixi)×r if Ps,i(t+1)<xi(6)P_{s, i}(t+1)=\left\{\begin{array}{ll} \left(\overline{x_{i}}-P_{s, i}\right) \times r & \text { if } P_{s, i}(t+1)>\overline{x_{i}} \\ \left(P_{s, i}-\underline{x_{i}}\right) \times r & \text { if } P_{s, i}(t+1)<\underline{x_{i}} \end{array}\right.\tag{6}

其中xi\overline{x_{i}}xi\underline{x_{i}}分別爲搜索空間第ii維的上界和下界。

結束階段,重複執行上述迭代過程,直到滿足終止條件。終止條件可以是最大迭代次數、最大CPU運行時間、誤差率、最優適應度值不再改進最大迭代次數或者任意其他合適的準則。

通過以上三個階段,就構成了完整的SSA算法:

1:分配SSA參數值。

2:創建蜘蛛種羣poppop並分配內存。

3:爲每隻蜘蛛初始化vstarv_s^{tar}

4:while 不滿足終止條件 do

5: for poppop中的每隻蜘蛛ss do

6:  評估ss的適應度值。

7:  根據式(1)生成ss在當前位置的振動。

8: end for

9: for poppop中的每隻蜘蛛ss do

10:  根據式(3)計算所有蜘蛛生成的振動VV的強度。

11:  從VV中選擇最強的振動vsbestv_s^{best}

12:  if vsbestv_s^{best}大於vsstrv_s^{str} then

13:   將vsbestv_s^{best}存儲爲vsstrv_s^{str}

14:  end if

15:  更新csc_s

16:  生成[0,1)[0,1)上的隨機數rr

17:  if r>pccsr>p_c^{c_s} then

18:   更新維度掩碼ms\boldsymbol{m}_s

19:  end if

20:  根據式(4)生成Psfo\boldsymbol{P}_s^{fo}

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();
}
	

程序運行結果如下:

image

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章