Генератор случайных чисел 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)