Генератор случайных чисел JavaScript для посева
JavaScript Math.random()
функция возвращает случайное значение между 0 и 1, автоматически заполняется на основе текущего времени (аналогично Java, я считаю). Однако я не думаю, что есть какой-либо способ установить для вас собственное семя.
Как я могу сделать генератор случайных чисел, для которого я могу предоставить свое собственное начальное значение, чтобы я мог получить повторяемую последовательность (псевдо)случайных чисел?
9 ответов:
один из вариантов http://davidbau.com/seedrandom который является посевной RC4 на основе математики.случайный ()-замена с хорошими свойствами.
Если вам не нужна возможность посева, просто используйте
Math.random()
и построить вспомогательные функции вокруг него (например.randRange(start, end)
).Я не уверен, что RNG вы используете, но лучше знать и документировать его, чтобы вы знали о его характеристиках и ограничениях.
как сказал Старки, Mersenne Twister-хороший PRNG, но его нелегко реализовать. Если вы хотите сделать это самостоятельно, попробуйте реализует LCG - это очень легко, имеет приличные качества случайности (не так хорошо, как Mersenne Twister), и вы можете использовать некоторые из популярных констант.
function RNG(seed) { // LCG using GCC's constants this.m = 0x80000000; // 2**31; this.a = 1103515245; this.c = 12345; this.state = seed ? seed : Math.floor(Math.random() * (this.m - 1)); } RNG.prototype.nextInt = function() { this.state = (this.a * this.state + this.c) % this.m; return this.state; } RNG.prototype.nextFloat = function() { // returns in range [0,1] return this.nextInt() / (this.m - 1); } RNG.prototype.nextRange = function(start, end) { // returns in range [start, end): including start, excluding end // can't modulu nextInt because of weak randomness in lower bits var rangeSize = end - start; var randomUnder1 = this.nextInt() / this.m; return start + Math.floor(randomUnder1 * rangeSize); } RNG.prototype.choice = function(array) { return array[this.nextRange(0, array.length)]; } var rng = new RNG(20); for (var i = 0; i < 10; i++) console.log(rng.nextRange(10, 50)); var digits = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; for (var i = 0; i < 10; i++) console.log(rng.choice(digits));
Если вы хотите иметь возможность указать семя, вам просто нужно заменить вызовы на
getSeconds()
иgetMinutes()
. Вы можете передать int и использовать половину его mod 60 для значения секунд, а другую половину по модулю 60, чтобы дать вам другую часть.как говорится, Этот метод выглядит как мусор. Делать правильную генерацию случайных чисел очень сложно. Очевидная проблема заключается в том, что семя случайных чисел основано на секундах и минутах. Чтобы угадать семя и воссоздать свой поток случайных чисел требует только попробовать 3600 различных вторых и минутных комбинаций. Это также означает, что есть только 3600 различных семян. Это исправимо, но я бы с подозрением относился к этому RNG с самого начала.
Если вы хотите использовать лучший ГСЧ, попробуйте Вихрь Мерсена. Это хорошо протестированный и довольно надежный ГСЧ с огромной орбитой и отличной производительностью.
EDIT: я действительно должен быть прав и ссылаться на это как на псевдослучайный Генератор чисел и ПГСЧ.
Я использую JavaScript порт Mersenne Twister: https://gist.github.com/300494 Это позволяет установить вручную. Кроме того, как упоминалось в других ответах, Mersenne Twister-действительно хороший PRNG.
код, который вы перечислили выглядит как Lehmer RNG. Если это так, то
2147483647
является самым большим 32-разрядным целым числом со знаком,2147483647
является самым большим 32-разрядным простым, и48271
полный срок множитель, который используется для генерации чисел.если это правда, вы можете изменить
RandomNumberGenerator
чтобы взять дополнительный параметрseed
, а затем установитьthis.seed
доseed
; но вы должны быть осторожны, чтобы убедиться, что семя приведет к хорошему распределению случайные числа (Лемер может быть странным, как это) - но большинство семян будет хорошо.
ниже приведен PRNG, который может быть подан пользовательское семя. Звоню
SeedRandom
будет возвращать функция генератора случайных чисел.SeedRandom
может быть вызван без аргументов, чтобы заполнить возвращенную случайную функцию с текущим временем, или он может быть вызван либо с 1, либо с 2 неотрицательными интерсами в качестве аргументов, чтобы заполнить его этими целыми числами. Из-за точности точки с плавающей запятой посев только с 1 значением позволит только генератору быть инициированным к одному из 2^53 различных штат.возвращаемая функция генератора случайных чисел принимает 1 целочисленный аргумент с именем
limit
, предел должен быть в диапазоне от 1 до 4294965886, функция вернет число в диапазоне от 0 до предела-1.function SeedRandom(state1,state2){ var mod1=4294967087 var mul1=65539 var mod2=4294965887 var mul2=65537 if(typeof state1!="number"){ state1=+new Date() } if(typeof state2!="number"){ state2=state1 } state1=state1%(mod1-1)+1 state2=state2%(mod2-1)+1 function random(limit){ state1=(state1*mul1)%mod1 state2=(state2*mul2)%mod2 if(state1<limit && state2<limit && state1<mod1%limit && state2<mod2%limit){ return random(limit) } return (state1+state2)%limit } return random }
пример использования:
var generator1=SeedRandom() //Seed with current time var randomVariable=generator1(7) //Generate one of the numbers [0,1,2,3,4,5,6] var generator2=SeedRandom(42) //Seed with a specific seed var fixedVariable=generator2(7) //First value of this generator will always be //1 because of the specific seed.
этот генератор обладает следующими свойствами:
- это примерно 2^64 возможных внутренних состояний.
- в течение примерно 2^63, много больше, чем кто-либо когда-нибудь реально понадобится в программе JavaScript.
- из-за
mod
значения, являющиеся простыми числами в выходных данных нет простого шаблона, независимо от выбранного предела. Это не похоже на некоторые более простые PRNGs, которые демонстрируют некоторые довольно систематические шаблоны.- он отбрасывает некоторые результаты, чтобы получить идеальное распределение независимо от предела.
- это относительно медленный, работает около 10 000 000 раз в секунду на моей машине.
Если вы программируете в Typescript, я адаптировал реализацию Mersenne Twister, которая была введена в ответ Кристофа Хенкельмана на этот поток как класс typescript:
/** * copied almost directly from Mersenne Twister implementation found in https://gist.github.com/banksean/300494 * all rights reserved to him. */ export class Random { static N = 624; static M = 397; static MATRIX_A = 0x9908b0df; /* constant vector a */ static UPPER_MASK = 0x80000000; /* most significant w-r bits */ static LOWER_MASK = 0x7fffffff; /* least significant r bits */ mt = new Array(Random.N); /* the array for the state vector */ mti = Random.N + 1; /* mti==N+1 means mt[N] is not initialized */ constructor(seed:number = null) { if (seed == null) { seed = new Date().getTime(); } this.init_genrand(seed); } private init_genrand(s:number) { this.mt[0] = s >>> 0; for (this.mti = 1; this.mti < Random.N; this.mti++) { var s = this.mt[this.mti - 1] ^ (this.mt[this.mti - 1] >>> 30); this.mt[this.mti] = (((((s & 0xffff0000) >>> 16) * 1812433253) << 16) + (s & 0x0000ffff) * 1812433253) + this.mti; /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */ /* In the previous versions, MSBs of the seed affect */ /* only MSBs of the array mt[]. */ /* 2002/01/09 modified by Makoto Matsumoto */ this.mt[this.mti] >>>= 0; /* for >32 bit machines */ } } /** * generates a random number on [0,0xffffffff]-interval * @private */ private _nextInt32():number { var y:number; var mag01 = new Array(0x0, Random.MATRIX_A); /* mag01[x] = x * MATRIX_A for x=0,1 */ if (this.mti >= Random.N) { /* generate N words at one time */ var kk:number; if (this.mti == Random.N + 1) /* if init_genrand() has not been called, */ this.init_genrand(5489); /* a default initial seed is used */ for (kk = 0; kk < Random.N - Random.M; kk++) { y = (this.mt[kk] & Random.UPPER_MASK) | (this.mt[kk + 1] & Random.LOWER_MASK); this.mt[kk] = this.mt[kk + Random.M] ^ (y >>> 1) ^ mag01[y & 0x1]; } for (; kk < Random.N - 1; kk++) { y = (this.mt[kk] & Random.UPPER_MASK) | (this.mt[kk + 1] & Random.LOWER_MASK); this.mt[kk] = this.mt[kk + (Random.M - Random.N)] ^ (y >>> 1) ^ mag01[y & 0x1]; } y = (this.mt[Random.N - 1] & Random.UPPER_MASK) | (this.mt[0] & Random.LOWER_MASK); this.mt[Random.N - 1] = this.mt[Random.M - 1] ^ (y >>> 1) ^ mag01[y & 0x1]; this.mti = 0; } y = this.mt[this.mti++]; /* Tempering */ y ^= (y >>> 11); y ^= (y << 7) & 0x9d2c5680; y ^= (y << 15) & 0xefc60000; y ^= (y >>> 18); return y >>> 0; } /** * generates an int32 pseudo random number * @param range: an optional [from, to] range, if not specified the result will be in range [0,0xffffffff] * @return {number} */ nextInt32(range:[number, number] = null):number { var result = this._nextInt32(); if (range == null) { return result; } return (result % (range[1] - range[0])) + range[0]; } /** * generates a random number on [0,0x7fffffff]-interval */ nextInt31():number { return (this._nextInt32() >>> 1); } /** * generates a random number on [0,1]-real-interval */ nextNumber():number { return this._nextInt32() * (1.0 / 4294967295.0); } /** * generates a random number on [0,1) with 53-bit resolution */ nextNumber53():number { var a = this._nextInt32() >>> 5, b = this._nextInt32() >>> 6; return (a * 67108864.0 + b) * (1.0 / 9007199254740992.0); } }
вы можете использовать его следующим образом:
var random = new Random(132); random.nextInt32(); //return a pseudo random int32 number random.nextInt32([10,20]); //return a pseudo random int in range [10,20] random.nextNumber(); //return a a pseudo random number in range [0,1]
проверьте источник для нескольких способов.
Примечание: этот код был первоначально включен в вопрос выше. В интересах сохранения вопроса коротким и сосредоточенным, я переместил его в этот ответ сообщества Wiki.
Я нашел этот код ногами вокруг, и он, кажется, работает нормально для получения случайного числа, а затем с помощью семени позже, но я не совсем уверен, как работает логика (например, где 2345678901, 48271 & 2147483647 номера пришли от.)
function nextRandomNumber(){ var hi = this.seed / this.Q; var lo = this.seed % this.Q; var test = this.A * lo - this.R * hi; if(test > 0){ this.seed = test; } else { this.seed = test + this.M; } return (this.seed * this.oneOverM); } function RandomNumberGenerator(){ var d = new Date(); this.seed = 2345678901 + (d.getSeconds() * 0xFFFFFF) + (d.getMinutes() * 0xFFFF); this.A = 48271; this.M = 2147483647; this.Q = this.M / this.A; this.R = this.M % this.A; this.oneOverM = 1.0 / this.M; this.next = nextRandomNumber; return this; } function createRandomNumber(Min, Max){ var rand = new RandomNumberGenerator(); return Math.round((Max-Min) * rand.next() + Min); } //Thus I can now do: var letters = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z']; var numbers = ['1','2','3','4','5','6','7','8','9','10']; var colors = ['red','orange','yellow','green','blue','indigo','violet']; var first = letters[createRandomNumber(0, letters.length)]; var second = numbers[createRandomNumber(0, numbers.length)]; var third = colors[createRandomNumber(0, colors.length)]; alert("Today's show was brought to you by the letter: " + first + ", the number " + second + ", and the color " + third + "!"); /* If I could pass my own seed into the createRandomNumber(min, max, seed); function then I could reproduce a random output later if desired. */
хорошо, вот решение, на котором я остановился.
сначала вы создаете начальное значение с помощью функции " newseed ()". Затем вы передаете начальное значение функции " srandom ()". Наконец, функция "srandom ()" возвращает псевдослучайное значение между 0 и 1.
ключевой момент заключается в том, что значение хранится внутри массива. Если бы это было просто целое число или float, значение перезаписывалось бы каждый раз при вызове функции, так как значения целых чисел, floats, строки и так далее хранятся непосредственно в стеке, а не только указатели, как в случае массивов и других объектов. Таким образом, значение семени может оставаться постоянным.
наконец, можно определить функцию "srandom () "таким образом, что это метод объекта" Math", но я оставлю это до вас, чтобы выяснить. ;)
удачи!
JavaScript:
// Global variables used for the seeded random functions, below. var seedobja = 1103515245 var seedobjc = 12345 var seedobjm = 4294967295 //0x100000000 // Creates a new seed for seeded functions such as srandom(). function newseed(seednum) { return [seednum] } // Works like Math.random(), except you provide your own seed as the first argument. function srandom(seedobj) { seedobj[0] = (seedobj[0] * seedobja + seedobjc) % seedobjm return seedobj[0] / (seedobjm - 1) } // Store some test values in variables. var my_seed_value = newseed(230951) var my_random_value_1 = srandom(my_seed_value) var my_random_value_2 = srandom(my_seed_value) var my_random_value_3 = srandom(my_seed_value) // Print the values to console. Replace "WScript.Echo()" with "alert()" if inside a Web browser. WScript.Echo(my_random_value_1) WScript.Echo(my_random_value_2) WScript.Echo(my_random_value_3)
Lua 4 (моя личная цель окружающая среда):
-- Global variables used for the seeded random functions, below. seedobja = 1103515.245 seedobjc = 12345 seedobjm = 4294967.295 --0x100000000 -- Creates a new seed for seeded functions such as srandom(). function newseed(seednum) return {seednum} end -- Works like random(), except you provide your own seed as the first argument. function srandom(seedobj) seedobj[1] = mod(seedobj[1] * seedobja + seedobjc, seedobjm) return seedobj[1] / (seedobjm - 1) end -- Store some test values in variables. my_seed_value = newseed(230951) my_random_value_1 = srandom(my_seed_value) my_random_value_2 = srandom(my_seed_value) my_random_value_3 = srandom(my_seed_value) -- Print the values to console. print(my_random_value_1) print(my_random_value_2) print(my_random_value_3)