9.6. random — 擬似乱数を生成する

ソースコード: Lib/random.py


このモジュールでは様々な分布をもつ擬似乱数生成器を実装しています。

整数用に、ある範囲からの一様な選択があります。シーケンス用には、シーケンスからのランダムな要素の一様な選択、リストのランダムな置換をインプレースに生成する関数、順列を置換せずにランダムサンプリングする関数があります。

実数用としては、一様分布、正規分布 (ガウス分布)、対数正規分布、負の指数分布、ガンマおよびベータ分布を計算する関数があります。角度の分布を生成するにはフォン・ミーゼス分布が利用できます。

ほとんど全てのモジュール関数は、基礎となる関数 random() に依存します。この関数はランダムな浮動小数点数を半開区間 [0.0, 1.0) 内に一様に生成します。Python は中心となる乱数生成器としてメルセンヌツイスタを使います。これは 53 ビット精度の浮動小数点を生成し、周期は 2**19937-1 です。本体は C で実装されていて、高速でスレッドセーフです。メルセンヌツイスタは、現存する中で最も広範囲にテストされた乱数生成器のひとつです。しかしながら、メルセンヌツイスタは完全に決定論的であるため、全ての目的に合致しているわけではなく、暗号化の目的には全く向いていません。

このモジュールで提供されている関数は、実際には random.Random クラスの隠蔽されたインスタンスのメソッドに束縛されています。内部状態を共有しない生成器を取得するため、自分で Random のインスタンスを生成することができます。

自分で考案した基本乱数生成器を使いたい場合、クラス Random をサブクラス化することもできます。この場合、メソッド random()seed()getstate()setstate() をオーバライドしてください。オプションとして、新しいジェネレータは getrandbits() メソッドを提供することができます。これにより randrange() メソッドが任意に大きな範囲から選択を行えるようになります。

random モジュールは SystemRandom クラスも提供していて、このクラスは OS が提供している乱数発生源を利用して乱数を生成するシステム関数 os.urandom() を使うものです。

警告

The pseudo-random generators of this module should not be used for security purposes.

Bookkeeping functions:

random.seed(a=None, version=2)

乱数生成器を初期化します。

a が省略されるか None の場合、現在のシステム時刻が使用されます。乱数のソースがオペレーティングシステムによって提供される場合、システム時刻の代わりにそれが使用されます (利用可能性についての詳細は os.urandom() 関数を参照)。

a が int の場合、それが直接使われます。

バージョン2 (デフォルト) では、 str, bytes, bytearray オブジェクトは int に変換され、そのビットがすべて使用されます。

バージョン1 (Python の古いバージョンでのランダムなシーケンスを再現するために提供される) では、 strbytes に対して適用されるアルゴリズムは、より狭い範囲のシードを生成します。

バージョン 3.2 で変更: 文字列シードのすべてのビットを使うバージョン2スキームに移行。

random.getstate()

乱数生成器の現在の内部状態を記憶したオブジェクトを返します。このオブジェクトを setstate() に渡して内部状態を復元することができます。

random.setstate(state)

state は予め getstate() を呼び出して得ておかなくてはなりません。 setstate()getstate() が呼び出された時の乱数生成器の内部状態を復元します。

random.getrandbits(k)

k 桁の乱数ビットで Python の整数を生成し、返します。このメソッドはメルセンヌツイスタ生成器で提供されており、その他の乱数生成器でもオプションの API として提供されている場合があります。randrange() メソッドを使用できる場合、getrandbits() はそのメソッドを有効にし、任意の大きな範囲を扱えるようになります。

Functions for integers:

random.randrange(stop)
random.randrange(start, stop[, step])

range(start, stop, step) の要素からランダムに選ばれた要素を返します。この関数は choice(range(start, stop, step)) と等価ですが、実際には range オブジェクトを生成しません。

