processing で random walk を書いてみた

真鍋です。
統計学の初学者で、processing 初心者です。この度は何とかカレンダーを書いてと言われ、それなりにいい機会なので、「自然科学の統計学」を勉強していたら出て来た random walk (p278) を、前から興味を持っていた processing で描画してみようと思い立ちました。
processing の埋め込み方は、このブログを参照にしました。

random walk とは、時間が進むごとに、確率 p1 , 確率 q-1 移動するような系列のことです。つまり、時間を横軸に取ると、上へ行ったり下へ行ったりと、ランダムにフラフラとした軌跡を描きます。酔っ払いの千鳥足に似ているので、「酔歩」とも呼ばれるとのことです。

下の図は、1 次元の random walk ( 1 方向のみにランダムに移動する。この場合は y 方向。x 軸は時間を表している) です。
pq は等しく、\frac{1}{2} と設定しています。
スタートボタンを押すと始まります。押すたびに、毎回異なる軌跡を描きます。processing のコードも、一応、、下に載せています。




float p = 0.5;

void setup(){ 
  size(600,200);
  background(255);
  stroke(0,100);
  strokeWeight(2);
  line(10,height/2,width-10,height/2);
  line(10,10,10,height-10);
  stroke(125,0,0);
  strokeWeight(1);
}

int x = 10;
int y = 200/2;
int lastx = x;
int lasty = y;

void draw(){
  if (x <= width-10){
  x += 1;
  float rnd = random(1);
  if (rnd < p){
    y += 1;
  }else{
    y -= 1;
  }
  line(lastx,lasty,x,y);
  lastx = x;
  lasty = y;
  }
}


移動開始のうちは原点付近をふらつきますが、一度外れる (つまり同じ方向への移動が連続する)と、なかなか戻って来れなくなってしまう様子がみて取れます。
しかし、この 1 次元の random walk、実は、「必ず 1 度は原点に戻ってくる」という性質があるそうです。
少なくとも 1 回でも原点を通る確率は、
 1-|p-q|
となることが知られているそうで、今は pq\frac{1}{2} ですから、確率は 1 となります ( pq が異なる時は、戻って来ないこともある)。コースを外れたら戻って来れなさそうな気がしますが、「いつかは(仮にそれが無限であっても)」戻ってくるということです。この証明は難しくてわたしなぞにはよくわかりませんが(こことか参考に)、、興味深いのは、2 次元の random walk でも必ず戻ってくる (!) そうですが、3 次元だと、たとえ無限であっても、その確率は 1 にならないそうです。次元によって結果が異なるのは大変面白いです。いつか理解したいです。

2 次元の random walk は、x 方向と y 方向の両方向に random に移動する(こちらの方が実際の酔歩に近しい)ものですが、これも作ってみました。スタートボタンで始まります。



class Walker{
  int x;
  int y;
  
  Walker() {
    x = width/2;
    y = height/2;
  }
  
  void display() {
    stroke(0);
    point(x,y);
  }
  
  void step() {
    float stepx = int(random(3))-1;
    float stepy = int(random(3))-1;
    x += stepx;
    y += stepy;
  }
}


Walker w;

void setup(){
  size(200,200);
  w = new Walker();
  background(255);
  stroke(0,100);
  strokeWeight(2);
  line(10,height/2,width-10,height/2);
  line(width/2,10,width/2,height-10);
  stroke(125,0,0);
  strokeWeight(1);
}

void draw(){
  w.step();
  w.display();
}

原点に戻るように全く見えないときがありますが、確率的にはいつかは戻るらしいです。期待値無限大らしいですが。

虫が動いてるみたいで面白いので、しばらく眺めてしまいます。

この虫を数百匹に増やして(しかも色を変えて)みたらどうなるのだろうと思いついて作ってみました。



void setup(){
  size(500,500);
  background(255);
  //stroke(0,100);
  //strokeWeight(2);
  //line(10,height/2,width-10,height/2);
  //line(width/2,10,width/2,height-10);
  stroke(150,0,0,40);
  strokeWeight(1);
  frameRate(3000);
}

int randWalk(int x,int y,float r,float g,float b){
  int last = new int[2];
  last[0] = x;
  last[1] = y;
  float rnd = random(1);
  float p = 0.25;
  if (rnd < p){
    x += 1;
  }else if (rnd < p*2){
    x -= 1;
  }else if (rnd < p*3){
    y += 1;
  }else if (rnd < 1){
    y -= 1;
  }
  stroke(r,g,b,10);
  line(last[0],last[1],x,y);
  last[0] = x;
  last[1] = y;
  return last;
}

int h = 0;
int last_val1 = new int[400];
int last_val2 = new int[400];
int mode = 1;
void draw(){
  if (h == 0){
    for (int n = 0; n < 400; n += 1){
      int tmp = randWalk(250,250,127+127*sin(5*radians(n)),127+127*sin(4*radians(n)),127+127*sin(2*radians(n)));
      last_val1[n] = tmp[0];
      last_val2[n] = tmp[1];
    }
  }else{
      for (int n = 0; n < 400; n += 1){
        int tmp = randWalk(last_val1[n],last_val2[n],127+127*sin(5*radians(n)),127+127*sin(4*radians(n)),127+127*sin(2*radians(n)));
        last_val1[n] = tmp[0];
        last_val2[n] = tmp[1];
      }
  }
  h ++;
}

void mousePressed() {
  mode = mode*(-1);      
}

void mouseReleased() {
  loop();        
}


しばらく待ってるとわりかし面白い模様が出来上がります(面白くないですかそうですか)。random なので作るたびに、描画は毎回異なります。
processing、やってみると非常に面白いので、数学の勉強と組み合わせて、色々と作ってみようと思う今日この頃です。