Hunger games search: Visions, conception, implementation, deep analysis, perspectives, and towards performance shifts
期刊:Expert Systems With Applications SCI1区
主体框架
public HGS(){
initialize();
calculateFitness();
sortTheFitness();
calculateHungry();
for (t = 0;t<T;t++){
UpdateWeight();
UpdateLocation();
calculateFitness();
sortTheFitness();
calculateHungry();
savePoints();
}
}
ps:经过多次的复现以后,我发现实现一个算法最快的方式就是先将算法分模块搞好,所以上来先贴一个框架图。
这篇论文主要分为四个模块,分别是初始化模块,适应度与饥饿值设置模块,权重更新模块,位置更新模块。
论文有意思的点:
在这篇文章里面的公式非常的多,但是在这里我只想着重介绍这篇论文的创新点:饥饿机制。
文章定义了一个叫饥饿值的东西,如下公式所示。我们发现当且仅当i适应度值最小的时候,饥饿值为0,别的情况饥饿值为上次的饥饿值加上H。也就是说,只要适应度高,那么就会越来越饥饥饿。后面我们再说这个饥饿度怎么用。值得注意的是:每一轮迭代都会更新一次hungry值
hungry ( i ) = { 0 , AllFitness ( i ) = = B F hungry ( i ) + H , AllFitness ( i ) ! = B F \text { hungry }(i)=\left\{\begin{array}{c}0, \text { AllFitness }(i)==B F \\\text { hungry }(i)+H, \text { AllFitness }(i) !=B F\end{array}\right. hungry (i)={0, AllFitness (i)==BF hungry (i)+H, AllFitness (i)!=BF
其中 B F BF BF为最差的适应度,也就是最小的适应度值。 H H H的定义如下所示
T H = F ( i ) − B F W F − B F × r 6 × 2 × ( U B − L B ) H = { L H × ( 1 + r ) , T H < L H T H , T H ≥ L H \begin{aligned}&T H=\frac{F(i)-B F}{W F-B F} \times r_6 \times 2 \times(U B-L B)\\&H=\left\{\begin{array}{c}L H \times(1+r), T H<L H \\T H, T H \geq L H\end{array}\right.\end{aligned} TH=WF−BFF(i)−BF×r6×2×(UB−LB)H={LH×(1+r),TH<LHTH,TH≥LH
其中 F ( i ) F(i) F(i)为适应度值, W F WF WF(Worse Fitness)为最大的适应度值(至今为止), U B 和 L B UB和LB UB和LB为问题空间的上下界。 L H LH LH(Limit Hungry)则为饥饿感的下限,r是一个随机值。
也就是说当适应度值 F ( i ) F(i) F(i)越小的时候, T H TH TH(Temp Hungry)会越小,当低于上限的时候,本轮增加的饥饿度 H H H,将会被设置为大于 T H TH TH的一个随机值。
我们从上面的三个公式只要知道一个关键结论就好了:
适应度越小,越不饥饿。
适应度越大,这轮增加的饥饿度越高。
那么这个饥饿度值该怎么用呢?
W 1 ( i ) → = { hungry ( i ) ⋅ N SHungry × r 4 , r 3 < l 1 , r 3 > l \overrightarrow{W_1(i)}=\left\{\begin{array}{c}\text { hungry }(i) \cdot \frac{N}{\text { SHungry }} \times r_4, r_3<l \\1, r_3>l\end{array}\right. W1(i)={ hungry (i)⋅ SHungry N×r4,r3<l1,r3>l
W 2 ( i ) → = ( 1 − exp ( − ∣ hungry ( i ) − SHungry ∣ ) ) × r 5 × 2 \overrightarrow{W_2(i)}=(1-\exp (-\mid \text { hungry }(i)-\text { SHungry } \mid)) \times r_5 \times 2 W2(i)=(1−exp(−∣ hungry (i)− SHungry ∣))×r5×2
论文构建了两个向量,分别是 W 1 ( i ) , W 2 ( i ) W_1(i),W_2(i) W1(i),W2(i),这两个向量负责对个体进行变异,这里最关键的点是 S H u n g r y SHungry SHungry,这是所有 h u n g r y hungry hungry的总和,我们可以发现,两个向量组的规律。
对于 W 1 ( i ) W_1(i) W1(i)而言,hungry越小,里面的值越小
对于 W 2 ( i ) W_2(i) W2(i)而言,hungry越小,里面的值越接近2也就是说里面的值越大。
X ( t + 1 ) → = { Game 1 : X ( t ) → ⋅ ( 1 + randn ( 1 ) ) , r 1 < l Game 2 : W 1 → ⋅ X b → + R ⃗ ⋅ W 2 → ⋅ ∣ X b → − X ( t ) → ∣ , r 1 > l , r 2 > E Game 3 : W 1 → ⋅ X b → − R ⃗ ⋅ W 2 → ⋅ ∣ X b → − X ( t ) → ∣ , r 1 > l , r 2 < E \overrightarrow{X(t+1)}=\left\{\begin{array}{c}\text { Game }_1: \overrightarrow{X(t)} \cdot(1+\operatorname{randn}(1)), r_1<l \\\text { Game }_2: \overrightarrow{W_1} \cdot \overrightarrow{X_b}+\vec{R} \cdot \overrightarrow{W_2} \cdot\left|\overrightarrow{X_b}-\overrightarrow{X(t)}\right|, r_1>l, r_2>E \\\text { Game }_3: \overrightarrow{W_1} \cdot \overrightarrow{X_b}-\vec{R} \cdot \overrightarrow{W_2} \cdot\left|\overrightarrow{X_b}-\overrightarrow{X(t)}\right|, r_1>l, r_2<E\end{array}\right. X(t+1)=⎩ ⎨ ⎧ Game 1:X(t)⋅(1+randn(1)),r1<l Game 2:W1⋅Xb+R⋅W2⋅ Xb−X(t) ,r1>l,r2>E Game 3:W1⋅Xb−R⋅W2⋅ Xb−X(t) ,r1>l,r2<E
最后,每轮迭代的时候都根据上面的公式对位置进行变换,在文中,l被设置为0.08,在公式1中,其实就是一个高斯变异,烟花算法也用到这个。
公式二和三,本质上是一个DE,差分进化的两个公式都是没头脑的,就算正向学习和反向学习,很多论文也有这个。最后还有个E,这个E也是作者自己设定的一个阈值,可以参照野狗群算法,不过作者将这个搜索阈值自己自定义了:
E = sech ( ∣ F ( i ) − B F ∣ ) E=\operatorname{sech}(|F(i)-B F|) E=sech(∣F(i)−BF∣)
( sech ( x ) = 2 e x + e − x ) \left(\operatorname{sech}(x)=\frac{2}{e^x+e^{-x}}\right) (sech(x)=ex+e−x2)
sech就是双曲正割函数
x越大,y越小,也就是说,当前解的适应度值越小,E就会越接近1,公式3被调用的概率就会变高。当前的解适应度越大,E就会越接近0,公式2被调用的概率越大。
最后再补充一个
R ⃗ = 2 × shrink × rand − shrink shrink = 2 × ( 1 − t T ) \begin{aligned}& \vec{R}=2 \times \text { shrink } \times \text { rand }- \text { shrink } \\& \text { shrink }=2 \times\left(1-\frac{t}{T}\right)\end{aligned} R=2× shrink × rand − shrink shrink =2×(1−Tt)
这个东西就是随着迭代次数,波动逐渐减少的向量,最后会收敛到0,值得注意的是R里面是有正有负的,意味着game2和game3是存在等效的情况。
实验的截图~
寻找sphere最小值。
实际代码
import javax.swing.*;
import java.awt.*;
import java.util.*;
import java.util.List;
import java.util.function.Consumer;
/**
* Hunger games search: Visions, conception, implementation, deep analysis, perspectives, and towards performance shifts
*/
public class HGS {
int N = 30;//5 10 30 50 100
Double D;
Double L = 0.08;
Double H;
Double SHungry;
List<Double> Xb;
double BF = Double.MAX_VALUE;
double WF = -Double.MAX_VALUE;
double lb = 0;
double ub = 1;
int dimension = 30;
int t;
int T = 1000;
List<Double> R = new ArrayList<>(dimension);
ArrayList<Point> res = new ArrayList<>();
double LH = 10000;
public HGS(){
initialize();
calculateFitness();
sortTheFitness();
calculateHungry();
for (t = 0;t<T;t++){
UpdateWeight();
UpdateLocation();
calculateFitness();
sortTheFitness();
calculateHungry();
savePoints();
}
}
public static void main(String[] args) {
HGS hgs = new HGS();
ArrayList<Point> points = hgs.res;
// 创建绘制折线图的窗口
JFrame frame = new JFrame("折线图");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new LineChart(points));
frame.setSize(800, 600);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public void savePoints(){
System.out.println("curt:"+t+",BF="+BF);
res.add(new Point(t,(int)(BF*100000)));
}
List<Individual> individuals = new ArrayList<>();
public void initialize(){
IndividualFactory individualFactory = new IndividualFactory(dimension,lb,ub);
individuals = individualFactory.getRandomIndividuals(N);
t = 0;
}
public void calculateFitness(){
for (Individual i:
individuals) {
i.UpDateFitness();
}
}
public void sortTheFitness(){
individuals.sort((a,b)->new Double(a.fitness).compareTo(b.fitness));//按照适应度排序
WF = Math.max(WF,individuals.get(N-1).fitness);
if(BF>individuals.get(0).fitness){
BF = individuals.get(0).fitness;
Xb = new ArrayList<>(individuals.get(0).position);//copy一份
}
BF = Math.min(BF,individuals.get(0).fitness);
}
public void calculateHungry(){
for (Individual i:
individuals) {
if(i.fitness==BF){
i.hungry = 0;
}else {
double r6 = Math.random();
Double TH = (i.fitness-BF)/(WF-BF)*r6*2*(ub-lb);
Double H;
if(TH<LH){
H = LH*(1+Math.random());
}else {
H = TH;
}
i.hungry = i.hungry+H;
}
}
}
public void UpdateWeight(){
double SHungry = 0;
for (Individual i:
individuals) {
SHungry+=i.hungry;
}
for (Individual i:
individuals) {
i.updateW(N,SHungry,L);
}
}
public void UpdateLocation(){
for (Individual i:
individuals) {
i.upDateLocation(L,t,T,Xb,BF);
}
}
}
class Individual{
double hungry;
double fitness;
double lb;
double ub;
List<Double> position = new ArrayList<>();
List<Double> w1;
List<Double> w2;
int dimension;
public double getHungry() {
return hungry;
}
public void setHungry(double hungry) {
this.hungry = hungry;
}
public Individual(double lb, double ub, List<Double> position) {
this.lb = lb;
this.ub = ub;
this.position = position;
dimension = position.size();
w1 = new ArrayList<>(dimension);
w2 = new ArrayList<>(dimension);
for (int i = 0; i < dimension; i++) {
w1.add(0.0);
w2.add(0.0);
}
}
public void Game1(){
//formula 2.1
Random random = new Random();
for (int i = 0; i < dimension; i++) {
double originX = position.get(i);
position.set(i,originX*(1+random.nextGaussian()));
}
}
public void Game2(List<Double> R,List<Double> Xb){
//formula 2.1
for (int i = 0; i < dimension; i++) {
position.set(i,w1.get(i)*Xb.get(i)+R.get(i)*w2.get(i)*Math.abs(Xb.get(i)-this.position.get(i)));
}
}
public void Game3(List<Double> R,List<Double> Xb){
//formula 2.1
for (int i = 0; i < dimension; i++) {
position.set(i,w1.get(i)*Xb.get(i)-R.get(i)*w2.get(i)*Math.abs(Xb.get(i)-this.position.get(i)));
}
}
public void upDateLocation(double l,int t,int T,List<Double> Xb,double BF){
double r1 = Math.random();
double r2 = Math.random();
if(r1<l){
Game1();
}else{
List<Double> R = new ArrayList<>(dimension);
for (int i = 0; i < dimension; i++) {
R.add(0.0);
}
double shrink = 2.0*(1.0-t/T);
for (int i = 0; i < dimension; i++) {
R.set(i,2*shrink*Math.random()-shrink);
}
double E = sech(Math.abs(fitness-BF));
if(r2>E){
Game2(R,Xb);
}else {
Game3(R,Xb);
}
}
}
public static double sech(double x){
return 2.0/(Math.exp(x)+Math.exp(-x));
}
public void updateW(int N,double SHungry,double l){
for (int i = 0; i < dimension; i++) {
//formula 2.6
double r3 = Math.random();
double r4 = Math.random();
if (r3<l){
w1.set(i,hungry*N/SHungry*r4);
}else {
w1.set(i,1.0);
}
double r5 = Math.random();
//formula 2.6
w2.set(i,(1-Math.exp(-Math.abs(hungry-SHungry)))*r5*2);
}
}
public void UpDateFitness(){
this.fitness = Sphere(this.position);
}
public static double Sphere(List<Double> x){
double sum = 0;
for (int i = 0; i < x.size(); i++) {
sum += x.get(i)*x.get(i);
}
return sum;//修正函数
}
}
class IndividualFactory{
Random random = new Random();
private int dimension;
private double lb;
private double ub;
public IndividualFactory(int dimension,double lb,double ub){
this.dimension = dimension;
this.lb = lb;
this.ub = ub;
}
public Individual getRandom(){
List<Double> Position = new ArrayList<>(dimension);
for (int i = 0; i < dimension; i++) {
Position.add(lb+(ub-lb)*random.nextDouble());
}
Individual individual = new Individual(lb,ub,Position);
individual.UpDateFitness();
return individual;
}
public List<Individual> getRandomIndividuals(int N){
List<Individual> individuals = new ArrayList<>(N);
for (int i = 0; i < N; i++) {
individuals.add(getRandom());
}
return individuals;
}
}
class LineChart extends JPanel {
private ArrayList<Point> points;
public LineChart(ArrayList<Point> points) {
this.points = points;
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
int margin = 50; // 留出一些边距
int width = getWidth() - 2 * margin;
int height = getHeight() - 2 * margin;
// 绘制坐标轴
g2d.drawLine(margin, margin, margin, margin + height); // 垂直轴
g2d.drawLine(margin, margin + height, margin + width, margin + height); // 水平轴
// 计算最大和最小的x和y值
int minX = Integer.MAX_VALUE, minY = Integer.MAX_VALUE;
int maxX = Integer.MIN_VALUE, maxY = Integer.MIN_VALUE;
for (Point point : points) {
if (point.x < minX) minX = point.x;
if (point.x > maxX) maxX = point.x;
if (point.y < minY) minY = point.y;
if (point.y > maxY) maxY = point.y;
}
// 绘制折线
g2d.setColor(Color.BLUE);
for (int i = 0; i < points.size() - 1; i++) {
int x1 = margin + (points.get(i).x - minX) * width / (maxX - minX);
int y1 = margin + height - (points.get(i).y - minY) * height / (maxY - minY);
int x2 = margin + (points.get(i + 1).x - minX) * width / (maxX - minX);
int y2 = margin + height - (points.get(i + 1).y - minY) * height / (maxY - minY);
g2d.drawLine(x1, y1, x2, y2);
}
}
}