LoginSignup
2
0

More than 3 years have passed since last update.

C++でシェリングの分居モデルを実装する

Last updated at Posted at 2020-09-19

0 はじめに

「シェリングの分居モデル」というものを、C++という言語を用いて実装します。C++を使ったことがない人にも分かるよう、なるべく単純な実装を心がけましたが、そんなに分かりやすくないかもしれません。
おかしいところ、分かりにくいところ等ありましたら、コメントとかで教えてくださると嬉しいです。

0-1 分居モデルとは

トマス・シェリングというアメリカの経済学者(2005年ノーベル経済学賞を受賞)が、1969年に発表したものです。
地域社会が人種ごとに分居する傾向にあることに注目し、ミクロレベル(人々の個人的な好みや行動様式)から推測できることが、必ずしもマクロレベル(社会全体の有様)に反映するとは限らないことを定式化した、マルチエージェント・シミュレーションを社会学に取り入れた原点ともいえるものです。

0-2 今回実装するモデルの仕組み

分かりやすくゲームっぽく説明します。

  • このシミュレータには、碁盤のように整理された居住区が存在します。
  • 3種類の人種が存在していて、それぞれを「%人種」、「@人種」、「!人種」と呼称します。
  • 人々は、この碁盤の線が交わる点(整数値の座標で管理ができるため)に住むことができます。
  • このシミュレータ上で生活する人々は、各ターンに自分の周り(縦、横と斜めの合計8マス、これを近隣住民とします)を見渡し、同人種が近隣住民のうち1/3以上存在した場合に安心し、そこに住み続けます。
  • 安心できない場合は、居住区のランダムな場所に引っ越します。
  • 居住区に存在する全住民が安心した場合にシミュレーションを終了します。

近隣住民のうち3分の1だけ同人種が存在すれば良いわけですから、かなり異人種に寛容的に思えますよね?
結果をシミュレーションで確かめてみましょう。

1 実装

順番に実装していきましょう。
#1-1~4を全て上からコピーして順番に配置するだけで実行できます。

1-1 準備

人口とか住所を保管する配列とか定義していきましょう。
最初の7行はおまじないなのでC++わからない人は無視してください。

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<iostream>
#include<utility>
#include <iomanip>
using namespace std;


#define BOARD_WIDTH 30
#define BOARD_HEIGHT (BOARD_WIDTH)
#define population 500
int board[BOARD_HEIGHT][BOARD_WIDTH];
pair<int,int> address[population];

int dx[8]={-1,0,1,-1,1,-1,0,1};
int dy[8]={1,1,1,0,0,-1,-1,-1};

char symbol[4]={' ','%','@','!'};//表示用のシンボル

  • BOARD_WIDTHとBOARD_HEIGHTはボードの大きさです。boardという配列に人種を登録します。
  • populationは人口です。この3つの値は自由にいじっていいですが、必ず (BOARD_WIDTH)×(BOARD_HEIGHT)>populationとなるように気をつけてください。1マスも空き地がないと引っ越しが出来なくなってしまいます。
  • addressは戸籍みたいなものです。人に住所(座標)を紐付けるために使います。pairという型で直交座標x,yを格納しています。
  • dx,dyは見渡す時に使うものです。これを定義しておくとfor文で回すだけで周りを確認できるので後で楽になります。
  • symbolは人種の表示用に使います。

1-2 初期配置

board上に人々を住まわせていきましょう。完全にランダムに決めていきます。

void prepare(){
    for (int i=0;i<BOARD_HEIGHT;i++){ //全住所を-1で初期化
        for (int j=0;j<BOARD_WIDTH;j++){
            board[i][j]=-1;
        }
    }

    for (int i=0;i<population;i++){
        int x=rand()%BOARD_HEIGHT;
        int y=rand()%BOARD_WIDTH;
        while(board[x][y]!=-1){//空き地でなかった場合繰り返す
            x=rand()%BOARD_HEIGHT;
            y=rand()%BOARD_WIDTH;
        }
        board[x][y]=rand()%3;//人種の決定
        address[i]=make_pair(x,y);
    }
}

void とは戻り値のない関数のことです。
全住所を-1で初期化してから人口分の住所を生成しています。この時に住所がダブらないように気をつけてください。

1-3 描画用の関数

void draw(){
    for (int x=0;x<BOARD_HEIGHT;x++) {
        for (int y=0;y<BOARD_WIDTH;y++)
            cout << setw(2) << symbol[board[x][y]+1];
        cout << endl;
    }
}

シミュレーション上の空間を描画します。setw(2)は、2文字分のマスに表示をしてね、という意味で、見やすくするためだけの工夫です。

1-4 メイン関数

int main(){
    srand((unsigned int)time(NULL));//乱数を時間で初期化
    prepare();
    draw();

    bool flag=false;
    while(!flag){
        flag=true;
        for (int i=0;i<population;i++){
            int x=address[i].first,y=address[i].second;
            int countSame=0,countDiffrent=0;
            for (int j=0;j<8;j++){
                if (x+dx[j]<0 or x+dx[j]>=BOARD_HEIGHT or y+dy[j]<0 or y+dy[j]>=BOARD_WIDTH){//範囲外
                    continue;
                }
                if (board[x+dx[j]][y+dy[j]]==-1){
                    continue;
                }else if(board[x+dx[j]][y+dy[j]]==board[x][y]){
                    countSame++;
                }else{
                    countDiffrent++;
                }
            }   
            if (countSame*2<countDiffrent){//安心できない場合
                flag=false;
                int race=board[x][y];
                board[x][y]=-1;
                x=rand()%BOARD_HEIGHT;
                y=rand()%BOARD_WIDTH;
                while(board[x][y]!=-1){//空き地でなかった場合繰り返す
                    x=rand()%BOARD_HEIGHT;
                    y=rand()%BOARD_WIDTH;
                }
                address[i]=make_pair(x,y);
                board[x][y]=race;
            }

        }
    }
    cout << "----------結果----------" << endl;
    draw();
}

ルール通りの実装です。一番最初のsrand((unsigned int)time(NULL))は、乱数の初期化です。
flagというブール値が最後までtrueを保ち続けたら、全住民が安心して暮らしているということなので、ループを終了します。

2 実行結果

どのような結果になりましたか?最終的にかなり分居が進んでいるのが確認できたと思います。
自分が手元で実行した結果はこんな感じです。

スクリーンショット 2020-09-19 17.50.38.png

下半分が結果です。陣取り合戦みたいになってますね......
適当なタイミングでdraw関数を入れて、分居の進み具合を確認してみるのも面白いです。

3 終わりに

いろいろなパラメータをいじって遊んでみてください。人種によってルールを変えてみたら、より面白いデータが得られるかもしれません。
この間アメリカからの帰国生の友達にこのモデルを見せたら「3分の1も同人種が必要なのはなかなかのレイシストだ」みたいなことを言われました。すごいですね.....自分はずっと日本に住んでいるので、こんな環境には耐えられないかもしれません。(決してレイシストとかではないです。慣れていないので落ち着かないだけ)

4 参考文献

人工社会構築指南―artisocによるマルチエージェント・シミュレーション入門(山影進 著)を参考、一部引用させていただきました。ありがとうございます。

2
0
0

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
2
0