JavaScript Math.random()
함수는 0에서 1 사이의 임의의 값을 반환하며 현재 시간을 기반으로 자동 시드됩니다 (내가 믿는 Java와 유사). 그러나 나는 당신이 그것을 위해 자신의 씨앗을 설정하는 방법이 없다고 생각합니다.
고유 한 시드 값을 제공 할 수있는 난수 생성기를 어떻게 만들어서 반복 가능한 (의사) 난수 시퀀스를 생성 할 수 있습니까?
JavaScript Math.random()
함수는 0에서 1 사이의 임의의 값을 반환하며 현재 시간을 기반으로 자동 시드됩니다 (내가 믿는 Java와 유사). 그러나 나는 당신이 그것을 위해 자신의 씨앗을 설정하는 방법이 없다고 생각합니다.
고유 한 시드 값을 제공 할 수있는 난수 생성기를 어떻게 만들어서 반복 가능한 (의사) 난수 시퀀스를 생성 할 수 있습니까?
답변:
하나의 옵션은 http://davidbau.com/seedrandom 인데 이는 훌륭한 속성을 가진 시드 가능한 RC4 기반 Math.random () 드롭 인 대체입니다.
시딩 기능이 필요하지 않은 경우 Math.random()
주변에서 도우미 기능을 사용 하고 빌드하십시오 (예 :) randRange(start, end)
.
어떤 RNG를 사용하고 있는지 잘 모르겠지만 그 특성과 한계를 알기 위해 RNG를 알고 문서화하는 것이 가장 좋습니다.
Starkii가 말했듯이 Mersenne Twister는 좋은 PRNG이지만 구현하기 쉽지 않습니다. 당신이 그것을 원한다면 LCG를 구현해보십시오. 매우 쉽고, 괜찮은 임의의 특성 (Mersenne Twister만큼 좋지는 않음)을 가지고 있으며 인기있는 상수 중 일부를 사용할 수 있습니다.
편집 : LCG 옵션을 포함하여 짧은 시드 가능한 RNG 구현에 대한 이 답변 의 훌륭한 옵션을 고려하십시오 .
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));
this.a * this.state
은 2 ^ 53보다 큰 숫자를 초래할 수 있으므로 JavaScript의 정확한 정수에 대한 제한을 무효화합니다 . 결과는 제한된 출력 범위이며 일부 종자의 경우 매우 짧은 기간입니다. 또한 일반적으로 2의 거듭 제곱을 사용하면 m
몇 가지 명백한 패턴을 얻을 수 있습니다. 어쨌든 단순한 잘림이 아닌 모듈러스 연산을 소비하는 경우 소수를 사용하지 않을 이유가 없습니다.
시드를 지정하려면 getSeconds()
and에 대한 호출을 바꾸면됩니다 getMinutes()
. int를 전달하고 mod 60의 절반을 초 값으로 사용하고 나머지 절반 modulo 60을 사용하면 다른 부분을 얻을 수 있습니다.
즉,이 방법은 쓰레기처럼 보입니다. 적절한 난수 생성을 수행하는 것은 매우 어렵습니다. 이것의 명백한 문제는 난수 시드가 초와 분을 기반으로한다는 것입니다. 시드를 추측하고 난수 스트림을 재생성하려면 3600 개의 다른 초 및 분 조합 만 시도하면됩니다. 또한 3600 개의 다른 가능한 씨앗 만 있음을 의미합니다. 이것은 수정이 가능하지만 처음부터이 RNG가 의심됩니다.
더 나은 RNG를 사용하려면 Mersenne Twister를 시도하십시오 . 궤도가 우수하고 성능이 우수한 잘 테스트되고 상당히 강력한 RNG입니다.
편집 : 나는 정말로 정확해야하며 이것을 의사 난수 생성기 또는 PRNG라고합니다.
"산술 방법을 사용하여 난수를 생성하는 사람은 누구나 죄 상태입니다."
--- 존 폰 노이만
Mersenne Twister의 JavaScript 포트를 사용합니다. 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 개의 인수를 인수로 호출 할 수 있습니다. 값이 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.
이 발전기는 다음과 같은 속성을 나타냅니다.
mod
소수 인 값 출력의 간단한 패턴은 없다 어떠한 선택된 한계 상관 없다. 이것은 꽤 체계적인 패턴을 보이는 단순한 PRNG와는 다릅니다.for (var i = 0; i < 400; i++) { console.log("input: (" + i * 245 + ", " + i * 553 + ") | output: " + SeedRandom(i * 245, i * 553)(20)); }
Typescript로 프로그래밍하면 Christoph Henkelmann의 답변에 대한 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.
*/
RandomNumberGenerator
와 nextRandomNumber
기능은 실제로는 레머 / LCG RNG 있어야하는데 1996 년에 모든 방법을 다시 날짜. 32 비트 정수에 대해 모듈로 산술을 수행하기 위해 영리한 수학을 사용합니다. 그렇지 않으면 중간 값을 포함하기에는 너무 작습니다. 문제는 JavaScript가 32 비트 정수를 구현하지 않고 64 비트 부동 소수점을 구현한다는 것입니다.이 코드와 같이 나누기가 정수 나누기가 아니기 때문에 결과는 Lehmer 생성기가 아니라고 가정합니다. 무작위로 보이는 일부 결과를 생성하지만 Lehmer 생성기의 보장은 적용되지 않습니다.
createRandomNumber
함수는 나중에 추가되며 거의 모든 것을 잘못합니다. 특히 호출 할 때마다 새로운 RNG를 인스턴스화합니다. 즉, 빠른 연속 호출은 모두 동일한 부동 소수점을 사용합니다. 주어진 코드에서 그것은 거의 불가능하다 'a'
아무것도하지만,와 결합 될 '1'
및 'red'
.
좋아, 여기에 내가 정착 한 해결책이 있습니다.
먼저 "newseed ()"함수를 사용하여 시드 값을 만듭니다. 그런 다음 시드 값을 "srandom ()"함수에 전달합니다. 마지막으로 "srandom ()"함수는 0과 1 사이의 의사 난수 값을 반환합니다.
중요한 것은 시드 값이 배열 안에 저장된다는 것입니다. 단순히 정수 또는 부동 소수점 인 경우 정수, 부동 소수점, 문자열 등의 값은 배열의 경우와 같이 포인터 만 스택에 직접 저장되므로 함수가 호출 될 때마다 값을 덮어 씁니다. 다른 물체. 따라서 시드 값이 지속적으로 유지 될 수 있습니다.
마지막으로, "srandom ()"함수를 정의하여 "Math"객체의 메소드가되도록 할 수 있지만, 알아낼 수 있도록 남겨 두겠습니다. ;)
행운을 빕니다!
자바 스크립트 :
// 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)
루아 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)
seedobj[0] * seedobja
은 2 ^ 53보다 큰 숫자를 초래할 수 있으므로 JavaScript의 정확한 정수에 대한 제한을 무효화합니다 . 결과는 제한된 출력 범위이며 일부 종자의 경우 매우 짧은 기간입니다.