challenge 2次元ランダムウォーク

2次元ランダムウォークをつくってみてください。

******

元は3本建てにしようかと思ったけど、上の一本に絞りました。おまけとして、3本とも下に補足しておきます。作れるようでしたら作ってみてください。

1.一次元のランダムウォークを作ってください。

1.1 データファイルに残してください。 フォーマット:時間 位置

おまけ)

可視化が簡単な処理系・プログラミング言語でしたら実際に可視化してみてください。フォーマットしたファイルをスプレッドシートやplotutilitiesなどの可視化ソフトを使って、実際に動きをかくにんしてみましょう。:-)

2.同じように2次元のランダムウォークを作ってください。

2.1 1.1と同じようにしてください。

フォーマット:時間 x位置 y位置

3.凝りたければ、アニメーションにするもよし、3次元の動きをとるもよし、自分の想像力がいかせるところまでやってみてください。

http://ja.wikipedia.org/wiki/%E3%83%A9%E3%83%B3%E3%83%80%E3%83%A0%E3%82%A6%E3%82%A9%E3%83%BC%E3%82%AF

分からないというヒトへの分かりにくいヒント:

今の位置から次の時間の位置が決まるのですが、決まりかたが、乱数で一歩後退するか一歩先にいくか?ということをやればよいです。

Posted feedbacks - Flatten

Nested Hidden
こんな感じでしょうか。 移動距離は1固定です。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<?php
//移動回数
define('LOOP', 10);

//初期位置
$pos[0] = array('x' => 0, 'y' => 0);

for($time = 1; $time <= LOOP; $time++){
    $rad = deg2rad(mt_rand()%360);
    $x = $pos[$time-1]['x'] + cos($rad);
    $y = $pos[$time-1]['y'] + sin($rad);
    $pos[$time] = array('x' => $x, 'y' => $y);
}

foreach($pos as $key => $val){
    echo $key . " " . $val['x'] . " " . $val['y'] . " " . "\n";
}

2次元ランダムウォークを描け というお題だと解釈しました。
というわけで、Image::Magickで描いてみました。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
use strict;
use warnings;

use List::Util qw/min max/;
use Image::Magick;

# 歩く回数
my $count = 10000;

sub _walk { int(rand(3))-1; }

# 歩いて範囲を測定
my @current = (0,0);
my @area = (@current, @current);
my @footmarks;
for my $fm (map { [_walk, _walk] } 1 .. $count) {
  $current[0] += $fm->[0];
  $current[1] += $fm->[1];
  $area[0] = min $area[0], $current[0];
  $area[1] = min $area[1], $current[1];
  $area[2] = max $area[2], $current[0];
  $area[3] = max $area[3], $current[1];
  push @footmarks, [@current];
}

# 軌跡を書く
my $grid = 8;
my $lsize = 2;
my $img = Image::Magick->new;

$img->Set(
  size=>join(q/x/, map { ($_+1) * $grid } $area[2]-$area[0], $area[3]-$area[1])
);
$img->Read(q/xc:white/);
$img->Draw(primitive => q/polyline/, stroke => q/black/, strokewidth => $lsize,
  points => join(q/ /, map { join q/,/, map { $_ * $grid + $grid/2 } @$_ }
                       map { [$_->[0]-$area[0],$_->[1]-$area[1]] }
                       @footmarks)
);

# start地点にバッテン
$img->Draw(primitive => q/line/, stroke => q/red/, strokewidth => $lsize/2,
  points => join(q/ /, map { join q/,/, map { $_ * $grid } @$_ }
                       map { [$_-$area[0], $_-$area[1]] }
                       0, 1)
);
$img->Draw(primitive => q/line/, stroke => q/red/, strokewidth => $lsize/2,
  points => join(q/ /, map { join q/,/, map { $_ * $grid } @$_ }
                       map { [$_->[0]-$area[0], $_->[1]-$area[1]] }
                       [1,0], [0,1])
);
$img->Write(q/rwalk.png/);

