如何获得给定种子范围内的稳定随机数?

时间:2020-08-26 13:32:11

标签: javascript

假设我有

  • 种子-整数,例如1200303
  • 目标值[1,20]的范围

是否存在一种数学方法来获取由种子确定的[1, 20]范围内的随机数?

rand(seed int, range [int, int]);
rand(1, [1, 20]); // "4"
rand(1, [1, 20]); // "4" (if seed and range is the same, I expect the same value)
rand(1, [1, 21]); // "6" (changing range changes random number)
rand(2, [1, 20]); // "9" (changing seed changes random number)

2 个答案:

答案 0 :(得分:1)

结果是,我一直在寻找所谓的伪随机数生成器(PRNG)。

有许多PRNG实现。 Mersenne Twister最受欢迎,但安全性也很弱。

我和https://www.npmjs.com/package/seeded-rand一起去了

const SEED = 123,
const LOWER_RANGE = 1;
const UPPER_RANGE = 100;

const srand = new Srand(SEED_ID);
const gifId = srand.intInRange(LOWER_RANGE, UPPER_RANGE);

这将提供1到100之间的确定性数字,即,多个程序执行将在给定相同种子的情况下返回相同数字。


更新

结果是我最初的尝试并不像我想象的那样随机。

https://github.com/DomenicoDeFelice/jsrand/issues/3

解决方案是先对种子进行哈希处理,例如

import {
  createHash,
} from 'crypto';
import Srand from 'seeded-rand';

const seed = createHash('sha1').update(String(SEED)).digest().readUInt32BE();

new Srand(seed).intInRange(1, UPPER_RANGE);

如果每个种子仅需要1个确定的随机值,则这里根本不需要随机函数。只需seed % UPPER_RANGE(见下文)即可。


许多PRNG允许您指定输出范围。如果不是,则通常为0(含)到1(不含),基本上就足够了:

let range = (max - min);

Math.floor(value * range) + min

如果您采用的是这种方法,请确保仔细检查所选的实现是否不包含1(包括1),因为那样的话,它有时会超出最大值。

此外,如果您对可识别的可预测性模式还可以,那么您可以使用SEED % UPPER_RANGE,即

123 % 20; // "3"
124 % 20; // "4"
// ...
164 % 20; // "4"

感谢Freenode的 joepie91 的指导。我只是在记录他的建议。

答案 1 :(得分:0)

我写了一个概念,说明如何使用简单的数学和循环来做到这一点。我相信细节可以改善,但是总的来说,您是否想要这样的东西?

// Seed random (pseudo-random)
const seedRand = (num, range) => {
  // Generate number multiplication
  num = String(num).split('').reduce((c, n) => (n != 0) ? c * n : c * c);
  // Transform to match range
  let s = range[0],
      e = range[1]
  while(num < range[0] || num > range[1]) {
    if(num > range[1]) num = Math.floor(num / e--);
    if(num < range[0]) num = Math.floor(num * s++);
  }
  // Return value
  return num;
}

// Test
console.log(seedRand(1220501, [1, 20]));
console.log(seedRand(1520502, [1, 20]));
console.log(seedRand(1520502, [1, 20])); // Same seed as previous one
console.log(seedRand(1370503, [1, 20]));
console.log(seedRand(1370503, [1, 21])); // Same seed as previous one, different range