2018年1月3日水曜日

[Python] 整数の乱数を発生させる

Pythonにおいて,整数の乱数を発生させるプログラムを書く際のメモです.

整数の乱数なので,1〜6の目のついたサイコロを振ることを例に考えます.
まずは,NumPyを呼び出します.
>>> import numpy as np

20回サイコロを振る(整数の乱数を発生させる)ことを想定して,乱数を発生させるには,以下のようにします.
>>> np.random.choice(np.arange(1, 7), 20)
array([4, 2, 5, 1, 6, 5, 5, 3, 1, 5, 4, 4, 3, 4, 1, 5, 2, 1, 2, 2])
関数"np.random.chice()"は,ランダムな要素を取り出す関数です.

np.random.chice(sample, times)

上記の例では,sample = np.arange(), times = 20 としています.

関数"np,arrange()"は公差(間隔)を指定した連番を発生させる関数です.
()内は (start, stop)を意味しており,発生させる整数の区間を指定しています.

np.arange(start, stop)

上記の例のように(1, 7)とした場合は,発生させる乱数$r$は$1 < r \leq 7$となります.

ちなみに,以下のようにarange(start, stop, step) と指定すると,startから始まって,stopまでの,stepの値を公差とする等差数列を生成します.
>>> np.arange(1, 20, 3)
array([ 1,  4,  7, 10, 13, 16, 19])
"step"を指定しない場合は step = 1 となるので,サイコロのように公差が1の場合は指定は要りません.

"np.random.choice(np.arange(1, 7), 20)"では,まず"np.arange(1, 7)"でstart = 1, stop = 7の整数を発生させて,そこから,20個の要素をランダムに抽出する以下のような処理を行っています.

1. サイコロの目をdiceという1次元配列に格納する.
>>> dice = np.arange(1, 7, 1)
>>> dice
array([1, 2, 3, 4, 5, 6])
2. diceに格納された1〜6の目からランダムに要素を抜き出す.
>>> np.random.choice(dice, 20)
array([6, 4, 2, 6, 4, 3, 4, 3, 1, 2, 1, 4, 1, 1, 3, 4, 4, 2, 1, 3])

結果は1次元の配列に格納されています.

この例では,同じ要素を何度も取り出すことが可能な復元抽出になっていることがわかります.
choiseコマンドは,デフォルトでは復元抽出となるので,上記の例では記載していませんが,復元抽出を行うことを指定する場合は以下のようにします.
>>> np.random.choice(np.arange(1, 7), 20, replace = True)
array([5, 6, 4, 5, 3, 6, 3, 1, 4, 4, 6, 2, 1, 2, 4, 4, 5, 4, 2, 3])

一方で,非復元抽出を行う場合は,以下のようにします.
>>> np.random.choice(np.arange(1, 7), 6, replace = False)
array([5, 3, 4, 6, 1, 2])
ここで,非復元抽出なので,乱数を発生させる回数を6としています.
同じ目が出ていないことが確認できます.

試しに,元の20にしてみると,当然,以下のようにエラーとなります.
>>> np.random.choice(np.arange(1, 7), 20,replace=False)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "mtrand.pyx", line 1166, in mtrand.RandomState.choice
ValueError: Cannot take a larger sample than population when 'replace=False'

ここまでのサンプリングでは,標本は等確率でサンプリングされます.
等間隔でない分布からサンプリングを行うことを表現するには,"pオプション"を使用します.

>>> p = np.array([1, 1, 2, 2, 3, 3]); p = p / np.sum(p)
>>> np.random.choice(np.arange(1, 7), 12, p = p, replace = True)
array([5, 5, 4, 4, 4, 3, 5, 5, 1, 6, 1, 3])
pオプションでは,1〜6の目の出やすさを 1: 1: 2: 2: 3: 3 の比率に設定しています.また,pオプションに渡す配列は確率なので,総和が1である必要があります.なので,pを"np.sum(p)"で割ることで規格化しています.
すなわち,1と2の目がでる確率は 1/12,3と4の目がでる確率は1/6(= 2/12) ,5と6の目がでる確率は 1/4(=3/12)となります.
生成された値を見ると,1が1/12回,2が0/12回,3が2/12回,4が3/12回,5が4/12回,6が1/12回となっています.


0 件のコメント :

コメントを投稿