Ответ 1
Один из вариантов - http://davidbau.com/seedrandom, который представляет собой замену замены Math.random() на основе RC4 с хорошими свойствами.
Функция JavaScript Math.random()
возвращает случайное значение от 0 до 1, автоматически посеяно на основе текущего времени (похоже на Java, я верю). Тем не менее, я не думаю, что есть способ установить для вас собственное семя.
Как я могу создать генератор случайных чисел, чтобы я мог предоставить свое собственное начальное значение, так что я могу заставить его воспроизводить последовательность (псевдо) случайных чисел?
Один из вариантов - http://davidbau.com/seedrandom, который представляет собой замену замены Math.random() на основе RC4 с хорошими свойствами.
Если вы хотите указать семя, вам просто нужно заменить вызовы на getSeconds()
и getMinutes()
. Вы можете пройти в int и использовать половину этого mod 60 для значения секунд, а другую половину по модулю 60, чтобы предоставить вам другую часть.
При этом этот метод выглядит как мусор. Выполнение правильного генерации случайных чисел очень сложно. Очевидная проблема заключается в том, что количество случайных чисел основано на секундах и минутах. Чтобы угадать семя и воссоздать поток случайных чисел, требуется только 3600 различных комбинаций секунд и минут. Это также означает, что существует только 3600 различных возможных семян. Это можно исправить, но с самого начала я бы с подозрением относился к этому RNG.
Если вы хотите использовать лучший RNG, попробуйте Mersenne Twister. Это хорошо протестированный и довольно надежный RNG с огромной орбитой и отличной производительностью.
EDIT: Я действительно должен быть прав и ссылаюсь на это как генератор псевдослучайных чисел или PRNG.
"Любой, кто использует арифметические методы для создания случайных чисел, находится в состоянии греха".
--- Джон фон Нейман
если вам не нужна возможность посева, просто используйте Math.random()
и создайте вспомогательные функции вокруг него (например, randRange(start, end)
).
Я не уверен, что вы используете RNG, но лучше всего знать и документировать его, чтобы вы знали о его характеристиках и ограничениях.
Как сказал Старки, Мерсенн Твистер - хороший PRNG, но его непросто реализовать. Если вы хотите сделать это, попробуйте внедрить LCG - это очень просто, имеет приличные качества случайности (не так хорошо, как Mersenne Twister), и вы могут использовать некоторые из популярных констант.
function RNG(seed) {
// LCG using GCC 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));
Я использую порт JavaScript для Mersenne Twister: https://gist.github.com/300494 Это позволяет вам установить семя вручную. Кроме того, как упоминалось в других ответах, Mersenne Twister - действительно хороший PRNG.
Код, который вы указали, выглядит как Lehmer RNG. Если это так, то 2147483647
- это самое большое 32-разрядное целое число со знаком, 2147483647
- наибольшее 32-битное простое, а 48271
- множитель с полным периодом, который используется для генерации чисел.
Если это так, вы можете изменить RandomNumberGenerator
, чтобы взять дополнительный параметр seed
, а затем установить this.seed
в seed
; но вы должны быть осторожны, чтобы убедиться, что семя приведет к хорошему распределению случайных чисел (Lehmer может быть странным) - но большинство семян будет в порядке.
Ниже приведен PRNG, на который может быть отправлено пользовательское семя. Вызов SeedRandom
возвращает случайную функцию генератора. SeedRandom
может быть вызван без аргументов для того, чтобы засеять возвращенную случайную функцию с текущим временем, или ее можно вызвать с 1 или 2 неотрицательными интервалами в качестве аргументов, чтобы засеять их этими целыми числами. Благодаря посещению точки плавающей запятой только с одним значением, только генератор может быть инициирован в одном из 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.
Этот генератор обладает следующими свойствами:
mod
, являющихся штрихами, на выходе нет простого шаблона, независимо от выбранного предела. Это отличается от некоторых более простых PRNG, которые демонстрируют некоторые довольно систематические шаблоны.Если вы программируете в Typescript, я адаптировал реализацию Mersenne Twister, которая была принесена в Christoph Henkelmann, отвечая на этот поток как класс 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 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.
*/
Просто параметризуйте конструктор и установите семя:
function RandomNumberGenerator(Seed){
var d = new Date();
this.seed = Seed;
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(Seed, Min, Max){
var rand = new RandomNumberGenerator(Seed);
return Math.round((Max-Min) * rand.next() + Min);
}
И вызывается вот так:
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 seed = <generate seed>;
var first = createRandomNumber(seed, 0, letters.length);
var second = createRandomNumber(seed, 0, numbers.length);
var third = createRandomNumber(seed, 0, colors.length);
ОК, вот решение, на котором я остановился.
Сначала вы создаете начальное значение, используя функцию "newseed()". Затем вы передаете начальное значение функции "srandom()" . Наконец, функция "srandom()" возвращает псевдослучайное значение от 0 до 1.
Ключевым битом является то, что начальное значение хранится внутри массива. Если бы это было просто целое число или float, значение будет перезаписываться каждый раз при вызове функции, поскольку значения целых чисел, чисел с плавающей запятой, строк и т.д. Хранятся непосредственно в стеке, а не только указатели, как в случае массивов и другие объекты. Таким образом, возможно, что значение семени останется стойким.
Наконец, можно определить функцию "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)