matlabでランダムドットを書く方法についての段階的考察

matlabを用いてランダムドットを書く、ということを研究でやっていく途中で色々考える必要に迫られたので、目的に応じたものを書き記そうと思う。

matlabで食ってる人はこんな記事読まないだろうし、初心者向けってことで書いています。

 

matlab ランダムドット」でググって来た人で「結局どうやってプログラム書けばいいのかだけ知りたい」って人は一番下側にある3行だけ見ればおk 

 

 

 

 

 

 

 

例題「2×5で作られたエリアの30%を黒で塗ったランダムドットを作る」

まず思いつくのは各配列要素に対して30%の確率で黒情報を入力するfor文を書いてやること。

 

① 

img=zeros(2,5);   %2*5の真っ白な配列を用意
for i = 1:2;
 for j = 1:5;
  if ( rand < 0.3 )   %乱数が0~1の間で生成され、それが0.3未満なら以下を実行
   img(i,j) = 1;
  end
 end
end

 

これでimgの約30%が黒なランダムドットが生成されることになる。

しかしこの方法だと、乱数の出方によっては、10マスのうちピッタリ3マスだけ塗り替えられるという保証は無い。

 

なので最初から塗り替えるマスの数を指定しておく手法を考える。

 

img=zeros(2,5);
numDots = round(2*5*0.3);   %roundを使えばnumDotsが整数に丸められる

while (numDots > 0)
 i = randi(2);
 j = randi(5);   %これらによってiとjにそれぞれ1~2、1~5の間のランダムな整数を入れる
 if (img(i,j) == 0)
  img(i,j) = 1;
  numDots = numDots -1;
 end
end

 

上記の方法では、「適当に選んだ場所の色が白だったら黒に塗り替える」を予め塗り替えると決めた量だけ行う。これによって確実に指定の黒密度のランダムドットが書ける。

 

上のプログラムの致命的な弱点としては、「既に黒だったならもう一度新しい座標を探す」という試行が何度もダブるせいでありえんほど時間がかかるということ。

50%以上が黒であるランダムドットを作りたいなら、むしろ最初から全部黒で塗っといて、あとから白で塗り直すようにしたほうが早くなる場合も出てくる。

 

次に、さらに高速化を狙った方法を記す。

 

img=zeros(2,5);
numDots = round(2*5*0.3);
for i=1:2;
 for j=1:5;
  if( numDots > 0 )
   img(i,j)=1;
   numDots=numDots-1;
  end
 end
end   %ここまでで30%が黒の配列を作っちゃう

turn=10000000;
while( turn > 0 )
 i1 = randi(2);
 j 1= randi(5);
 i 2= randi(2);
 j 2= randi(5);
 z=img(i1,j1);
 img(i1,j1)=img(i2,j2);
 img(i2,j2)=z;
 turn=turn-1;
end

 

こんな感じで「最初の30%を黒く塗ってあとランダムに掴んでとりかえる」といった方法。もちろんランダムにひっくり返すだけなので、試行数が少ないと全然ランダムドットにならない。

この方法の欠点としては、「何回ループすれば『ランダム』ドットであるかを主張できるかわからない」ということがあげられる。

ちなみに、まだ試していないがwhileのとこから以下のように書き換えたら多少シャッフルが綺麗に実行されると思うし時間の短縮になると考えられる。

 

for i =1:2;
 for j=1:5;
  i1 = randi(2);
  j1 = randi(5);
  z=img(i,j);
  img(i,j)=img(i1,j1);
  img(i1,j1)=z;
 end
end

 

片方の手で順番に掴み、もう片方の手はランダムに掴む。

これなら全マスに入れ替えのチャンスがあるのでランダムな配列を作ったと言えるのではないか

 

最後に最適解としてrandpermという便利関数を用いる方法を紹介する。

これは例えば

 

A=randperm(5,3)

 

とすると、Aの中に[4 2 5]のように1~5の間から適当なが3つダブらず割り振られる関数だ。

これを利用して次のように書く

 

④ 

img=zeros(2,5);
A=randperm(2*5,3);
img(A)=1;

 

このときAは1*3の行列になる。imgは2*5の行列のはずだが、行列の要素ごとに番号が割り振られていて

img= [① ③ ⑤ ⑦ ⑨
          ② ④ ⑥ ⑧ ⑩]

みたいな順の1*10行列としてmatlabが勝手に扱ってくれるそうなので、img(A)とすれば30%分の要素だけダブらずに抽出できるらしい。

 

ベクトルと行列の違いがいまいちわからないので雑な説明になってしまったが、こんな感じで一定割合のランダムドットは生成できるはず。