位置引数のパターンは range() のそれと一致します。キーワード引数は、この関数に望まれない方法で使われるかもしれないので、使うべきではありません。

バージョン 3.2 で変更: 一様に分布した値の生成に関して randrange() がより洗練されました。以前は int(random()*n) のようなやや一様でない分布を生成するスタイルを使用していました。

random.randint(a, b)

a <= N <= b であるようなランダムな整数 N を返します。randrange(a, b+1) のエイリアスです。

Functions for sequences:

random.choice(seq)

空でないシーケンス seq からランダムに要素を返します。 seq が空のときは、 IndexError が送出されます。

random.shuffle(x[, random])

Shuffle the sequence x in place. The optional argument random is a 0-argument function returning a random float in [0.0, 1.0); by default, this is the function random().

Note that for even rather small len(x), the total number of permutations of x is larger than the period of most random number generators; this implies that most permutations of a long sequence can never be generated.

random.sample(population, k)

母集団のシーケンスまたは集合から選ばれた長さ k の一意な要素からなるリストを返します。重複無しのランダムサンプリングに用いられます。

母集団自体を変更せずに、母集団内の要素を含む新たなリストを返します。返されたリストは選択された順に並んでいるので、このリストの部分スライスもランダムなサンプルになります。これにより、くじの当選者 (サンプル) を1等賞と2等賞(の部分スライス)に分けることも可能です。

母集団の要素は ハッシュ可能 でなくても、ユニークでなくてもかまいません。母集団が繰り返しを含む場合、出現するそれぞれがサンプルに選択されえます。

To choose a sample from a range of integers, use an range() object as an argument. This is especially fast and space efficient for sampling from a large population: sample(range(10000000), 60).

サンプルの大きさが母集団の大きさより大きい場合 ValueError が送出されます。

以下の関数は特定の実数値分布を生成します。関数の引数の名前は、一般的な数学の慣例で使われている分布の公式の対応する変数から取られています; これらの公式のほとんどはどんな統計学のテキストにも載っています。

random.random()

次のランダムな浮動小数点数(範囲は [0.0, 1.0) )を返します。

random.uniform(a, b)

a <= b であれば a <= N <= bb < a であれば b <= N <= a であるようなランダムな浮動小数点数 N を返します。

端点の値 b が範囲に含まれるかどうかは、等式 a + (b-a) * random() における浮動小数点の丸めに依存します。

random.triangular(low, high, mode)

low <= N <= high でありこれら境界値の間に指定された最頻値 mode を持つようなランダムな浮動小数点数 N を返します。境界 lowhigh のデフォルトは 0 と 1 です。最頻値 mode 引数のデフォルトは両境界値の中点になり、対称な分布を与えます。

random.betavariate(alpha, beta)

ベータ分布です。引数の満たすべき条件は alpha > 0 および beta > 0 です。 0 から 1 の範囲の値を返します。

random.expovariate(lambd)

指数分布です。lambd は平均にしたい値の逆数です。(この引数は 「lambda」 と呼ぶべきなのですが、Python の予約語なので使えません。) 返す値の範囲は lambd が正なら 0 から正の無限大、lambd が負なら負の無限大から 0 です。

random.gammavariate(alpha, beta)

ガンマ分布です (ガンマ関数 ではありません !)。引数の満たすべき条件は alpha > 0 および beta > 0 です。

確率分布関数は:

          x ** (alpha - 1) * math.exp(-x / beta)
pdf(x) =  --------------------------------------
            math.gamma(alpha) * beta ** alpha
random.gauss(mu, sigma)

ガウス分布です。 mu は平均であり、 sigma は標準偏差です。この関数は後で定義する関数 normalvariate() より少しだけ高速です。

random.lognormvariate(mu, sigma)

対数正規分布です。この分布を自然対数を用いた分布にした場合、平均 mu で標準偏差 sigma の正規分布になります。 mu は任意の値を取ることができ、sigma はゼロより大きくなければなりません。

