labunix's blog

labunixのラボUnix

Windowsでランダムを得るならば、powershellを使おう?。。。ただし100万未満なら。

■Windowsでランダムを得るならば、powershellを使おう?。。。

■まず、DOSのランダムはダサいです。
 一見、一回づつだと正常に見えますが。。。

> set /a RANDVAL=%RANDOM% % 100
74
> set /a RANDVAL=%RANDOM% % 100
11

■以下のように実行すると、全く同じ結果が続きます。
 ※RANDOMを取り直していません。

> FOR /L %i IN (1,1,100) DO set /a RANDVAL=%RANDOM% % 100
> set /a RANDVAL=1833 % 100
33
> set /a RANDVAL=1833 % 100
33
> set /a RANDVAL=1833 % 100
33
...

■MSも上記の問題は認識していたらしく、powershellでは「next」で
 異なった結果を簡単に得られるようになっています。

 VBScript の Rnd 関数の変換
 http://technet.microsoft.com/ja-jp/library/ee176946.aspx

> powershell
PS> $a = new-object random
PS> $b = $a.next(1,100); echo $b;$b = $a.next(1,100); echo $b
89
24

PS> $a = new-object random;for($i = 0;$i -lt 100;$i++){$b = $a.next(1,100); echo $b >> args_ps}

■上記を先日のスクリプトでチェック。
 たった100のランダムを取得するだけで恐ろしいほどに遅いのですが、その辺はスルーで。。。
 バッチのあの気持ち悪いループ構文がいくらかましになっただけ、良しとします。

 USP友の会会長が出題した前半戦を解いてみた。
 http://d.hatena.ne.jp/labunix/20121111

$ nkf -Lu args_ps > args;wc -l args
100 args
$  awk '{print ($1-($1%10))/10}' args  | sort -n | uniq -c | awk '{printf "%3d~%3d  %3d\n",($2*10),($2*10)+9,$1}'
  0~  9    9
 10~ 19    8
 20~ 29    8
 30~ 39   19
 40~ 49    7
 50~ 59   12
 60~ 69   12
 70~ 79    8
 80~ 89   10
 90~ 99    7

■大量に実行すると、遅さが目立ちます。
 結果が出るだけまだマシですが、軽く1時間を超えるという驚愕の遅さです。
 「少なくとも大量の乱数を得る処理はWindowsには無理です。」と素直に諦めましょう。

PS> $a = new-object random;for($i = 0;$i -lt 1000;$i++){$b = $a.next(1,100); echo $b >> args_1000}
PS> $a = new-object random;for($i = 0;$i -lt 10000;$i++){$b = $a.next(1,100); echo $b >> args_10000}
PS> $a = new-object random;for($i = 0;$i -lt 100000;$i++){$b = $a.next(1,100); echo $b >> args_100000}
PS> get-date;$a = new-object random;for($i = 0;$i -lt 1000000;$i++){$b = $a.next(1,100); echo $b >> args_1000000};get-date

2012年11月12日 22:57:15
2012年11月13日 1:07:34

■ちなみにこのWindows(32bit)に乗る仮想マシン(vmplayer)上の256MBを割り当てたlinuxで実行すると。。。
 まあ、確かにしょぼいスペックですけど、vmwre-toolsも入れていないゲストOSにこれだけ差をつけられます。
 ちなみに別の64bitのLinuxでは約30秒で処理は完了しますが、スペックの問題では無いのは明らかです。
 えっと。。。窓から投げ捨ててもいいですか?Windows先生w。。。

$ time for n in `seq 1 1000000`;do echo $*1;done > args

real    3m46.452s
user    2m51.479
sys     0m54.239s

■CPUもメモリもほとんど使用していないので、
 純粋にロジックが問題なのでしょう(致命的)w。。。
 まあ、結果が大事なので、なんとか気を取り直して精度を見てみます。

$ wc -l args_1000;nkf -Lu args_1000 > args
1000 args_1000
$ awk '{print ($1-($1%10))/10}' args  | sort -n | uniq -c | awk '{printf "%3d~%3d  %3f\n",($2*10),($2*10)+9,$1/10.0}'
  0~  9  9.300000
 10~ 19  8.800000
 20~ 29  10.700000
 30~ 39  8.700000
 40~ 49  10.700000
 50~ 59  12.100000
 60~ 69  9.800000
 70~ 79  9.200000
 80~ 89  11.400000
 90~ 99  9.300000


$ wc -l args_10000; nkf -Lu args_10000 > args
10000 args_10000
$ awk '{print ($1-($1%10))/10}' args  | sort -n | uniq -c | awk '{printf "%3d~%3d  %3f\n",($2*10),($2*10)+9,$1/100.0}'
  0~  9  8.370000
 10~ 19  10.230000
 20~ 29  10.570000
 30~ 39  10.240000
 40~ 49  10.060000
 50~ 59  10.580000
 60~ 69  10.120000
 70~ 79  10.090000
 80~ 89  9.870000
 90~ 99  9.870000


$ wc -l args_100000; nkf -Lu args_100000 > args
100000 args_100000

$  awk '{print ($1-($1%10))/10}' args  | sort -n | uniq -c | awk '{printf "%3d~%3d  %3f\n",($2*10),($2*10)+9,$1/1000.0}'
  0~  9  9.039000
 10~ 19  10.145000
 20~ 29  10.136000
 30~ 39  10.358000
 40~ 49  10.145000
 50~ 59  10.042000
 60~ 69  10.189000
 70~ 79  10.023000
 80~ 89  9.917000
 90~ 99  10.006000

■なぜか407も無駄にループしたようですが。。。

$ wc -l args_1000000
1000407 args_1000000
$ nkf -Lu args_1000000 > args
$ awk '{print ($1-($1%10))/10}' args  | sort -n | uniq -c | awk '{printf "%3d~%3d  %3f\n",($2*10),($2*10)+9,$1/10000.0}'
  0~  9  9.113000
 10~ 19  10.153800
 20~ 29  10.077100
 30~ 39  10.099300
 40~ 49  10.120400
 50~ 59  10.061200
 60~ 69  10.095400
 70~ 79  10.113600
 80~ 89  10.093600
 90~ 99  10.113300

■精度としては百万未満であれば問題なさそうです。

*1:$RANDOM % 100