百日半狂乱

Shut the fuck up and write some code!!

awkで0~1の乱数を生成する

シェルスクリプトを書いていて、ふと0~1の乱数をお手軽に生成したくなったので、実験結果をメモ.

awkには組み込みでrand()という関数が用意されていて、例えば以下のような形で呼び出すたびに0~1の範囲の乱数を生成してくれる.

awk 'BEGIN{ srand('"$RANDOM"'); print rand() }'

ここでは環境変数RANDOMを組み込み関数srand()のseedとしている.組み込み関数についてはman awkを叩く.

筋が良いのか悪いのかわからないが、とりあえず上記のような結論に至ったのは、以下のサンプルコードの実行結果による.

awkで乱数生成

実行結果は以下の通り.

$ bash awk_rand.bash 
---> sleep 0
0.104709
0.104709
0.104709
0.104709
0.104709
0.104709
0.104709
0.104709
0.104709
0.104709
---> sleep 0.5
0.104709
0.104709
0.236247
0.236247
0.367785
0.367785
0.499323
0.499323
0.630861
0.762398
---> sleep 1.0
0.762398
0.893936
0.025474
0.157012
0.28855
0.420087
0.551625
0.683163
0.814701
0.946238
---> srand($RANDOM)
0.73843
0.445421
0.237373
0.537946
0.900747
0.589343
0.0254097
0.139673
0.721284
0.539107

srand()にseedを設定しないままfor文で回すと出力される乱数が変化しなかった.

上記の通り、またman awkにも書いてある通り、srandは時間をseedにしているので、乱数は1秒単位でしか変化しない.

というわけで、とりあえず環境変数RANDOMをseedとして、for文で回しても生成される乱数が毎回変化するようにした.

ところで、この方法で生成される乱数の精度はどの程度か気になる.

が、厳密な乱数精度の評価方法をすぐに思い出すことも、思いつくこともなかった.

ここでは視覚的にバラバラの値が出力されているっぽいことを確認してお茶を濁しておく.

#!/bin/bash

plotfile="awk_rand.plot"

[ -e $plotfile ] && rm $plotfile

for loop in `seq 10000`
do 
    output=`awk 'BEGIN{ srand('"$RANDOM"'); print rand() }'`
    echo "$loop $output" >> $plotfile
done 

gnuplot <<- EOF
  set terminal png    
  set output "awk_rand.png"
  plot "$plotfile"
EOF

見た目はこんな感じ.

f:id:doi-t:20140619023654p:plain

相加平均

$ awk 'BEGIN{ sum=0 } { sum+=$2  } END{ print sum/NR }' awk_rand.plot
0.50121

標本分散

$ awk 'BEGIN{ sum=0; ave=0.50121 } { sum+=($2-ave)^2  } END{ print sum/NR }' awk_rand.plot 
0.0846204

標準偏差

$ awk 'BEGIN{ sum=0; ave=0.50121 } { sum+=($2-ave)^2  } END{ print sqrt(sum/NR) }' awk_rand.plot
0.290896

今日も今日とてawk先生のお世話になった.