JavaScriptで乱数ジェネレーター(Math.random)をシードすることはできますか?
JavaScriptで乱数ジェネレーター(Math.random)をシードすることはできますか?
回答:
注:簡潔さおよび見かけ上の優雅さにもかかわらず(またはむしろそのため)、このアルゴリズムは決してランダム性に関して高品質のものではありません。より良い結果を得るために、たとえばこの回答にリストされているものを探してください。
(元々はコメントで提示された巧妙なアイデアから別の回答へと変更されました。)
var seed = 1;
function random() {
var x = Math.sin(seed++) * 10000;
return x - Math.floor(x);
}
設定できます seed
ゼロ(またはMath.PIの倍数)を避けて、任意の数にます。
このソリューションの優雅さは、私の意見では、あなたは奇妙なパターンを避けるために捨てなければならない数字の最小量について表し、10000以外の(任意の「マジック」の数字の欠如から来ている-値の結果を参照10、100、1000)。簡潔さもいいです。
Math.random()よりも少し遅い(2倍または3倍)が、JavaScriptで作成された他のソリューションとほぼ同じ速さだと思います。
プレーンなJavaScriptで、多くの優れた短くて高速な擬似乱数ジェネレーター(PRNG)関数を実装しました。それらのすべてを播種し、良質の数を提供することができます。
まず、PRNGを適切に初期化するように注意してください。以下のほとんどのジェネレータには、シード生成手順が組み込まれていない(簡単にするため)が、PRNGの初期状態として1つ以上の32ビット値を受け入れます。類似のシード(たとえば、1と2の単純なシード)は、弱いPRNGで相関を引き起こし、結果として同様のプロパティ(ランダムに生成されたレベルが類似するなど)を持つ出力をもたらす可能性があります。これを回避するには、適切に分散されたシードを使用してPRNGを初期化することをお勧めします。
ありがたいことに、ハッシュ関数は、短い文字列からPRNGのシードを生成するのに非常に優れています。2つの文字列が類似している場合でも、優れたハッシュ関数は非常に異なる結果を生成します。MurmurHash3の混合関数に基づく例を次に示します。
function xmur3(str) {
for(var i = 0, h = 1779033703 ^ str.length; i < str.length; i++)
h = Math.imul(h ^ str.charCodeAt(i), 3432918353),
h = h << 13 | h >>> 19;
return function() {
h = Math.imul(h ^ h >>> 16, 2246822507);
h = Math.imul(h ^ h >>> 13, 3266489909);
return (h ^= h >>> 16) >>> 0;
}
}
各後続の呼復帰機能のは、xmur3
PRNGシードとして使用される新しい「ランダム」32ビットのハッシュ値を生成します。使い方は次のとおりです。
// Create xmur3 state:
var seed = xmur3("apples");
// Output four 32-bit hashes to provide the seed for sfc32.
var rand = sfc32(seed(), seed(), seed(), seed());
// Output one 32-bit hash to provide the seed for mulberry32.
var rand = mulberry32(seed());
// Obtain sequential random numbers like so:
rand();
rand();
または、シードを埋め込むダミーデータを選択し、ジェネレータを数回(12〜20回)進めて、初期状態を完全に混合します。これは、PRNGのリファレンス実装でよく見られますが、初期状態の数を制限します。
var seed = 1337 ^ 0xDEADBEEF; // 32-bit seed with optional XOR value
// Pad seed with Phi, Pi and E.
// https://en.wikipedia.org/wiki/Nothing-up-my-sleeve_number
var rand = sfc32(0x9E3779B9, 0x243F6A88, 0xB7E15162, seed);
for (var i = 0; i < 15; i++) rand();
これらのPRNG関数の出力は、32ビットの正の数値(0から2 32 -1)を生成しMath.random()
、乱数が必要な場合は、と同等の0-1(0を含む、1を含まない)の間の浮動小数点数に変換されます。特定の範囲のMDNに関するこの記事を読んでください。生ビットのみが必要な場合は、最後の除算演算を削除してください。
注意すべきもう1つの点は、JSの制限です。数値は、53ビットの解像度までの整数のみを表すことができます。また、ビット単位の演算を使用する場合、これは32に削減されます。これにより、64ビットの数値を使用するCまたはC ++で記述されたアルゴリズムの実装が困難になります。64ビットコードの移植には、パフォーマンスを大幅に低下させる可能性のあるシムが必要です。そのため、単純さと効率のために、JSと直接互換性があるため、32ビット演算を使用するアルゴリズムのみを検討しました。
sfc32は、PractRand乱数テストスイート(もちろん合格)の一部です。sfc32は128ビットの状態を持ち、JSでは非常に高速です。
function sfc32(a, b, c, d) {
return function() {
a >>>= 0; b >>>= 0; c >>>= 0; d >>>= 0;
var t = (a + b) | 0;
a = b ^ b >>> 9;
b = c + (c << 3) | 0;
c = (c << 21 | c >>> 11);
d = d + 1 | 0;
t = t + d | 0;
c = c + t | 0;
return (t >>> 0) / 4294967296;
}
}
Mulberry32は32ビット状態のシンプルなジェネレーターですが、非常に高速で品質が優れています(著者は、gjrandテストスイートのすべてのテストに合格し、完全な2 32期間を持っていますが、確認していません)。
function mulberry32(a) {
return function() {
var t = a += 0x6D2B79F5;
t = Math.imul(t ^ t >>> 15, t | 1);
t ^= t + Math.imul(t ^ t >>> 7, t | 61);
return ((t ^ t >>> 14) >>> 0) / 4294967296;
}
}
あなただけの単純だが必要な場合、私はこれをお勧めしますまともな PRNGをし、(参照乱数の十億を必要としない誕生日の問題を)。
2018年5月の時点で、xoshiro128 **はVigna / Blackman(Chromeで使用されるxoroshiroも書いた)によるXorshift ファミリーの新しいメンバーです。128ビットの状態を提供する最速のジェネレーターです。
function xoshiro128ss(a, b, c, d) {
return function() {
var t = b << 9, r = a * 5; r = (r << 7 | r >>> 25) * 9;
c ^= a; d ^= b;
b ^= c; a ^= d; c ^= t;
d = d << 11 | d >>> 21;
return (r >>> 0) / 4294967296;
}
}
著者は、ランダム性テストによく合格していると主張しています(ただし、警告があります)。他の研究者は、TestU01の一部のテスト(特に、LinearCompおよびBinaryRank)に失敗することを指摘しています。実際には、フロートが使用されている場合(これらの実装など)に問題が発生することはありませんが、生の下位ビットに依存している場合は問題が発生する可能性があります。
これは、ISAACとSpookyHashを作成したBob Jenkins(2007)によるJSFまたは「smallprng」です。それは合格ではないが早くSFCとして、PractRandテストをして、非常に高速である必要があります。
function jsf32(a, b, c, d) {
return function() {
a |= 0; b |= 0; c |= 0; d |= 0;
var t = a - (b << 27 | b >>> 5) | 0;
a = b ^ (c << 17 | c >>> 15);
b = c + d | 0;
c = d + t | 0;
d = a + t | 0;
return (d >>> 0) / 4294967296;
}
}
LCGは非常に高速でシンプルですが、そのランダム性の品質は非常に低いため、不適切に使用するとプログラムにバグが発生する可能性があります。それにもかかわらず、使用に示唆いくつかの答えよりも有意に良好ですMath.sin
かMath.PI
!ワンライナーですが、素晴らしいです:)。
var LCG=s=>()=>(2**31-1&(s=Math.imul(48271,s)))/2**31;
この実装は、1988年と1993年にPark–Millerによって提案されたように最小標準 RNG と呼ばれ、C ++ 11としてとして実装されています。状態は31ビットであることを覚えておいてください(31ビットは20億の可能な状態を与え、32ビットはそれの2倍を与えます)。これは、他の人が置き換えようとしているまさにそのタイプのPRNGです。minstd_rand
それは機能しますが、本当に速度が必要で、ランダム性の品質(とにかくランダムとは何ですか?)を気にしない限り、私はそれを使用しません。ゲームジャムやデモなどに最適です。LCG はシード相関の影響を受けるため、LCGの最初の結果を破棄するのが最善です。また、LCGの使用を主張する場合は、増分値を追加すると結果が向上する可能性がありますが、はるかに優れたオプションが存在する場合、それはおそらく無益な練習になります。
32ビットの状態を提供する他の乗算器があるようです(状態空間の増加)。
var LCG=s=>()=>(s=Math.imul(741103597,s)>>>0)/2**32;
var LCG=s=>()=>(s=Math.imul(1597334677,s)>>>0)/2**32;
これらのLCG値は次のとおりです。P。L'Ecuyer:1997年4月30日、さまざまなサイズと適切な格子構造の線形合同生成器の表。
seed = (seed * 185852 + 1) % 34359738337
。
Math.imul
すると、32ビット整数でCの乗算を使用する場合と同様にオーバーフローできます。あなたが提案しているのは、JSの整数空間の全範囲を利用するLCGです。これは、同様に探求する興味深い領域です。:)
いいえ、ただし、これは単純な疑似ランダムジェネレーターです。ウィキペディアから改造したMultiply-with-carryの実装です(その後削除されました)。
var m_w = 123456789;
var m_z = 987654321;
var mask = 0xffffffff;
// Takes any integer
function seed(i) {
m_w = (123456789 + i) & mask;
m_z = (987654321 - i) & mask;
}
// Returns number between 0 (inclusive) and 1.0 (exclusive),
// just like Math.random().
function random()
{
m_z = (36969 * (m_z & 65535) + (m_z >> 16)) & mask;
m_w = (18000 * (m_w & 65535) + (m_w >> 16)) & mask;
var result = ((m_z << 16) + (m_w & 65535)) >>> 0;
result /= 4294967296;
return result;
}
編集:m_zをリセットすることでシード関数を修正しました編集
2:重大な実装の欠陥が修正されました
seed
ので、この関数は、乱数発生器をリセットしていないmz_z
とき、変数が変更されるrandom()
と呼ばれています。したがって、次のように設定しますmz_z = 987654321
(またはその他の値)seed
m_w
ではなく、シードの変更m_z
。2)両方m_w
とm_z
それらの以前の値に基づいて変化するので、結果を変更しません。
AnttiSykäriのアルゴリズムは素晴らしく、簡潔です。最初に、Math.seed(s)を呼び出すときにJavascriptのMath.randomを置き換えるバリエーションを作成しましたが、Jasonは、関数を返す方が良いとコメントしました。
Math.seed = function(s) {
return function() {
s = Math.sin(s) * 10000; return s - Math.floor(s);
};
};
// usage:
var random1 = Math.seed(42);
var random2 = Math.seed(random1());
Math.random = Math.seed(random2());
これにより、JavaScriptにはない別の機能が提供されます。複数の独立したランダムジェネレーターです。複数の再現可能なシミュレーションを同時に実行したい場合、これは特に重要です。
Math.random
複数の独立したジェネレータを持つことができますよね?
Math.seed(42);
、それはあなたがそうならば、機能をリセットし、var random = Math.seed(42); random(); random();
あなたが得る0.70...
それから、0.38...
。var random = Math.seed(42);
もう一度電話をかけてリセットした場合は、次に電話random()
をかけたときに0.70...
再び電話がかかり、次に電話をかけたときに電話がかかります0.38...
。
random
代わりに、ネイティブのJavaScript関数を上書きするのではなく、名前付きのローカル変数を使用してください。上書きMath.random
すると、JISTコンパイラがすべてのコードを最適化しなくなる可能性があります。
Pierre L'Ecuyerの作品が1980年代後半から1990年代初頭にさかのぼる様子をご覧ください。他にもあります。(疑似)乱数ジェネレータを自分で作成すると、結果が統計的にランダムにならないか、期間が短い可能性が高いため、エキスパートでない場合はかなり危険です。Pierre(およびその他)は、実装が簡単な優れた(疑似)乱数ジェネレーターをいくつかまとめました。私は彼のLFSRジェネレーターの1つを使用しています。
https://www.iro.umontreal.ca/~lecuyer/myftp/papers/handstat.pdf
フィル・トロイ
前の回答のいくつかを組み合わせると、これはあなたが探しているシード可能なランダム関数です:
Math.seed = function(s) {
var mask = 0xffffffff;
var m_w = (123456789 + s) & mask;
var m_z = (987654321 - s) & mask;
return function() {
m_z = (36969 * (m_z & 65535) + (m_z >>> 16)) & mask;
m_w = (18000 * (m_w & 65535) + (m_w >>> 16)) & mask;
var result = ((m_z << 16) + (m_w & 65535)) >>> 0;
result /= 4294967296;
return result;
}
}
var myRandomFunction = Math.seed(1234);
var randomNumber = myRandomFunction();
Math.seed(0)()
返し0.2322845458984375
、をMath.seed(1)()
返します0.23228873685002327
。両方m_w
を変更m_z
し、シードに応じて変更すると効果があるようです。 var m_w = 987654321 + s; var m_z = 123456789 - s;
異なるシードを持つ最初の値の適切な分布を生成します。
独自の疑似ランダムジェネレーターを作成するのは非常に簡単です。
Dave Scoteseの提案は有用ですが、他の人が指摘しているように、かなり均一に分散されていません。
しかし、それはsinの整数引数のためではありません。それは単に、たまたま円の1次元の投影である罪の範囲が原因です。代わりに円の角度を取ると、均一になります。
したがって、sin(x)の代わりにarg(exp(i * x))/(2 * PI)を使用します。
線形次数が気に入らない場合は、xorと少し混ぜてください。実際の要素もそれほど重要ではありません。
n個の疑似乱数を生成するには、次のコードを使用できます。
function psora(k, n) {
var r = Math.PI * (k ^ n)
return r - Math.floor(r)
}
n = 42; for(k = 0; k < n; k++) console.log(psora(k, n))
また、実際のエントロピーが必要な場合は、疑似ランダムシーケンスを使用できないことに注意してください。
最近、JavaScriptでシード可能な乱数ジェネレータを必要とする多くの人々が、David Bauのシードランダムモジュールを使用しています。
Math.random
いいえ、しかし実行されたライブラリはこれを解決します。想像できるほぼすべての分布があり、シードされた乱数生成をサポートしています。例:
ran.core.seed(0)
myDist = new ran.Dist.Uniform(0, 1)
samples = myDist.sample(1000)
シードされた乱数を返す関数を作成しました。Math.sinを使用して長い乱数を取得し、シードを使用してそこから数値を選択します。
使用する :
seedRandom("k9]:2@", 15)
最初のパラメータが任意の文字列値であるシードされた数を返します。あなたの種。2番目のパラメーターは、返される桁数です。
function seedRandom(inputSeed, lengthOfNumber){
var output = "";
var seed = inputSeed.toString();
var newSeed = 0;
var characterArray = ['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','y','x','z','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','U','R','S','T','U','V','W','X','Y','Z','!','@','#','$','%','^','&','*','(',')',' ','[','{',']','}','|',';',':',"'",',','<','.','>','/','?','`','~','-','_','=','+'];
var longNum = "";
var counter = 0;
var accumulator = 0;
for(var i = 0; i < seed.length; i++){
var a = seed.length - (i+1);
for(var x = 0; x < characterArray.length; x++){
var tempX = x.toString();
var lastDigit = tempX.charAt(tempX.length-1);
var xOutput = parseInt(lastDigit);
addToSeed(characterArray[x], xOutput, a, i);
}
}
function addToSeed(character, value, a, i){
if(seed.charAt(i) === character){newSeed = newSeed + value * Math.pow(10, a)}
}
newSeed = newSeed.toString();
var copy = newSeed;
for(var i=0; i<lengthOfNumber*9; i++){
newSeed = newSeed + copy;
var x = Math.sin(20982+(i)) * 10000;
var y = Math.floor((x - Math.floor(x))*10);
longNum = longNum + y.toString()
}
for(var i=0; i<lengthOfNumber; i++){
output = output + longNum.charAt(accumulator);
counter++;
accumulator = accumulator + parseInt(newSeed.charAt(counter));
}
return(output)
}
固定シードの簡単なアプローチ:
function fixedrandom(p){
const seed = 43758.5453123;
return (Math.abs(Math.sin(p)) * seed)%1;
}
0から100までの数値。
Number.parseInt(Math.floor(Math.random() * 100))
Math.random
れるたびにMath.random
、同じ一連の乱数が生成されるようにシードすることです。この質問は、言うまでもなく、の実際の使用法/デモについてではありませんMath.random
。