特に指定はないようなので、単位時間に上下左右の四方向に一歩ずつ
進む一番簡単そうなものにしました。

   random_walk 10
 1  0 _1
 2  0  0
 3 _1  0
 4 _1  1
 5 _2  1
 6 _3  1
 7 _2  1
 8 _2  2
 9 _2  3
10 _1  3
1
random_walk=:3 :'(>:i.y),.+/\>(<:?y#4){0 _1;0 1;_1 0;1 0'

BASICで。グラフィック機能が組み込まれた言語の見せ所だと思って、アニメーション。同時にファイルにも書きます。

N88互換BASICで動作を確認。Microsoft BASIC系依存なので(仮称)十進BASICでは動きません。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
100 cls 3
110 x=320 : y=200 : t=0
120 open "c:\RandomWalk.dat" for output as #1
130 print #1, t; x; y
140 *loop
150   circle(x,y),2,0
160   if rnd(1)<0.5 then x=x+1 else x=x-1
170   if rnd(1)<0.5 then y=y+1 else y=y-1
180   t=t+1
190   circle(x,y),2,7
200   print #1, t; x; y
210   if inkey$<>"" then goto *quit
220   goto *loop
230 *quit
240   close #1

まずはシンプルなところで。
   1   0  -1
   2   0   0
   3   0   1
   4  -1   1
   5  -2   1
   6  -1   1
   7   0   1
   8  -1   1
   9  -1   0
  10  -1   1
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <cstdlib>
#include <complex>
#include <iostream>
#include <iomanip>

int main(int args, char* argv[])
{
    std::complex<int> d[] =
    {
        std::complex<int>( 1,  0),
        std::complex<int>( 0,  1),
        std::complex<int>(-1,  0),
        std::complex<int>( 0, -1)
    };

    std::complex<int> p(0, 0);

    for(int i = 1; i <= 10; ++i)
    {
        p += d[std::rand() % 4];
        std::cout << std::setw(4) << i
                  << std::setw(4) << p.real()
                  << std::setw(4) << p.imag()
                  << std::endl;
    }

    return 0;
}

uwsc はマウスとキーボード入力を記録して再生するソフトですが
スクリプト言語を備えています。
これを使ってランダムウォークをやってみました。出力にはwindows 付属の
paint を使いましたが、マウスのドラッグで線が描けるソフトならなんでも
いいと思います。
paint などでマウスのドラッグで線が描ける状態にして、uwsc を起動して
このスクリプトを読み込みます。マウスを開始したい位置に移動して
ALT+F1 で再生、ALT+F2で停止します。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
dim dx[4]=-1,1,0,0
dim dy[4]=0,0,-1,1
t=input("歩数(dot)=",500)
w=input("歩幅(dot)=",5)
x=G_MOUSE_X
y=G_MOUSE_Y
btn(left,down,x,y)
for i=1 to t
  r=random(4)
  x=x+w*dx[r]
  y=y+w*dy[r]
  mmv(x,y,10)
next
btn(left,up)

きっともっと綺麗な書き方があると思うのですが、挑戦の意味を込めて投稿です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import System.Random
import Text.Printf

direction = [(1, 0), (0, 1), (-1, 0), (0, -1)]

printPoint :: (Int, (Int, Int)) -> IO ()
printPoint (n, (x, y)) = printf "%4d%4d%4d¥n" n x y

addPair (x1, y1) (x2, y2) = (x1 + x2, y1 + y2)

main = do
  gen <- getStdGen
  mapM_ printPoint $ take 10 $ zip [1..] $ scanl addPair (0, 0) $ map (\x -> direction !! (mod x 4)) $ randoms gen

座標の表現に複素数を使ってみました。描画はこの出力を gnuplot なり何なりに食わせてやればよいでしょう。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
(use srfi-27)

