random --- 疑似乱数を生成する¶
ソースコード: Lib/random.py
このモジュールでは様々な分布をもつ擬似乱数生成器を実装しています。
整数用に、ある範囲からの一様な選択があります。 シーケンス用には、シーケンスからのランダムな要素の一様な選択、リストのランダムな置換をインプレースに生成する関数、選んだ要素を母集団に戻さずにランダムサンプリングする関数があります。
実数用としては、一様分布、正規分布 (ガウス分布)、対数正規分布、負の指数分布、ガンマおよびベータ分布を計算する関数があります。角度の分布を生成するにはフォン・ミーゼス分布が利用できます。
ほとんど全てのモジュール関数は、基礎となる関数 random() に依存します。この関数はランダムな浮動小数点数を半開区間 0.0 <= X < 1.0 内に一様に生成します。Python は中心となる乱数生成器としてメルセンヌツイスタを使います。これは 53 ビット精度の浮動小数点を生成し、周期は 2**19937-1 です。本体は C で実装されていて、高速でスレッドセーフです。メルセンヌツイスタは、現存する中で最も広範囲にテストされた乱数生成器のひとつです。しかしながら、メルセンヌツイスタは完全に決定論的であるため、全ての目的に合致しているわけではなく、暗号化の目的には全く向いていません。
このモジュールで提供されている関数は、実際には random.Random クラスの隠蔽されたインスタンスのメソッドに束縛されています。内部状態を共有しない生成器を取得するため、自分で Random のインスタンスを生成することができます。
独自に考案した別の基本生成器を使う場合は、Random クラスをサブクラス化することもできます。
詳しくは同クラスのドキュメントを参照してください。
random モジュールは SystemRandom クラスも提供しています。
このクラスはシステム関数 os.urandom() を使い、オペレーティングシステムが提供する発生源から乱数を生成します。
警告
このモジュールの擬似乱数生成器をセキュリティ目的に使用してはいけません。セキュリティや暗号学的な用途については secrets モジュールを参照してください。
参考
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 長い周期と比較的シンプルな更新操作を備えた互換性のある別の乱数生成器。
注釈
グローバルな乱数生成器と Random のインスタンスはスレッドセーフです。
ただし、フリースレッドビルドでは、グローバルな生成器や同じ Random インスタンスを同時に呼び出すと、競合によって性能が低下する場合があります。
代わりにスレッドごとに別の Random インスタンスを使うことを検討してください。
保守 (bookkeeping) 関数¶
- random.seed(a=None, version=2)¶
乱数生成器を初期化します。
a が省略されるか
Noneの場合、現在のシステム時刻が使用されます。乱数のソースがオペレーティングシステムによって提供される場合、システム時刻の代わりにそれが使用されます (利用可能性についての詳細はos.urandom()関数を参照)。a が int の場合は、その絶対値が直接使用されます。
バージョン2 (デフォルト) では、
str,bytes,bytearrayオブジェクトはintに変換され、そのビットがすべて使用されます。バージョン1 (Python の古いバージョンでの乱数列を再現するために提供される) では、
strとbytesに対して適用されるアルゴリズムは、より狭い範囲のシードを生成します。バージョン 3.2 で変更: 文字列シードのすべてのビットを使うバージョン2スキームに移行。
- random.getstate()¶
乱数生成器の現在の内部状態を記憶したオブジェクトを返します。このオブジェクトを
setstate()に渡して内部状態を復元することができます。
- random.setstate(state)¶
state は予め
getstate()を呼び出して得ておかなくてはなりません。setstate()はgetstate()が呼び出された時の乱数生成器の内部状態を復元します。
バイト列用の関数¶
- random.randbytes(n)¶
n バイトのランダムなバイト列を生成します。
セキュリティトークンを生成する目的で、このメソッドを使用しないでください。代わりに
secrets.token_bytes()を使用してください。Added in version 3.9.
整数用の関数¶
- random.randrange(stop)¶
- random.randrange(start, stop[, step])
range(start, stop, step)の要素からランダムに選ばれた要素を返します。これはおおむね
choice(range(start, stop, step))と等価ですが、任意の大きさの範囲を扱え、一般的なケース向けに最適化されています。位置引数のパターンは
range()関数の引数と一致します。キーワード引数は予期しない解釈をされることがあるため、使わないでください。 例えば
randrange(start=100)はrandrange(0, 100, 1)と解釈されます。バージョン 3.2 で変更: 一様に分布した値の生成に関して
randrange()がより洗練されました。以前はint(random()*n)のようなやや一様でない分布を生成するスタイルを使用していました。バージョン 3.12 で変更: 整数以外の型の自動変換はサポートされなくなりました。
randrange(10.0)やrandrange(Fraction(10, 1))のような呼び出しはTypeErrorを送出します。
- random.randint(a, b)¶
a <= N <= bであるようなランダムな整数 N を返します。randrange(a, b+1)のエイリアスです。
- random.getrandbits(k)¶
k 桁の乱数ビットを持つ Python の非負整数を生成し、返します。 このメソッドはメルセンヌツイスタ生成器で提供されており、その他の乱数生成器でもオプションの API として提供されている場合があります。
getrandbits()が使用可能な場合、randrange()は任意の範囲の乱数を生成できるようになります。バージョン 3.9 で変更: k の値として 0 が許されるようになりました。
シーケンス用の関数¶
- random.choice(seq)¶
空でないシーケンス seq からランダムに要素を返します。 seq が空のときは、
IndexErrorが送出されます。
- random.choices(population, weights=None, *, cum_weights=None, k=1)¶
population から重複ありで選んだ要素からなる大きさ k のリストを返します。population が空の場合
IndexErrorを送出します。weights シーケンスが与えられた場合、相対的な重みに基づいて要素が選ばれます。あるいは、cum_weights シーケンスが与えられた場合、累積的な重み (
itertools.accumulate()を用いて計算されるかもしれません) で要素が選ばれます。例えば、相対的な重み[10, 5, 30, 5]は累積的な重み[10, 15, 45, 50]と等価です。内部的には、相対的な重みは要素選択の前に累積的な重みに変換されるため、累積的な重みを渡すと手間を省けます。weights および cum_weights が与えられなかった場合、要素は同じ確率で選択されます。重みのシーケンスが与えられた場合、その長さは population シーケンスと同じでなければなりません。weights と cum_weights を同時に与えると
TypeErrorが送出されます。weights や cum_weights には、
random()が返すfloat値と組み合わせて演算できる任意の数値型を使用できます (整数、浮動小数点数、有理数は含みますが、十進数は除きます)。 重みは非負かつ有限であることが前提です。 すべての重みが 0 の場合、ValueErrorが送出されます。与えられた種に対して、同じ重みを持つ
choices()関数は、一般にchoice()を繰り返し呼び出す場合とは異なるシーケンスを生成します。choices()で使用されるアルゴリズムは、内部の一貫性とスピードのために浮動小数点演算を使用します。choice()で使われるアルゴリズムは、丸め誤差による小さな偏りを避けるために、デフォルトでは選択を繰り返す整数演算になっています。Added in version 3.6.
バージョン 3.9 で変更: 全ての重みが 0 の場合、例外
ValueErrorを送出します。
- random.shuffle(x)¶
シーケンス x をインプレースにシャッフルします。
イミュータブルなシーケンスをシャッフルしてシャッフルされたリストを新たに返すには、代わりに
sample(x, k=len(x))を使用してください。たとえ
len(x)が小さくても、x の並べ替えの総数 (訳注: 要素数の階乗) は大半の乱数生成器の周期よりもすぐに大きくなることに注意してください。つまり、長いシーケンスの大半の並べ替えは決して生成されないだろう、ということです。例えば、長さ 2080 のシーケンスがメルセンヌツイスタ生成器の周期に収まる中で最大のものになります。バージョン 3.11 で変更: オプション引数 random が削除されました。
- random.sample(population, k, *, counts=None)¶
母集団のシーケンスから選ばれた長さ k の一意な要素からなるリストを返します。 選んだ要素を母集団に戻さない非復元抽出に用いられます。
母集団自体を変更せずに、母集団内の要素を含む新たなリストを返します。返されたリストは選択された順に並んでいるので、このリストの部分スライスもランダムなサンプルになります。これにより、くじの当選者 (サンプル) を1等賞と2等賞(の部分スライス)に分けることも可能です。
母集団の要素は ハッシュ可能 でなくても、ユニークでなくてもかまいません。母集団が繰り返しを含む場合、出現するそれぞれがサンプルに選択されえます。
母集団に重複がある場合はその分だけ1つずつ指定するか、キーワード専用オプション引数 counts で指定することができます。例えば、
sample(['red', 'blue'], counts=[4, 2], k=5)はsample(['red', 'red', 'red', 'red', 'blue', 'blue'], k=5)と等価になります。ある範囲の整数からサンプルを取る場合、引数に
range()オブジェクトを使用してください。大きな母集団の場合、これは特に速く、メモリ効率が良いです:sample(range(10000000), k=60)。サンプルの大きさが母集団の大きさより大きい場合
ValueErrorが送出されます。バージョン 3.9 で変更: counts 引数が追加されました。
バージョン 3.11 で変更: population 変数はシーケンスでなければなりません。 setsのリストへの暗黙の型変換はサポートされていません。
離散分布¶
次の関数は離散分布に従う乱数を生成します。
実数分布¶
以下の関数は特定の実数値分布を生成します。関数の引数の名前は、一般的な数学の慣例で使われている分布の公式の対応する変数から取られています; これらの公式のほとんどはどんな統計学のテキストにも載っています。
- random.random()¶
ランダムな浮動小数点数(範囲は
0.0 <= X < 1.0)を返します。
- random.uniform(a, b)¶
a <= bであればa <= N <= b、b < aであればb <= N <= aであるようなランダムな浮動小数点数 N を返します。端点の値
bが範囲に含まれるかどうかは、式a + (b-a) * random()における浮動小数点の丸めに依存します。
- random.triangular(low, high, mode)¶
low <= N <= highでありこれら境界値の間に指定された最頻値 mode を持つようなランダムな浮動小数点数 N を返します。境界 low と high のデフォルトは 0 と 1 です。最頻値 mode 引数のデフォルトは両境界値の中点になり、対称な分布を与えます。
- random.betavariate(alpha, beta)¶
ベータ分布です。引数の満たすべき条件は
alpha > 0およびbeta > 0です。 0 から 1 の範囲の値を返します。
- random.expovariate(lambd=1.0)¶
指数分布です。 lambd は求める平均値で 1.0 を割った値で、ゼロであってはなりません。 (引数名は本来 "lambda" としたいところですが、Python の予約語なので使えません。) 返される値の範囲は、lambd が正なら 0 から正の無限大、負なら負の無限大から 0 です。
バージョン 3.12 で変更:
lambdにデフォルト値を追加しました。
- random.gammavariate(alpha, beta)¶
ガンマ分布です (ガンマ関数では ありません)。 形状パラメーター alpha と尺度パラメーター beta は正の値でなければなりません。 (引数の指定方法は資料によって異なり、'beta' を尺度の逆数と定義する資料もあります。)
確率分布関数は:
x ** (alpha - 1) * math.exp(-x / beta) pdf(x) = -------------------------------------- math.gamma(alpha) * beta ** alpha
- random.gauss(mu=0.0, sigma=1.0)¶
正規分布、またはガウス分布と呼ばれます。 mu は平均であり、 sigma は標準偏差です。この関数は後で定義する関数
normalvariate()より少しだけ高速です。マルチスレッドでの注意: 2 つのスレッドがこの関数を同時に呼び出すと、同じ戻り値を受け取る可能性があります。 これは 3 つの方法で避けられます。 1) スレッドごとに別の乱数生成器のインスタンスを使う。 2) すべての呼び出しをロックで保護する。 3) 速度は遅いもののスレッドセーフな
normalvariate()関数を代わりに使う。バージョン 3.11 で変更: mu と sigma にデフォルト値を追加しました。
- random.lognormvariate(mu, sigma)¶
対数正規分布です。この分布を自然対数を用いた分布にした場合、平均 mu で標準偏差 sigma の正規分布になります。 mu は任意の値を取ることができ、sigma はゼロより大きくなければなりません。
- random.normalvariate(mu=0.0, sigma=1.0)¶
正規分布です。 mu は平均で、 sigma は標準偏差です。
バージョン 3.11 で変更: 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 は形状パラメータです。
他の生成器¶
- class random.Random([seed])¶
randomモジュールが使うデフォルトの疑似乱数生成器を実装するクラスです。バージョン 3.11 で変更: 以前は seed は任意のハッシュ可能オブジェクトが設定可能でした。現在は
None,int,float,str,bytes,bytearrayに制限されています。異なる基本生成器を使う
Randomのサブクラスでは、次のメソッドをオーバーライドしてください。- getstate()¶
Randomインスタンスのgetstate()の動作を変更するには、サブクラスでこのメソッドをオーバーライドします。
- setstate(state)¶
Randomインスタンスのsetstate()の動作を変更するには、サブクラスでこのメソッドをオーバーライドします。
カスタムの生成器のサブクラスでは、必要に応じて次のメソッドも提供できます。
- getrandbits(k)¶
Randomインスタンスのgetrandbits()の動作を変更するには、サブクラスでこのメソッドをオーバーライドします。
- randbytes(n)¶
Randomインスタンスのrandbytes()の動作を変更するには、サブクラスでこのメソッドをオーバーライドします。
- class random.SystemRandom([seed])¶
オペレーティングシステムの提供する発生源によって乱数を生成する
os.urandom()関数を使うクラスです。 すべてのシステムで使えるメソッドではありません。 ソフトウェアの状態に依存せず、生成される乱数列は再現できません。 従って、seed()メソッドは何の影響も及ぼさず、無視されます。getstate()とsetstate()メソッドが呼び出されると、例外NotImplementedErrorが送出されます。
再現性について¶
疑似乱数生成器から与えられたシーケンスを再現できると便利なことがあります。シード値を再利用することで、複数のスレッドが実行されていない限り、実行ごとに同じシーケンスが再現できます。
random モジュールのアルゴリズムやシード処理関数のほとんどは、Python バージョン間で変更される対象となりますが、次の二点は変更されないことが保証されています:
新しいシード処理メソッドが追加されたら、後方互換なシード処理器が提供されます。
生成器の
random()メソッドは、互換なシード処理器に同じシードが与えられた場合、引き続き同じシーケンスを生成します。
使用例¶
基礎的な例:
>>> random() # ランダムな浮動小数点数: 0.0 <= x < 1.0
0.37444887175646646
>>> uniform(2.5, 10.0) # ランダムな浮動小数点数: 2.5 <= x <= 10.0
3.1800146073117523
>>> expovariate(1 / 5) # 平均 5 秒の到着間隔
5.148957571865031
>>> randrange(10) # 0 から 9 までの整数 (両端を含む)
7
>>> randrange(0, 101, 2) # 0 から 100 までの偶数 (両端を含む)
26
>>> choice(['win', 'lose', 'draw']) # シーケンスからランダムに選んだ 1 要素
'draw'
>>> deck = 'ace two three four'.split()
>>> shuffle(deck) # リストをシャッフルする。
>>> deck
['four', 'two', 'ace', 'three']
>>> sample([10, 20, 30, 40, 50], k=4) # 非復元抽出で選んだ 4 要素
[40, 10, 50, 30]
シミュレーション:
>>> # ルーレットを 6 回回す (重み付きの復元抽出)。
>>> choices(['red', 'black', 'green'], [18, 18, 2], k=6)
['red', 'green', 'black', 'black', 'red', 'black']
>>> # 52 枚のトランプから 20 枚を非復元抽出で配り、
>>> # 値が 10 となるカード (10、ジャック、クイーン、キング) の
>>> # 割合を求める。
>>> deal = sample(['tens', 'low cards'], counts=[16, 36], k=20)
>>> deal.count('tens') / 20
0.15
>>> # 表が出る確率が 60% の偏ったコインを 7 回投げて、
>>> # 表が 5 回以上出る確率を推定する。
>>> sum(binomialvariate(n=7, p=0.6) >= 5 for i in range(10_000)) / 10_000
0.4169
>>> # 5 個のサンプルの中央値が中央の 2 つの四分位区間に入る確率
>>> def trial():
... return 2_500 <= sorted(choices(range(10_000), k=5))[2] < 7_500
...
>>> sum(trial() for i in range(10_000)) / 10_000
0.7958
サンプルの平均の信頼区間を推定するのに、重複ありでリサンプリングして 統計的ブートストラップ を行う例:
# https://www.thoughtco.com/example-of-bootstrapping-3126155
from statistics import fmean as mean
from random import choices
data = [41, 50, 29, 37, 81, 30, 73, 63, 20, 35, 68, 22, 60, 31, 95]
means = sorted(mean(choices(data, k=len(data))) for i in range(100))
print(f'The sample mean of {mean(data):.1f} has a 90% confidence '
f'interval from {means[5]:.1f} to {means[94]:.1f}')
薬と偽薬の間に観察された効果の違いについて、統計的有意性、すなわち p 値 を決定するために、リサンプリング順列試験 を行う例:
# Dennis Shasha と Manda Wilson の "Statistics is Easy" に掲載された例
from statistics import fmean as mean
from random import shuffle
drug = [54, 73, 53, 70, 73, 68, 52, 65, 65]
placebo = [54, 51, 58, 44, 55, 52, 42, 47, 58, 46]
observed_diff = mean(drug) - mean(placebo)
n = 10_000
count = 0
combined = drug + placebo
for i in range(n):
shuffle(combined)
new_diff = mean(combined[:len(drug)]) - mean(combined[len(drug):])
count += (new_diff >= observed_diff)
print(f'{n} label reshufflings produced only {count} instances with a difference')
print(f'at least as extreme as the observed difference of {observed_diff:.1f}.')
print(f'The one-sided p-value of {count / n:.4f} leads us to reject the null')
print(f'hypothesis that there is no difference between the drug and the placebo.')
マルチサーバーキューにおける到達時間とサービス提供のシミュレーション:
from heapq import heapify, heapreplace
from random import expovariate, gauss
from statistics import mean, quantiles
average_arrival_interval = 5.6
average_service_time = 15.0
stdev_service_time = 3.5
num_servers = 3
waits = []
arrival_time = 0.0
servers = [0.0] * num_servers # 各サーバーが利用可能になる時刻
heapify(servers)
for i in range(1_000_000):
arrival_time += expovariate(1.0 / average_arrival_interval)
next_server_available = servers[0]
wait = max(0.0, next_server_available - arrival_time)
waits.append(wait)
service_duration = max(0.0, gauss(average_service_time, stdev_service_time))
service_completed = arrival_time + wait + service_duration
heapreplace(servers, service_completed)
print(f'Mean wait: {mean(waits):.1f} Max wait: {max(waits):.1f}')
print('Quartiles:', [round(q, 1) for q in quantiles(waits)])
参考
Statistics for Hackers Jake Vanderplas による統計解析のビデオ。シミュレーション、サンプリング、シャッフル、交差検定といった基本的な概念のみを用いています。
Economics Simulation Peter Norvig による市場価格のシミュレーション。このモジュールが提供する多くのツールや分布 (gauss, uniform, sample, betavariate, choice, triangular, randrange) の活用法を示しています。
A Concrete Introduction to Probability (using Python) Peter Norvig によるチュートリアル。確率論の基礎、シミュレーションの書き方、Python を使用したデータ解析法をカバーしています。
レシピ¶
以下のレシピは、itertools モジュールや more-itertools プロジェクトの組合せイテレータから効率よくランダムに選択する方法を示します。
import random
def random_product(*iterables, repeat=1):
"次からランダムに選択: itertools.product(*iterables, repeat=repeat)"
pools = tuple(map(tuple, iterables)) * repeat
return tuple(map(random.choice, pools))
def random_permutation(iterable, r=None):
"次からランダムに選択: itertools.permutations(iterable, r)"
pool = tuple(iterable)
r = len(pool) if r is None else r
return tuple(random.sample(pool, r))
def random_combination(iterable, r):
"次からランダムに選択: itertools.combinations(iterable, r)"
pool = tuple(iterable)
n = len(pool)
indices = sorted(random.sample(range(n), r))
return tuple(pool[i] for i in indices)
def random_combination_with_replacement(iterable, r):
"""復元抽出で r 個の要素を選ぶ。
結果は iterable 内の順序に合わせて並べる。
"""
# 結果は set(itertools.combinations_with_replacement(iterable, r)) に含まれる。
pool = tuple(iterable)
n = len(pool)
indices = sorted(random.choices(range(n), k=r))
return tuple(pool[i] for i in indices)
def random_derangement(iterable):
"どの要素も元の位置に残らない順列を選ぶ。"
seq = tuple(iterable)
if len(seq) < 2:
if not seq:
return ()
raise IndexError('No derangments to choose from')
perm = list(range(len(seq)))
start = tuple(perm)
while True:
random.shuffle(perm)
if all(p != q for p, q in zip(start, perm)):
return tuple([seq[i] for i in perm])
デフォルトの random() は、0.0 ≤ x < 1.0 の範囲にある 2⁻⁵³ の倍数を返します。
これらの数はすべて等間隔で、Python の浮動小数点数として正確に表現できます。
ただし、この区間内にある他の多くの表現可能な浮動小数点数は選ばれません。
例えば、0.05954861408025609 は 2⁻⁵³ の整数倍ではありません。
次のレシピは別の方法を使います。 区間内のすべての浮動小数点数が選択対象になります。 仮数は 2⁵² ≤ mantissa < 2⁵³ の範囲の整数の一様分布から得ます。 指数は幾何分布から得られ、-53 より小さい指数の出現頻度は、1 つ大きい指数の半分です。
from random import Random
from math import ldexp
class FullRandom(Random):
def random(self):
mantissa = 0x10_0000_0000_0000 | self.getrandbits(52)
exponent = -53
x = 0
while not x:
x = self.getrandbits(32)
exponent += x.bit_length() - 32
return ldexp(mantissa, exponent)
このクラスのすべての 実数値分布 は、新しいメソッドを使います。
>>> fr = FullRandom()
>>> fr.random()
0.05954861408025609
>>> fr.expovariate(0.25)
8.87925541791544
The recipe is conceptually equivalent to an algorithm that chooses from
all the multiples of 2⁻¹⁰⁷⁴ in the range 0.0 ≤ x < 1.0. All such
numbers are evenly spaced, but most have to be rounded down to the
nearest representable Python float. (The value 2⁻¹⁰⁷⁴ is the smallest
positive unnormalized float and is equal to math.ulp(0.0).)
参考
Generating Pseudo-random Floating-Point Values は、通常の random() より細かい間隔の浮動小数点数を生成する方法について述べた Allen B. Downey の論文です。
コマンドラインでの使用¶
Added in version 3.13.
random モジュールはコマンドラインから実行できます。
python -m random [-h] [-c CHOICE [CHOICE ...] | -i N | -f N] [input ...]
以下のオプションが使用できます:
- -h, --help¶
ヘルプメッセージを表示して終了します。
オプションが指定されない場合、出力は入力に依存します。
コマンドラインの例¶
以下は random のコマンドラインインターフェースの例です:
$ # ランダムに 1 つ選ぶ。
$ python -m random egg bacon sausage spam "Lobster Thermidor aux crevettes with a Mornay sauce"
Lobster Thermidor aux crevettes with a Mornay sauce
$ # ランダムな整数
$ python -m random 6
6
$ # ランダムな浮動小数点数
$ python -m random 1.8
1.7080016272295635
$ # 明示的な引数を使う。
$ python -m random --choice egg bacon sausage spam "Lobster Thermidor aux crevettes with a Mornay sauce"
egg
$ python -m random --integer 6
3
$ python -m random --float 1.8
1.5666339105010318
$ python -m random --integer 6
5
$ python -m random --float 6
3.1942323316565915