(源码)群体智能优化算法之社会蜘蛛算法(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中执行优化的代理,在算法初始阶段,先将预定义数量的蜘蛛放置在蜘蛛网上,每只蜘蛛 s s 都有一个存储器,用于存储以下个体信息:

  • s s 在蜘蛛网上的位置。
  • s s 当前位置的适应度。
  • 上一次迭代时 s s 的目标振动。
  • s s 上一次改变其目标振动之后的迭代次数。
  • 上一次迭代时 s s 的移动。
  • 上一次迭代中 s s 用于导引移动的维度掩码。

前两类信息描述了 s s 的个体情况,而其他所有信息都涉及引导 s s 到新的位置。

根据观察发现,蜘蛛对振动有非常准确的感觉。此外,它们可以区分在同一蜘蛛网上传播的不同振动,并感知它们各自的强度。在SSA中,当蜘蛛到达一个与前一个不同的新位置时,它会产生振动。振动的强度与位置的适应度有关。振动会在蛛网上传播,其他蜘蛛可以感觉到它。这样,蜘蛛在同一个网络上与他蜘蛛分享自己的个体信息,就形成了集体的社会知识。

振动

在SSA中振动是一个非常重要的概念,是区分SSA与其他元启发式算法的主要特征之一。在SSA中,使用振动的源位置和源强度这两个属性定义振动。源位置由优化问题的搜索空间进行定义,而振动强度的取值范围为 [ 0 , + ) [0,+\infty) 。当蜘蛛移动到新的位置时,它就就会在新的位置上产生振动,将蜘蛛 a a 在使劲儿 t t 地位置定义为 P a ( t ) \boldsymbol{P}_{a}(t) ,或者时间参数为 t t 时直接简化为 P a \boldsymbol{P}_{a} ,进一步使用 I ( P a , P b , t ) I\left(\boldsymbol{P}_{a}, \boldsymbol{P}_{b}, t\right) 来表达当振动源在位置 P a \boldsymbol{P}_{a} 时,在时刻 t t 由位置 P b \boldsymbol{P}_{b} 上的蜘蛛感知到的振动强度。那么 I ( P s , P s , t ) I\left(\boldsymbol{P}_{s}, \boldsymbol{P}_{s}, t\right) 就表示蜘蛛 s s 在源位置产生的振动强度。源位置的振动强度与其位置 f ( P s ) f\left(\boldsymbol{P}_{s}\right) 的适应度值有关,定义如下:

I ( P s , P s , t ) = log ( 1 f ( P s ) 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}
其中 C C 为一足够小的数,所有可能的适应度值都大于 C C 值(考虑最小化问题),上式的设计主要考虑了以下几个问题:

  • 优化问题中所有可能的振动强度都是正的。
  • 具有较好的适应度值的位置,即最小化问题的较小值,比适应度值较差的位置具有更大的振动强度。
  • 当解逼近全局最优时,振动强度不应过度增加,从而避免振动衰减机制失效。

作为能量的一种形式,振动也会随距离而衰减,这一物理现象在SSA的设计中有所体现。蜘蛛 a a 和蜘蛛 b b 之间的距离为 D ( P a , P b ) D(\boldsymbol{P}_{a},\boldsymbol{P}_{b}) ,并根据1-范数进行距离计算:
D ( P a , P b ) = P a P b 1 (2) D\left(\boldsymbol{P}_{a}, \boldsymbol{P}_{b}\right)=\left\|\boldsymbol{P}_{a}-\boldsymbol{P}_{b}\right\|_{1}\tag{2}

所有蜘蛛位置在每个维度上的标准差为 σ \boldsymbol{\sigma} ,那么振动随距离的衰减可以定义为:

I ( P a , P b , t ) = I ( P a , P a , t ) × exp ( D ( P a , P b ) σ × r a ) (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}

参数 r a ( 0 , ) r_a\in(0,\infty) 控制振动强度随距离的衰减率, r a r_a 越大,振动衰减越弱。

搜索模式

在SSA中存在三个顺序执行的阶段:初始化、迭代和结束。

在初始化阶段,算法定义了目标函数和求解空间,同时还指定算法参数的值,然后就创建初始蜘蛛种群。在仿真过程中,种群大小不变,所以分配固定大小的内存来存储这些蜘蛛的信息,蜘蛛的位置在搜索空间中随机生成,设置每只蜘蛛在种群中的初始目标振动在当前位置,且振动强度为0。蜘蛛存储的其他属性也都初始化为0。

在迭代阶段,每次迭代中蜘蛛网上的蜘蛛都会移动到新的位置,并评估适应度值,每次迭代又可以分为以下几步:适应度评估、振动生成、掩码改变、随机行走和约束处理。

  • 适应度评估。算法首先计算所有蜘蛛在不同位置的适应度值,更新全局最优解,每只蜘蛛在每次迭代中只评估一次。

  • 振动生成。根据式(1)这些蜘蛛在它们的位置上产生振动,然后使用式(3)模拟振动的传播过程,在该过程中,每只蜘蛛 s s 都将接收 p o p |pop| 个来自其他蜘蛛产生的不同振动,其中 p o p |pop| 为蜘蛛种群大小。这些被接收的振动信息包括振动源位置和衰减强度。使用 V V 表达这 p o p |pop| 个振动,在接收到 V V 后, s s 会选择 V V 中最强的振动 v s b e s t v_s^{best} ,然后比较该强度与内存中存储的目标振动 v s t a r v_s^{tar} 的强度,如果 v s b e s t v_s^{best} 更大,则 s s v s b e s t v_s^{best} 存储为 v s t a r v_s^{tar} ,同时设置自上次改变目标振动后的迭代次数 c s c_s 重置为0,否则 v s t a r v_s^{tar} 保持不变, c s c_s 加1。使用 P s i \boldsymbol{P}_s^i P s s t a r \boldsymbol{P}_s^{star} 分别表示 V V v t a r v_{tar} 的源位置,其中 i = { 1 , 2 , , p o p } i=\{1,2, \cdots,|p o p|\}

  • 掩码改变。接下来对 s s v s t a r v_s^{tar} 执行随机行走,这里利用了维度掩码引导移动。每只蜘蛛都有一个长度为 D D 的0-1二进制向量 m \boldsymbol{m} ,其中 D D 为优化问题的维度。初始时,掩码中所有的值为0,但是在每次迭代中蜘蛛 s s 可以以 1 p c c s 1-p_c^{c_s} 的概率改变掩码,其中 p c ( 0 , 1 ) p_c\in(0,1) 为用户自定义的用于目标掩码改变的概率,如果决定更改掩码,那么向量中的每一位都有 p m p_m 的概率被赋值为1, p m ( 0 , 1 ) p_m\in(0,1) 也是由用户自定义的控制参数.掩码的每个位都是独立改变的,与前一个掩码没有任何关联,如果所有的位都是0,掩码的一个随机值就会变成1。类似地,如果所有值都为1,则将一个随机位赋值为0。维度掩码确定后,基于该掩码生成新的位置 P s f o \boldsymbol{P}_s^{fo} ,其第 i i 个维度值计算如下:
    P s , i f o = { P s , i t a r m s , i = 0 P s , i r m s , 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}
    其中 r r [ 1 , p o p ] [1,|pop|] 内的随机整数, m s , i m_{s, i} 代表蜘蛛 s s 维度掩码 m \boldsymbol{m} 的第 i i 个纬度值。 m s , i = 1 m_{s,i}=1 的两个不同维度的随机数 r r 是独立生成的。

  • 随机行走。根据生成的 P s f o \boldsymbol{P}_s^{fo} s s 向该位置执行随机行走:

P s ( t + 1 ) = P s + ( P s P s ( t 1 ) ) × r + ( P s f o P s ) 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]之间均匀分布的随机浮点型向量。在遵循 P s f o \boldsymbol{P}_{s}^{f o} 之前, s s 首先沿着上一次迭代的方向进行移动,而移动的距离是上一次移动的随机一部分,然后才是 s s 根据(0,1)的随机因子在每个维度上向 P s f o \boldsymbol{P}_s^{fo} 逼近,不同维度上的随机因子也是独立生成的。随机行走之后, s s 将该移动存储在当前迭代中以备下次迭代使用。

  • 约束处理。蜘蛛在行走过程中可能会移出蜘蛛网,从而导致违反约束,SSA中采用下面的方法保证边界约束:

P s , i ( t + 1 ) = { ( x i P s , i ) × r  if  P s , i ( t + 1 ) > x i ( P s , i x i ) × r  if  P s , i ( t + 1 ) < x i (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}

其中 x i \overline{x_{i}} x i \underline{x_{i}} 分别为搜索空间第 i i 维的上界和下界。

结束阶段,重复执行上述迭代过程,直到满足终止条件。终止条件可以是最大迭代次数、最大CPU运行时间、误差率、最优适应度值不再改进最大迭代次数或者任意其他合适的准则。

通过以上三个阶段,就构成了完整的SSA算法:

1:分配SSA参数值。

2:创建蜘蛛种群 p o p pop 并分配内存。

3:为每只蜘蛛初始化 v s t a r v_s^{tar}

4:while 不满足终止条件 do

5: for p o p pop 中的每只蜘蛛 s s do

6:  评估 s s 的适应度值。

7:  根据式(1)生成 s s 在当前位置的振动。

8: end for

9: for p o p pop 中的每只蜘蛛 s s do

10:  根据式(3)计算所有蜘蛛生成的振动 V V 的强度。

11:  从 V V 中选择最强的振动 v s b e s t v_s^{best}

12:  if v s b e s t v_s^{best} 大于 v s s t r v_s^{str} then

13:   将 v s b e s t v_s^{best} 存储为 v s s t r v_s^{str}

14:  end if

15:  更新 c s c_s

16:  生成 [ 0 , 1 ) [0,1) 上的随机数 r r

17:  if r > p c c s r>p_c^{c_s} then

18:   更新维度掩码 m s \boldsymbol{m}_s

19:  end if

20:  根据式(4)生成 P s f o \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

猜你喜欢

转载自blog.csdn.net/hba646333407/article/details/106082087