(define wvec '#(+1 -1 +i -i))

(define (main args)
  (let ((n (string->number (cadr args)))
        (l (vector-length wvec)))
    (random-source-randomize! default-random-source)
    (let random-walk ((m 0)
                      (pos 0))
      (when (< m n)
        (format #t "~A ~A ~A~%" m (real-part pos) (imag-part pos))
        (random-walk (+ m 1) (+ pos (ref wvec (random-integer l))))))
    0))

適当に。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
from random import uniform
from itertools import islice
from math import sin,cos,pi

def randomwalk(x = 0,y = 0):
    while True:
        rad = uniform(0,2*pi)
        x, y = x + cos(rad), y + sin(rad)
        yield (x,y)

def main():
    for i,(x,y) in enumerate(islice(randomwalk(),10)):
        print i,x,y

動作には直接影響しませんが、いくつか訂正します。
パラメータ入力の文字列の部分が変でした。

t=input("歩数(dot)=",500)

歩数の単位は歩幅なので (dot) は間違いでした。
パラメータの入力の順番は歩幅を先にした方が良いのかな。
配列に初期値を設定する場合は、要素数は省略可能でした。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
dim dx[]=-1,1,0,0
dim dy[]=0,0,-1,1
w=input("歩幅(dot)",5)
t=input("歩数",500)
x=G_MOUSE_X
y=G_MOUSE_Y
btn(left,down,x,y)
for i=1 to t
  r=random(4)
  x=x+w*dx[r]
  y=y+w*dy[r]
  mmv(x,y,10)
next
btn(left,up)

Processing は可視化が容易です。とりあえずシンプル版。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
int x, y, t;

int d = 1;

void setup() {
  x = width / 2;
  y = height / 2;
  t = 0;
//  randomSeed(0);
}

void draw() {
  t += 1;
  float r = random(1);
  if (r < 0.25) {
    x += d;
  } else if (r < 0.5) {
    x -= d;
  } else if (r < 0.75) {
    y += d;
  } else {
    y -= d;
  }
  point(x, y);
  println(t + " " + x + " " + y);
}

複数の点を扱いアニメーションにした版。 点を増やしていくとランダムウォークと拡散が関係していそう、というのが分かりますね。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
int n = 1000;
int t;
Particle[] particles = new Particle[n];

int hsb = 16;

void setup() {
  colorMode(HSB, hsb);
  size(200,200);

  smooth();
  for (int i=0; i < n; i++) {
    Particle p = new Particle(width/2, height/2, 1);
    particles[i] = p;
  }
}

void draw() {
  t += 1;
  float xm = 0;
  float ym = 0;
  for (int i=0; i < n; i++) {
    Particle p = particles[i];
    p.move();
    xm += p.x;
    ym += p.y;
    p.render();
  }

  println(t + " " + xm/n + " " + ym/n); 
  
  rectMode(CORNER);
  fill(hsb, hsb * 0.25);
  rect(0, 0, width, height);
}

class Particle {
  int x;
  int y;
  int d;
  
  Particle(int x_, int y_, int d_) {
    x = x_;
    y = y_;
    d = d_;
  }
  
  void move() {
    float r = random(1);
    if (r < 0.25) {
       x += d;
    } else if (r < 0.5) {
      x -= d;
    } else if (r < 0.75) {
      y += d;
    } else {
      y -= d;
    }
  }
  
  void render() {
    point(x, y);
  }
}

Squeak Smalltalk で。

1
2
3
4
5
6
7
8
| pen origin out time |
pen := Pen new defaultNib: 1; color: Color red; place: (origin := Display center).
out := FileStream fileNamed: 'out.txt'.
time := 0.
[(time := time + 1) < 1e3] whileTrue: [
    out nextPutAll: time printString; tab; nextPutAll: (pen location - origin) printString; cr.
    pen turn: 360 atRandom; go: 5].
out edit

大変おもしろいと思いました。:-)


コマンドライン引数で次元数とログファイルのパスを指定するようにしました。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
using System;
using System.Collections.Generic;
using System.IO;

class Program {
    static void Main(string[] args) {
        RandomWalk rndwalk = new RandomWalk(int.Parse(args[0]));

        while(true) {
            if(Console.ReadLine() != "end") {
                foreach(int i in rndwalk.Tic()) {
                    Console.Write(i.ToString() + " ");
                }
                Console.WriteLine();
            } else {
                break;
            }
        }

        rndwalk.WriteLog(Environment.CurrentDirectory + "\\" + args[1]);

        using(StreamReader sr = new StreamReader(Environment.CurrentDirectory + "\\" + args[1])) {
            while(!sr.EndOfStream) {
                Console.WriteLine(sr.ReadLine());
            }
        }
        Console.ReadLine();
    }
}

class RandomWalk {
    int rank;
    List<int[]> log = new List<int[]>();
    Random rnd = new Random();
    int[] direction = new int[] { -1, 1 };

    public RandomWalk(int rank) {
        this.rank = rank;
        log.Add(new int[rank]);
    }

    public int[] Tic() {
        int changeRank = rnd.Next(0, rank);
        int[] pre = log[log.Count - 1];
        int[] ticedPoint = new int[rank];

        for(int i = 0; i < rank; i++) {
            if(i == changeRank) {
                ticedPoint[i] = pre[i] + direction[rnd.Next(0, 2)];
            } else {
                ticedPoint[i] = pre[i];
            }
        }

        log.Add(ticedPoint);

        return log[log.Count - 1];
    }

    public void WriteLog(string fileName) {
        using(StreamWriter sw = new StreamWriter(fileName)) {
            for(int time = 0; time < log.Count; time++) {
                int[] point = log[time];
                sw.Write(time + " ");
                foreach(int i in point) {
                    sw.Write(i.ToString() + " ");
                }
                sw.WriteLine();
            }
            sw.Close();
        }
    }
}

貧相ですが,画面上に表示する様にしてみました。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
require 'vr/vruby'
require 'vr/vrcontrol'

class RandomWalk
    attr_accessor :x, :y, :trace
    
    def self.next(current)
        rand(2) == 0 ? (current - 1) : (current + 1)
    end
    
    def initialize
        self.trace = []
        self.x = 0
        self.y = 0
    end
    
    def walk(step=10000)
        self.trace = (1..step).map { |_| move }
    end
    
private
    def move
        [:x,:y].map { |p| send("#{p.to_s}=".intern, self.class.next(send(p))) }
    end
end

class RandomWalkPlotter <VRForm
    
    include VRDrawable
    
    WIDTH    = 300
    STEP    = 10000
    
    attr_accessor :scale
    
    def construct
        random_walk
        addControl VRButton, "btn_walk", "walk", 10, 310, 40, 30
        addControl VRButton, "btn_save", "save", 60, 310, 40, 30
        addControl VRButton, "btn_quit", "quit", 240, 310, 40, 30
        move 100, 100, 300, 385
    end
    
    def btn_walk_clicked
        random_walk
        refresh
    end
    
    def btn_save_clicked
        if (file = SWin::CommonDialog::saveFilename(self))
            File.open(file,"w") { |f| (1..trace.size).zip(trace).each { |d| f.puts(d.flatten.join(",")) } }
        end
    end
    
    def btn_quit_clicked
        close
    end
    
    def self_paint
        setPen RGB(0, 0, 0)
        x, y = WIDTH / 2, WIDTH / 2
        trace.each do |p|
            l = [(p[0] * scale).to_i + WIDTH / 2,WIDTH / 2 - (p[1] * scale).to_i]
            drawLine x , y , *l
            x, y = *l
        end
    end
    
    def random_walk
        random_walk = RandomWalk.new
        random_walk.walk STEP
        self.trace = random_walk.trace
    end
    
    def trace=(trace)
        @trace = trace
        self.scale = WIDTH.to_f / (trace.flatten.inject(0) { |m,p| (m < p.abs) ? p.abs : m } * 2)
    end
    
    def trace
        @trace
    end
end

VRLocalScreen.start RandomWalkPlotter

出力はSVGにしてみました。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private import std.stdio, std.math, std.random;

void main() {
    write(`<?xml version="1.0"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="600" height="600">
<title>ランダムウォーク</title>
<g transform="translate(300,300)">
<circle style="fill:blue" cx="0" cy="0" r="5"/>
<path style="fill:none; stroke:black; stroke-width:1" d="M 0 0`);

    auto rgen = Random(unpredictableSeed);
    auto uniform = UniformDistribution!(real)(0, PI * 2);
    foreach(_; 0 .. 10000) {
        auto angle = uniform.next(rgen);
        auto move = expi(angle) * 10;
        writef(` l %.2f %.2f`, move.re, move.im);
    }

    write(`"/>
</g>
</svg>`);
}

なぜかRで 1-1と2-1は解いてません 本職の人はどう書くんでしょう?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
t <- 1000
#1次元
plot(cumsum(c(0,2*floor(2*runif(t-1))-1)),type="l")
#2次元
x <- 0
y <- 0
f.x <- function(t){switch(t,1,-1,0,0)}
f.y <- function(t){switch(t,0,0,1,-1)}
w <- floor(4*runif(t)+1)
move.x <- unlist(lapply(as.list(w),f.x))
move.y <- unlist(lapply(as.list(w),f.y))
x <- x + cumsum(move.x)
y <- y + cumsum(move.y)
plot(x,y,type="b") 
#3次元
library(scatterplot3d)
z <- 1:t
scatterplot3d(x,y,z,pch=20)

本職ではありませんが・・・

simecolというライブラリーに、ランダムウォークを実装するためのフレームワークが用意されています。

もちろんアニメーションもします。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
library(simecol)

size <- 50
obj <- rwalkModel(
  parms = list(size=size, area=c(0, size, 0, size)),
  init  = data.frame(time=0, x=size/2, y=size/2),
  times = c(from=0, to=100, by=1),
  main  = function(time, init, parms) {
    r <- 2 * pi * runif(1)
    data.frame(time=time, x=((init$x + cos(r)) %% parms$size), 
               y=((init$y + sin(r)) %% parms$size))
  }
)
sim(obj, animate=T)

GUIで作ってみました。
画像が置けないので、画面イメージはBLOGに貼っています。
"Run"ボタンを押すたび100回ずつ動きます。
"Groovy in Action"を参考にしました。

実行結果
-------------------------
以下のURLに画面イメージ貼り付け。
http://d.hatena.ne.jp/nemo_kaz/20080730



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
import groovy.swing.SwingBuilder
import java.awt.Color
import java.awt.BorderLayout as BL
import javax.swing.WindowConstants as WC
import javax.swing.BorderFactory as BF
import javax.swing.JOptionPane
swing = new SwingBuilder()
paint = swing.action(
    name: 'Run',
    closure: this.&paintGraph,
    mnemonic: 'R',
    accelerator: 'ctrl R'
)
about = swing.action(
    name: 'About',
    closure: this.&showAbout,
    mnemonic: 'A',
    accelerator: 'F1'
)
frame = swing.frame(title:'2次元ランダムウォーク',
location:[100,100], size:[300,300],
defaultCloseOperation:WC.EXIT_ON_CLOSE) {
    menuBar (){
        menu(mnemonic:'A','Action'){
        menuItem(action:paint)
        }
        glue()
        menu(mnemonic:'H','Help'){
            menuItem(action:about)
        }
    }
    panel (border:BF.createEmptyBorder(6,6,6,6)) {
        borderLayout()
    
        vbox(constraints: BL.CENTER,border:BF.createTitledBorder('Runボタンを押すたびに100回動きます')) {
            panel(id:'canvas')
        }

        hbox (constraints: BL.SOUTH){
            hstrut(width:10)
            button(action:paint)
        }
    }
}

frame.show()

def labeledSpinner(label, value){
    swing.label(label)
    swing.hstrut()
    swing.spinner(id:label, stateChanged:this.&paintGraph,
    model:swing.spinnerNumberModel(value:value))
}
    gfx = swing.canvas.graphics
    gfx.color = new Color(255, 255, 150)
    gfx.color = Color.red
    xpos = 100
    ypos = 100
def paintGraph(event) {

    int width = swing.canvas.size.width
    int height = swing.canvas.size.height
    
    1.upto(100) {
        xdir = Math.random()
        ydir = Math.random()
        if (xdir <0.3333 &&  xpos >5)         { xpos-=5 }
        if (xdir >0.6666 && (xpos < width-5)) { xpos+=5 }
        if (ydir <0.3333 &&  ypos >5)         { ypos-=5 }
        if (ydir >0.6666 && (ypos <height-5)) { ypos+=5 }
        gfx.fillRect(xpos,ypos,4,4)
    }
}
void showAbout(event) {
    JOptionPane.showMessageDialog(frame,
    '''2次元ランダムウォーク
赤い四角がランダムに歩きます。''')
}

Java がまだ投稿されていなかったので、Swingで表示するものを作ってみました。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;

import javax.swing.AbstractAction;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;

public class RandomWalk extends JFrame {
    private static final int WIDTH = 640;
    private static final int HEIGHT = 480;
    private static final Dimension PLOT_SIZE = new Dimension(WIDTH, HEIGHT);

    private final JTextField countField_ = new JTextField(8);
    private final JButton startButton_ = new JButton(new AbstractAction("開始") {
        public void actionPerformed(ActionEvent e) { start(); }
    });
    private final JButton stopButton_ = new JButton(new AbstractAction("停止") {
        public void actionPerformed(ActionEvent e) { stop(); }
    });
    private final JButton endButton_ = new JButton(new AbstractAction("終了") {
        public void actionPerformed(ActionEvent e) { dispose(); }
    });

    private final Random random_ = new Random();

    private volatile boolean walking_ = false;
    private final List<Point> points_ = new ArrayList<Point>();

    public RandomWalk() {
        super("2次元ランダムウォーク");
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setResizable(false);

        configure();

        Box main = Box.createVerticalBox();
        main.add(createOperationArea());
        main.add(createPlotArea());
        this.add(main);

        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            public void run() {
                repaint();
            };
        }, 0, 100L);
    }
    private void configure() {
        countField_.setText("1000");

        startButton_.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
            }
        });
        stopButton_.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
            }
        });
    }

    private JComponent createPlotArea() {
        plotArea_.setBackground(Color.WHITE);
        plotArea_.setPreferredSize(PLOT_SIZE);
        return plotArea_;
    }
    private JComponent createOperationArea() {
        JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
        panel.add(new JLabel("N="));
        panel.add(countField_);
        panel.add(Box.createHorizontalGlue());
        panel.add(startButton_);
        panel.add(stopButton_);
        panel.add(endButton_);
        return panel;
    }


    public void start() {
        initialize();
        walking_ = true;
    }
    public void stop() {
        walking_ = false;
    }


    private void initialize() {
        int count = 1;
        try {
            count = Integer.parseInt(countField_.getText());
        } catch (NumberFormatException e) {
        }
        points_.clear();
        points_.add(new Point(WIDTH / 2, HEIGHT / 2));
        for (int index = 1; index < count; index++) {
            points_.add(new Point(random_.nextInt(WIDTH), random_.nextInt(HEIGHT)));
        }
    }

    private final JPanel plotArea_ = new JPanel() {
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);

            if (walking_) {
                for (Point p: points_) {
                    nextStep(p);
                    g.drawRect(p.x, p.y, 1, 1);
                }
            }
        }
        private void nextStep(Point p) {
            switch (random_.nextInt(4)) {
                case 0:
                    p.x--;
                    break;
                case 1:
                    p.x++;
                    break;
                case 2:
                    p.y--;
                    break;
                case 3:
                    p.y++;
                    break;
            }
        }
    };


    public static void main(String[] args) {
        RandomWalk frame = new RandomWalk();
        frame.pack();
        frame.setVisible(true);
    }
}

2次元も1次元と同じような書き方でいいと思いますが。
1
2
w <- floor(4*runif(t)+1)
plot(cumsum(c(1,-1,0,0)[w]),cumsum(c(0,0,1,-1)[w]),type="b")

もっといえば、1~4の乱数が欲しいようなときはrunif()ではなくsample()を使った方がRっぽいかもです。

1
plot(cumsum(data.frame(x=c(1,-1,