numpy.random.seed()
の内部には、たとえばnumpy.random.seed(1)
、numpy.random.seed(101)
など、さまざまな数値を入力できることに気づきました。異なる数字はどういう意味ですか?どのように数字を選びますか?
非常に基本的な乱数ジェネレータを考えてみましょう:
Z[i] = (a*Z[i-1] + c) % m
ここで、Z[i]
はith
乱数、a
は乗数、c
は増分です-異なる[a
、c
とm
の組み合わせには、異なるジェネレーターがあります。これは、レーマーによって導入された 線形合同ジェネレータ として知られています。その除算の剰余、つまり係数(%
)は、0〜m-1
の数値を生成します。U[i] = Z[i] / m
を設定することにより、0〜1の乱数を取得します。
お気づきかもしれませんが、この生成プロセスを開始するには、Z[1]
を取得するために、Z[0]
-初期値が必要です。プロセスを開始するこの初期値はシードと呼ばれます。この例を見てください:
初期値、シードはプロセスを開始するために7として決定されます。ただし、その値は乱数の生成には使用されません。代わりに、最初のZ
を生成するために使用されます。
疑似乱数ジェネレータの最も重要な機能は、その予測不能性です。一般に、シードを共有しない限り、現在のジェネレーターはこれよりもはるかに複雑であるため、すべてのシードで問題ありません。ただし、次のステップとして、ランダムにシードを生成することもできます。別の方法として、最初のn
番号をスキップできます。
主な出典:Law、A. M.(2007)。シミュレーションのモデリングと分析。タタ・マグロウヒル。
値は決定論的アルゴリズムを使用して計算され、確率は実際の役割を果たさないため、実際には通常、乱数シーケンスと呼ばれるものは「疑似乱数」シーケンスです。
「シード」はシーケンスの開始点であり、同じシードから開始すると、同じ数のシーケンスが得られることが保証されます。これは、デバッグなどの場合に非常に役立ちます(プログラムでエラーを探している場合、問題を再現して調査する必要があります。実行が異なるため、非決定的プログラムのデバッグははるかに困難です) 。
短い答え:
numpy.random
で乱数ジェネレータをseed()
するには3つの方法があります。
引数を使用しないか、None
を使用します-RNGは、OSの乱数ジェネレーター(通常、暗号的にランダムです)から初期化されます。
いくつかの32ビット整数Nを使用します-RNGはこれを使用して、決定論的関数に基づいてその状態を初期化します(同じシード→同じ状態)
32ビット整数nの配列のようなシーケンスを使用する、n1、n2など、RNGはこれを使用して、決定論的関数に基づいてその状態を初期化します(シードと同じ値→同じ状態)。これは、ソースコードにマジックナンバーがあり、彼らがしていることをしている理由が明確ではありませんが、一種のハッシュ関数で実行することを目的としています。
繰り返し可能なシンプルな処理を行う場合は、単一の整数を使用します。
繰り返し可能な何かをしたいがサードパーティが推測する可能性が低い場合は、タプル、リスト、または32ビット整数のシーケンスを含むnumpy配列を使用します。たとえば、numpy.random
をNone
のシードとともに使用して、OSから32ビット整数の束(たとえば、32個、合計で1024ビットを生成する)を生成できます。 RNG、秘密の場所に保存したシードS
に保存し、そのシードを使用して、希望する疑似乱数のシーケンスRを生成します。その後、S
を再度シードしてシーケンスを再作成できます。S
シークレット(および生成された数値R)の値を保持している限り、誰もできません。そのシーケンスRを再現できます。単一の整数を使用する場合、40億の可能性しかなく、誰かがそれらすべてを試す可能性があります。それは偏執狂の側に少しかもしれませんが、あなたはそれをすることができました。
より長い答え
numpy.random
モジュールは、 Mersenne Twister アルゴリズムを使用します。これは、次の2つの方法のいずれかで確認できます。
numpy.random.RandomState
のドキュメントを参照するか、numpy.random
がnumpy.random.*
関数のインスタンスを使用します(ただし、分離されたの独立したインスタンス)
Pyrexと呼ばれるものを使用して高速C実装をラップする ソースコード を mtrand.pyx で確認すると、 randomkit.c および initarray.c 。
いずれにせよ、seed()
についてのnumpy.random.RandomState
の説明は次のとおりです。
互換性の保証固定シードと、同じパラメーターを使用する
RandomState
メソッドへの固定の一連の呼び出しは、値が正しくない。不正な値は修正され、修正が行われたNumPyバージョンが関連するドキュメント文字列に記載されます。以前の動作が変更されていない限り、既存のパラメーター範囲の拡張と新しいパラメーターの追加が許可されます。パラメータ:
seed:{None、int、array_like}、オプション疑似乱数ジェネレータの初期化に使用されるランダムシード。 0から2 ** 32-1までの整数、そのような整数の配列(または他のシーケンス)、または
None
(デフォルト)を指定できます。シードがNone
の場合、RandomStateは、利用可能な場合は/dev/urandom
(またはWindowsのアナログ)からデータを読み込もうとし、そうでない場合はクロックからシードしようとします。
シードの使用方法は記載されていませんが、ソースコードを掘り下げる場合、init_by_array
関数を参照します:(docstring elided)
def seed(self, seed=None):
cdef rk_error errcode
cdef ndarray obj "arrayObject_obj"
try:
if seed is None:
with self.lock:
errcode = rk_randomseed(self.internal_state)
else:
idx = operator.index(seed)
if idx > int(2**32 - 1) or idx < 0:
raise ValueError("Seed must be between 0 and 2**32 - 1")
with self.lock:
rk_seed(idx, self.internal_state)
except TypeError:
obj = np.asarray(seed).astype(np.int64, casting='safe')
if ((obj > int(2**32 - 1)) | (obj < 0)).any():
raise ValueError("Seed must be between 0 and 2**32 - 1")
obj = obj.astype('L', casting='unsafe')
with self.lock:
init_by_array(self.internal_state, <unsigned long *>PyArray_DATA(obj),
PyArray_DIM(obj, 0))
init_by_array
関数は次のようになります。
extern void
init_by_array(rk_state *self, unsigned long init_key[], npy_intp key_length)
{
/* was signed in the original code. RDH 12/16/2002 */
npy_intp i = 1;
npy_intp j = 0;
unsigned long *mt = self->key;
npy_intp k;
init_genrand(self, 19650218UL);
k = (RK_STATE_LEN > key_length ? RK_STATE_LEN : key_length);
for (; k; k--) {
/* non linear */
mt[i] = (mt[i] ^ ((mt[i - 1] ^ (mt[i - 1] >> 30)) * 1664525UL))
+ init_key[j] + j;
/* for > 32 bit machines */
mt[i] &= 0xffffffffUL;
i++;
j++;
if (i >= RK_STATE_LEN) {
mt[0] = mt[RK_STATE_LEN - 1];
i = 1;
}
if (j >= key_length) {
j = 0;
}
}
for (k = RK_STATE_LEN - 1; k; k--) {
mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941UL))
- i; /* non linear */
mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
i++;
if (i >= RK_STATE_LEN) {
mt[0] = mt[RK_STATE_LEN - 1];
i = 1;
}
}
mt[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */
self->gauss = 0;
self->has_gauss = 0;
self->has_binomial = 0;
}
これは、提供されたシード値のシーケンス内の各値を使用して、非線形のハッシュのような方法で乱数状態を本質的に「変更」します。
1つの非常に具体的な答え:np.random.seed
は0 and 2**32 - 1
から値を取得できます。これは、ハッシュ可能なオブジェクトを取得できるrandom.seed
とは興味深い点で異なります。
ランダムシードの意味を理解するには、値が決定論的アルゴリズムを使用して計算されるため、最初に「疑似ランダム」数列を理解する必要があります。
したがって、この数値は、ランダムジェネレーターから取得した次の数値を計算するための開始値と考えることができます。ここに同じ値を入力すると、プログラムは毎回同じ「ランダム」値を取得するため、プログラムは確定的になります。
これで述べたように post
彼ら(
numpy.random
およびrandom.random
)どちらもメルセンヌツイスターシーケンスを使用して乱数を生成します。どちらも完全に確定的です。つまり、情報の重要なビットをいくつか知っている場合は、次に来る数字を確実に予測できます。
ランダム性に本当に関心がある場合は、ユーザーにノイズ(任意の単語)を生成するように依頼するか、システム時間をシードとして使用してください。
コードがIntel CPU(または最新のチップを搭載したAMD)で実行される場合は、CPU命令rdrand
を使用して「真の」(ハードウェア)ランダム性を収集する RdRand パッケージを確認することもお勧めします。
参照:
基本的に、その数は毎回同じ「ランダム性」を保証します。
より正確には、数値はシードであり、整数、任意の長さの整数の配列(または他のシーケンス)、またはデフォルト(なし)にすることができます。シードが存在しない場合、randomは/ dev/urandomからデータを読み込もうとするか、そうでなければクロックからシードを作成しようとします。
編集:正直なところ、プログラムが非常に安全である必要があるものでない限り、何を選んでもかまいません。このような場合は、これらのメソッドを使用しないでください。暗号で保護された疑似乱数ジェネレータが必要な場合は、os.urandom()
またはSystemRandom
を使用してください。
ここで理解する最も重要な概念は、擬似ランダム性の概念です。このアイデアを理解したら、プログラムにシードなどが本当に必要かどうかを判断できます。 ここ をお勧めします。