10
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

群知能〜蟻コロニー最適化法の実装

Last updated at Posted at 2017-05-31

蟻コロニー最適化法をCで実装します。
参考図書は「機械学習と深層学習(小高知宏,オーム社)」です。

群知能(Swarm Intelligence)とは

生物の集団が見せる知的な行動を模した機械学習手法です。
蟻コロニー最適化法の他に
・粒子群最適化法:群れ全体が効率的に餌を見つける挙動をシミュレート
・AFSA:魚の群れが見せる捕食や追尾などの行動特性をシミュレート
などがあります。

蟻コロニー最適化法とは

蟻が巣穴と餌場の間の最短経路を見つける挙動をシミュレートしたものです。

蟻は自分が歩いた道筋にフェロモンを残します。
他の蟻はそのフェロモンの跡を辿って移動しようとしますが、フェロモンは時間の経過と共に蒸発して消えてしまいます。フェロモンが蒸発する前に他の蟻が通れば追加上書きできます。
巣穴と餌場の距離が短いとフェロモンがすぐに上書きされるので、その道筋に多くの蟻が群がることになります。
この結果、最短経路に群れが誘導されるのです。

学習手続き

1.設定したすべての経路のフェロモン濃度を0に初期化する。
2.以下を適切な回数繰り返す
 2.1.すべての蟻を巣穴から餌場まで歩かせる
    経路は、確率εでランダムに、確率(1-ε)でフェロモン濃度に応じて選択する。
 2.2.すべての経路のフェロモン濃度に定数ρ(0~1)を乗じて蒸発させる。
 2.3.各蟻の移動距離Lを求め、個体の選択した経路に対応するフェロモン濃度に、Q*(1/L)を加える。

コードの概要

巣穴と餌場の間に図のような9つの中間分岐点があるような道を考えます。
それぞれの経路の距離は、簡単のため5と1としています。
巣穴から餌場までの最短経路をたどる行動知識を獲得します。

コード解説1〜変数について

cost[][]:道筋の長さを格納
pheromone[][]:それぞれの道のフェロモン濃度を格納
ー1次元目は分岐の方向、2次元目はステップ数です

mstep[][]:実際に歩いた経路の記録
ー1次元目は蟻の個体番号、2次元目はステップ数です。0か1でそれぞれの経路の選択を表します。

コード解説2〜蟻を歩かせる

学習手続き2.1を実現するために、ε-greedy法により経路を選択します。
確率εでランダムに、確率(1-ε)でフェロモン濃度が高い道を選択する

if((rand1()<EPSILON)||(abs(pheromone[0][s]-pheromone[1][s])<1e-9)){
    mstep[m][s]=rand01(); //ランダムに行動
}
else{ //濃度が高い方を選択
    if(pheromone[0][s]>pheromone[1][s])mstep[m][s]=0;
    else mstep[m][s]=1; 
}

コード解説3〜フェロモンの更新

###1.フェロモンの蒸発 
一律にすべての道のフェロモンをRHO(ここでは0.8)倍に薄めます

for(int i=0;i<2;++i){
    for(int j=0;j<STEP;++j){
        pheromone[i][j]*=RHO; //すべてをRHO倍に薄めている
    }
}

###2.フェロモンの上書き
各個体の移動距離Lを求め、個体の選択した経路に対応するフェロモン濃度に、Q*(1/L)を加えます。

for(int m=0;m<NOA;++m){
    /*個体mの移動距離lmの計算*/
    lm=0;
    for(int i=0;i<STEP;++i){
        lm+=cost[mstep[m][i]][i];
    }
    /*フェロモンの上塗り*/
    for(int i=0;i<STEP;++i){
        pheromone[mstep[m][i]][i]+=Q*(1.0/lm);
    }
    sum_lm+=lm;
}
printf("Average Distance=%lf\n\n",sum_lm/NOA); //蟻の歩いた平均距離を出力

結果

最初フェロモン濃度を0に初期化し、経路もランダムに選んでいたため、最初は平均移動距離が
25.8(1試行目), 28.0(2試行目)と大きくなっています。

学習が進むにつれ、どんどん小さくなり、(8試行目:22.4, 9試行目:22.8)

19試行目で16.8となり、かなり効率よく移動できるようになったことがわかります。

全コード

aco.c
#include<stdio.h>
#include<stdlib.h>

#define _CRT_SECURE_NO_WARNINGS