random.normalvariate(mu, sigma)

正規分布です。 mu は平均で、 sigma は標準偏差です。

random.vonmisesvariate(mu, kappa)

mu は平均の角度で、0 から 2*pi までのラジアンで表されます。 kappa は濃度パラメータで、ゼロ以上でなければなりません。kappa がゼロに等しい場合、この分布は範囲 0 から 2*pi の一様でランダムな角度の分布に退化します。

random.paretovariate(alpha)

パレート分布です。 alpha は形状パラメータです。

random.weibullvariate(alpha, beta)

ワイブル分布です。 alpha は尺度パラメタで、 beta は形状パラメータです。

Alternative Generator:

class random.SystemRandom([seed])

オペレーティングシステムの提供する発生源によって乱数を生成する os.urandom() 関数を使うクラスです。すべてのシステムで使えるメソッドではありません。ソフトウェアの状態に依存してはいけませんし、一連の操作は再現不能です。従って、 seed() メソッドは何の影響も及ぼさず、無視されます。 getstate()setstate() メソッドが呼び出されると、例外 NotImplementedError が送出されます。

参考

M. Matsumoto and T. Nishimura, 「Mersenne Twister: A 623-dimensionally equidistributed uniform pseudorandom number generator」, ACM Transactions on Modeling and Computer Simulation Vol. 8, No. 1, January pp.3–30 1998.

Complementary-Multiply-with-Carry recipe 長い周期と比較的シンプルな更新操作を備えた互換性のある別の乱数生成器。

9.6.1. 再現性について

疑似乱数生成器から与えられたシーケンスを再現できると便利なことがあります。シード値を再利用することで、複数のスレッドが実行されていない限り、実行ごとに同じシーケンスが再現できます。

random モジュールのアルゴリズムやシード処理関数のほとんどは、Python バージョン間で変更される対象となりますが、次の二点は変更されないことが保証されています:

  • 新しいシード処理メソッドが追加されたら、後方互換なシード処理器が提供されます。
  • 生成器の random() メソッドは、互換なシード処理器に同じシードが与えられた場合、引き続き同じシーケンスを生成します。

9.6.2. 例とレシピ

Basic usage:

>>> random.random()                      # Random float x, 0.0 <= x < 1.0
0.37444887175646646

>>> random.uniform(1, 10)                # Random float x, 1.0 <= x < 10.0
1.1800146073117523

>>> random.randrange(10)                 # Integer from 0 to 9
7

>>> random.randrange(0, 101, 2)          # Even integer from 0 to 100
26

>>> random.choice('abcdefghij')          # Single random element
'c'

>>> items = [1, 2, 3, 4, 5, 6, 7]
>>> random.shuffle(items)
>>> items
[7, 3, 2, 5, 6, 4, 1]

>>> random.sample([1, 2, 3, 4, 5],  3)   # Three samples without replacement
[4, 1, 5]

A common task is to make a random.choice() with weighted probabilities.

If the weights are small integer ratios, a simple technique is to build a sample population with repeats:

>>> weighted_choices = [('Red', 3), ('Blue', 2), ('Yellow', 1), ('Green', 4)]
>>> population = [val for val, cnt in weighted_choices for i in range(cnt)]
>>> population
['Red', 'Red', 'Red', 'Blue', 'Blue', 'Yellow', 'Green', 'Green', 'Green', 'Green']

>>> random.choice(population)
'Green'

A more general approach is to arrange the weights in a cumulative distribution with itertools.accumulate(), and then locate the random value with bisect.bisect():

>>> choices, weights = zip(*weighted_choices)
>>> cumdist = list(itertools.accumulate(weights))
>>> cumdist            # [3, 3+2, 3+2+1, 3+2+1+4]
[3, 5, 6, 10]

>>> x = random.random() * cumdist[-1]
>>> choices[bisect.bisect(cumdist, x)]
'Blue'