狼群算法(WPA)
摘要
狼群算法是基于狼群群体智能,模拟狼群捕食行为及其猎物分配方式,抽象出游走、召唤、围攻3种智能行为以及“胜者为王”的头狼产生规则和“强者生存”的狼群更新机制,提出一种新的群体智能算法。
狼群角色
头狼:狼群以头狼为首,头狼负责指挥整个狼群。头狼在整个狼群中始终都是在整个狼群中离猎物最近的一匹狼。
探狼:狼群在寻找猎物时,不会全体出动而是派出少数精锐的探狼在猎物的可能活动范围内游猎,探狼始终朝着猎物所在位置的方向搜寻。[1]
猛狼:一旦探狼发现猎物踪迹,就会立即向头狼报告,头狼视情通过嚎叫召唤周围的猛狼来对猎物进行围攻。周围的猛狼闻声则会自发地朝着该探狼的方向奔袭,向猎物进一步逼近。[1]
数量定义
狼群数量定义:例狼群中有50只狼
1 int wolf_num = 50;//狼群数量 2 vector<Wolf> wolfs;//狼群数组
头狼数量定义:因为头狼在狼群中是独一无二的存在,所以一个狼群中只有一匹头狼。但通过算法的迭代,每一匹狼都有机会代替头狼成为新的头狼。创建头狼指针,当其他狼有更优解,就把头狼指针指向那匹狼。
1 vector<Wolf>::iterator head_wolf_iter;//头狼指针
探狼数量定义:探狼的数量需要根据探狼比例因子和狼群数量来确定,具体代码如下
1 double alpha = 4;//探狼比例因子 2 int exploring_wolf_num;//探狼数量,(取[n/(α+1),n/α]之间的整数) 3 //探狼数量,(取[n/(α+1),n/α]之间的整数) 4 int MIN_VALUE = wolf_num / (alpha + 1); 5 int MAX_VALUE = wolf_num / alpha; 6 exploring_wolf_num= rand() % (MAX_VALUE - MIN_VALUE + 1) + MIN_VALUE;
猛狼数量定义:猛狼数量=狼群数量(wolf_num)-探狼数量(exploring_wolf_num)-头狼数量(1)
1 int fierce_wolf_num;//猛狼数量 2 //猛狼数量,狼群数量(wolf_num)-探狼数量(exploring_wolf_num)-头狼数量(1) 3 fierce_wolf_num = wolf_num - exploring_wolf_num - 1;
狼群行为
游走
探狼不断向猎物方向游走,当探狼离猎物的距离比头狼要近(Yi>Ylead,其中i为探狼的标号,lead为头狼的标号)或者探狼游走的次数超出一定值,就会触发下一个行为——召唤行为。
游狼游走后的位置定义图下所示,xidp为游狼下一个p方向的位置(p=1,2,...,h),xid为当前游狼的位置,stepad为游走步长中的第d维方向,sin表达式为方向(把360度分成h个方向,p/h表示在第p个方向)。
整体的公式可以理解为,游狼下一个位置=游狼当前位置+方向*步长。
![]()
例:游狼需要从四周检测离猎物最近的方向决定下一步要走的位置
1 void WPA::wandering() 2 { 3 for(int epoch=0;epoch< max_wandering_iter;++epoch) { 4 for (int i = 1; i <= exploring_wolf_num; ++i) { 5 if(wolfs[i].calculateFitness()<head_wolf_iter->calculateFitness()) {//若Yi大于头狼所感知的猎物气味浓度Ylead,表明猎物离探狼i已相对较近且该探狼最有可能捕获猎物 6 update();//更新狼头位置 7 flag = true;//Yi>Ylead 8 draw(); 9 return; 10 }else {//若Yi<Ylead,则探狼先自主决策,即探狼向h个方向分别前进一步 11 for(int p=1;p<=h;++p) { 12 Wolf tmp; 13 tmp.x = wolfs[i].x + sin(2 * pi*p / h)*StepA[0]; 14 tmp.y = wolfs[i].y + sin(2 * pi*p / h)*StepA[1]; 15 tmp.target_x = 500; 16 tmp.target_y = 500; 17 if(tmp.calculateFitness()<wolfs[i].calculateFitness()) { 18 wolfs[i] = tmp; 19 cout << "游走行为:" << "第" << i + 1 << "狼:" << "x=" << wolfs[i].x << ",y=" << wolfs[i].y << endl; 20 } 21 } 22 draw(); 23 } 24 } 25 } 26 flag = true;//T>Tmax 27 }
召唤
头狼通过嚎叫发起召唤行为,召集周围的fierce_wolf_num匹猛狼向头狼所在位置迅速靠拢(奔袭)。奔袭途中,若第i只猛狼感知到的猎物的距离比头狼离猎物的距离还要近,则这只猛狼就变成头狼,并发起召唤行为;
否则第i只猛狼继续奔袭直到其与头狼之间的距离小于一定的阈值(dis<dnear)时加入到对猎物的攻击行列,即转入围攻行为。
猛狼下一个位置定义如下,xid为当前猛狼位置,stepbd为召唤步长中的第d维方向,gdk为第k只(这里k=1)猎物在d维方向
整体的公式可以理解为,猛狼下一个位置=猛狼当前位置+步长*方向

dnear定义如下,D为维度,w为距离判定因子,maxd与mind分别为第d个维度的最大最小值

1 void WPA::call() 2 { 3 while(!besiege_flag) { 4 //猛狼靠近头狼 5 for (int i = exploring_wolf_num + 1; i < wolf_num; ++i) { 6 if (wolfs[i].calculateFitness() < head_wolf_iter->calculateFitness()) { 7 //存疑:原来的猛狼是否要更新为探狼的身份(原来的探狼是否更新为猛狼)? 8 //这里我的做法是:只是交换头狼和猛狼的身份 9 swap_wolf(head_wolf_iter, head_wolf_iter + i);//交换头狼和猛狼的身份 10 draw(); 11 break;//继续执行召唤行为,Yi>Ylead 12 } 13 else { 14 //猛狼奔袭 15 wolfs[i].x = wolfs[i].x + StepB[0] * (head_wolf_iter->x - wolfs[i].x) / abs(head_wolf_iter->x - wolfs[i].x); 16 wolfs[i].y = wolfs[i].y + StepB[1] * (head_wolf_iter->y - wolfs[i].y) / abs(head_wolf_iter->y - wolfs[i].y); 17 cout << "召唤行为:" << "第" << i + 1 << "狼:" << "x=" << wolfs[i].x << ",y=" << wolfs[i].y << endl; 18 draw(); 19 //计算判定距离d_near 20 double d_near = 0; 21 for (int i = 0; i < dim; ++i) { 22 d_near += (1 / (dim*w))*abs(maxd[i] - mind[i]); 23 } 24 //转入围攻行为 25 if (cal_distance(head_wolf_iter->x, head_wolf_iter->y, wolfs[i].x, wolfs[i].y) < d_near) { 26 besiege_flag = true; 27 update(); 28 break; 29 } 30 } 31 } 32 } 33 34 }
围攻
经过奔袭的猛狼已离猎物较近,这时猛狼要联合探狼对猎物进行紧密地围攻以期将其捕获。这里将离猎物最近的狼,即头狼的位置视为猎物的移动位置。[1]
猛狼围攻下一个位置定义如下,xid为当前猛狼位置,stepcd为围攻步长中的第d维方向,gdk为第k只(这里k=1)猎物在d维方向,λ为[-1,1]间均匀分布的随机数。
整体的公式可以理解为,猛狼围攻下一个位置=猛狼当前位置+随机数*步长*方向

若实施围攻行为后游狼和猛狼感知到的当前狼与猎物的距离比原来与猎物的距离要近,则更新当前狼的位置
1 void WPA::besiege() 2 { 3 if (head_wolf_iter->calculateFitness() > 10) { 4 cout << "围攻行为(失败)" << endl; 5 draw(); 6 return; 7 } 8 int MIN_VALUE = -1; 9 int MAX_VALUE = 1; 10 for (int i = 0; i < wolfs.size(); ++i) { 11 int r = rand() % (MAX_VALUE - MIN_VALUE + 1) + MIN_VALUE; 12 // double tmpx = wolfs[i].x + r * StepC[0] * abs(wolfs[i].target_x - wolfs[i].x); 13 // double tmpy = wolfs[i].y + r * StepC[1] * abs(wolfs[i].target_y - wolfs[i].y); 14 // if (tmpx < wolfs[i].x - StepC[0] || tmpy < wolfs[i].y - StepC[1]) { 15 // //不改变位置 16 // }else { 17 // wolfs[i].x = tmpx; 18 // wolfs[i].y = tmpy; 19 // } 20 wolfs[i].x = wolfs[i].x + r * StepC[0] * abs(wolfs[i].target_x - wolfs[i].x); 21 wolfs[i].y = wolfs[i].y + r * StepC[1] * abs(wolfs[i].target_y - wolfs[i].y); 22 cout << "围攻行为:" << "第" << i + 1 << "狼:" << "x=" << wolfs[i].x << ",y=" << wolfs[i].y << endl; 23 draw(); 24 } 25 }
三种行为的步长关系如下,S为步长因子

1 vector<int> maxd;//维度最大值 2 vector<int> mind;//维度最小值 3 vector<double> StepA;//游走步长 4 vector<double> StepB;//召唤步长 5 vector<double> StepC;//围攻步长 6 double S = 1000;//步长因子 7 int dim = 2;//维度 8 //二维空间,表示X,Y,所以把数组大小设置为2 9 StepA.resize(dim); 10 StepB.resize(dim); 11 StepC.resize(dim); 12 //初始化各种行为的步长 13 //围攻步长=1.024,召唤步长=4.096,游走步长=2.048 14 maxd = { 512,512 }; 15 mind = { -512,-512 }; 16 for (int i = 0; i < dim; ++i) { 17 StepC[i] = abs(maxd[i] - mind[i]) / S; 18 StepB[i] = 4 * StepC[i]; 19 StepA[i] = StepB[i] / 2; 20 }


引用:[1]吴虎胜;张凤鸣;吴庐山.一种新的群体智能算法——狼群算法[J].系统工程与电子技术,2013,35(11):2430-2438.
ZafirTan/Wolf-Pack-Algorithm: 狼群算法(WPA)案例,用OpenCV显示效果 (github.com)

浙公网安备 33010602011771号