#define NOA 10       //蟻の個体数
#define ILIMIT 20    //繰り返しの回数
#define Q 3          //フェロモン更新の定数
#define RHO 0.8      //蒸発の定数
#define STEP 10      //道のりのステップ数
#define EPSILON 0.15 //行動選択のランダム性を決定
#define SEED 31      //乱数のシード

void printp(double pheromone[2][STEP]); //表示
void printmstep(int mstep[NOA][STEP]);  //蟻の挙動の表示
void walk(int cost[2][STEP],double phenomenone[2][STEP],
        int mstep[NOA][STEP]);  //蟻を歩かせる
void update(int cost[2][STEP],double pheromone[2][STEP],
        int mstep[NOA][STEP]);  //フェロモンの更新
double rand1();
int rand01(); 

/**************************************************************/
int main(){
    int cost[2][STEP]={ //各ステップのコスト(距離が短い方を1、長い方を5とする)
        {1,1,1,1,1,1,1,1,1,1},
        {5,5,5,5,5,5,5,5,5,5}
    };
    double pheromone[2][STEP]={0}; //各ステップのフェロモン量
    int mstep[NOA][STEP]={0};      //蟻が歩いた過程
    int i;                         //繰り返し回数の制御

    srand(SEED);

    for(i=0;i<ILIMIT;++i){
        /*フェロモンの状態出力*/
        printf("%d:\n",i); 
        printp(pheromone);
        /*蟻を歩かせる*/
        walk(cost,pheromone,mstep);
        /*フェロモンの更新*/
        update(cost,pheromone,mstep);
    }
    /*フェロモンの状態出力*/
    printf("%d:\n",i); 
    printp(pheromone);

    return 0;
}

/****************************************************************/
/*フェロモンの更新*/
void update(int cost[2][STEP],double pheromone[2][STEP],int mstep[NOA][STEP]){
    int m;           //蟻の個体番号
    int lm;          //蟻の歩いた距離
    double sum_lm=0; //蟻の歩いた距離の合計

    /*フェロモンの蒸発*/
    for(int i=0;i<2;++i){
        for(int j=0;j<STEP;++j){
            pheromone[i][j]*=RHO; //すべてを0.8倍に薄めている
        }
    }

    /*蟻による上塗り*/
    for(int m=0;m<NOA;++m){
        /*個体mの移動距離lmの計算*/
        lm=0;
        for(int i=0;i<STEP;++i){
            lm+=cost[mstep[m][i]][i];
        }
        /*フェロモンの上塗り*/
        for(int i=0;i<STEP;++i){
            pheromone[mstep[m][i]][i]+=Q*(1.0/lm);
        }
        sum_lm+=lm;
    }
    printf("Average Distance=%lf\n\n",sum_lm/NOA); //蟻の歩いた平均距離を出力
}

/*蟻を歩かせる*/
void walk(int cost[2][STEP],double pheromone[2][STEP],int mstep[NOA][STEP]){
    int m; //蟻の個体番号
    int s; //蟻の現在ステップ位置

    for(m=0;m<NOA;++m){
        for(s=0;s<STEP;++s){
            /*ε-greedyによる行動選択*/
            if((rand1()<EPSILON)||(abs(pheromone[0][s]-pheromone[1][s])<1e-9)){
                mstep[m][s]=rand01(); //ランダムに行動
            }
            else{ //濃度が高い方を選択
                if(pheromone[0][s]>pheromone[1][s])mstep[m][s]=0;
                else mstep[m][s]=1; 
            }
        }
    } 
    printmstep(mstep);//蟻の挙動の出力
}

/*蟻の挙動の表示*/
void printmstep(int mstep[NOA][STEP]){
    printf("*mstep\n");
    for(int i=0;i<NOA;++i){
        for(int j=0;j<STEP;++j){
            printf("%d",mstep[i][j]);
        }
        printf("\n");
    }
}

/*フェロモンの表示*/
void printp(double pheromone[2][STEP]){
    for(int i=0;i<2;++i){
        for(int j=0;j<STEP;++j){
            printf("%4.2lf ",pheromone[i][j]);
        }
        printf("\n");
    }
}

/*乱数生成*/
double rand1(){
    return (double)rand()/RAND_MAX;
}

int rand01(){
    int rnd;
    while((rnd=rand())==RAND_MAX);
    return (int)((double)rnd/RAND_MAX*2);
}
10
11
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?