如何在 javascript 中使用种子生成随机数

文章目录

    • 使用种子和 SFC32 生成随机数
    • 使用种子和 Mulberry32 生成随机数
    • 使用种子和 Xoshiro128** 生成随机数
    • 使用种子和 JSF 生成随机数
    • 使用 seedrandom.js 生成随机数


本文介绍如何使用种子从 PRNG 生成随机数。 同时,确保 PRNG 的种子具有高熵是最佳实践。

因此,我们将使用哈希函数来生成种子。 之后,我们将种子传递给 PRNG。


使用种子和 SFC32 生成随机数

SFC32 或 Simple Fast Counter 是 PractRand 的快速 PRNG(主要使用 C 语言),它在 JavaScript 中有一个 128 位状态的实现,而且速度非常快。 SFC32 至少需要一个种子来生成随机数。

我们将使用哈希函数生成这个种子,这是 MurmurHash3 的 JavaScript 实现,它需要一个初始字符串来生成种子。 结果,我们传入了一个字符串。

在下面的代码中,我们生成种子并将其传递给返回随机数的 SFC32。

代码:

// Define the Murmur3Hash function
function MurmurHash3(string) {
    let i = 0;
    for (i, hash = 1779033703 ^ string.length; i < string.length; i++) {
        let bitwise_xor_from_character = hash ^ string.charCodeAt(i);
        hash = Math.imul(bitwise_xor_from_character, 3432918353);
        hash = hash << 13 | hash >>> 19;
    } return () => {
       // Return the hash that you can use as a seed
        hash = Math.imul(hash ^ (hash >>> 16), 2246822507);
        hash = Math.imul(hash ^ (hash >>> 13), 3266489909);
        return (hash ^= hash >>> 16) >>> 0;
    }
}

function SimpleFastCounter32(seed_1, seed_2, seed_3, seed_4) {
    return () => {
      seed_1 >>>= 0; seed_2 >>>= 0; seed_3 >>>= 0; seed_4 >>>= 0;
      let cast32 = (seed_1 + seed_2) | 0;
      seed_1 = seed_2 ^ seed_2 >>> 9;
      seed_2 = seed_3 + (seed_3 << 3) | 0;
      seed_3 = (seed_3 << 21 | seed_3 >>> 11);
      seed_4 = seed_4 + 1 | 0;
      cast32 = cast32 + seed_4 | 0;
      seed_3 = seed_3 + cast32 | 0;
      return (cast32 >>> 0) / 4294967296;
    }
}

let generate_seed = MurmurHash3("String for the Seed Key");
let random_number = SimpleFastCounter32(generate_seed(), generate_seed());
console.log(random_number());
console.log(random_number());

输出:

0.837073584087193
0.3599331611767411

使用种子和 Mulberry32 生成随机数

Mulberry32 也是一个 PRNG,尽管代码结构比 SFC32 更简单。 与至少需要一个种子的 SFC32 相反。

我们将使用 MurmurHash3 使用字符串生成种子。 在以下示例中,我们使用 for 循环和 Mulberry32 生成五个随机数。

代码:

// Define the Murmur3Hash function
function MurmurHash3(string) {
    let i = 0;
    for (i, hash = 1779033703 ^ string.length; i < string.length; i++) {
        let bitwise_xor_from_character = hash ^ string.charCodeAt(i);
        hash = Math.imul(bitwise_xor_from_character, 3432918353);
        hash = hash << 13 | hash >>> 19;
    } return () => {
       // Return the hash that you can use as a seed
        hash = Math.imul(hash ^ (hash >>> 16), 2246822507);
        hash = Math.imul(hash ^ (hash >>> 13), 3266489909);
        return (hash ^= hash >>> 16) >>> 0;
    }
}

function Mulberry32(string) {
    return () => {
        let for_bit32_mul = string += 0x6D2B79F5;
        let cast32_one = for_bit32_mul ^ for_bit32_mul >>> 15;
        let cast32_two = for_bit32_mul | 1;
        for_bit32_mul = Math.imul(cast32_one, cast32_two);
        for_bit32_mul ^= for_bit32_mul + Math.imul(for_bit32_mul ^ for_bit32_mul >>> 7, for_bit32_mul | 61);
        return ((for_bit32_mul ^ for_bit32_mul >>> 14) >>> 0) / 4294967296;
    }
}

let generate_seed = MurmurHash3("String for the Seed Key");
let random_number = Mulberry32(generate_seed());

for (let i = 0; i < 5; i++) {
    console.log(random_number());
}

输出:

0.13532060221768916
0.8630009586922824
0.53870237339288
0.5237146227154881
0.8748106376733631

使用种子和 Xoshiro128** 生成随机数

Vagna 教授和 Blackman 开发了 Xoshiro128** 发生器。 xorshift128 是 Xorshift PRNG 的一个家族,它是最快的 PRNG。

与 SFC32 一样,Xoshiro128** 可以在生成随机数之前至少获取一个种子。 在以下代码片段中,我们使用 MurmiurHash3 创建了所需的种子。

代码:

// Define the Murmur3Hash function
function MurmurHash3(string) {
    let i = 0;
    for (i, hash = 1779033703 ^ string.length; i < string.length; i++) {
        let bitwise_xor_from_character = hash ^ string.charCodeAt(i);
        hash = Math.imul(bitwise_xor_from_character, 3432918353);
        hash = hash << 13 | hash >>> 19;
    } return () => {
       // Return the hash that you can use as a seed
        hash = Math.imul(hash ^ (hash >>> 16), 2246822507);
        hash = Math.imul(hash ^ (hash >>> 13), 3266489909);
        return (hash ^= hash >>> 16) >>> 0;
    }
}

function Xoshiro128_twostar(seed_1, seed_2, seed_3, seed_4) {
    return () => {
        let t = seed_2 << 9, y = seed_1 * 5; y = (y << 7 | y >>> 25) * 9;
        seed_3 ^= seed_1; seed_4 ^= seed_2;
        seed_2 ^= seed_3; seed_1 ^= seed_4; seed_3 ^= t;
        seed_4 = seed_4 << 11 | seed_4 >>> 21;
        return (y >>> 0) / 4294967296;
    }
}

let generate_seed = MurmurHash3("String for the Seed Key");
let random_number = Xoshiro128_twostar(generate_seed(), generate_seed());
console.log(random_number());

输出:

0.6150987280998379

使用种子和 JSF 生成随机数

Bob Jenkins 创建了 Jenkins Small Fast (JSF) 生成器,这是一种快速生成器。 不过,与 SFC32 相比,它并不快。

当您观察 JSF 的代码时,您会发现它与 SFC32 的相似之处。 在生成随机数之前,JSF 可以采用多个种子。

我们在下一个代码中使用种子和 JSF 生成十个随机数。

代码:

// Define the Murmur3Hash function
function MurmurHash3(string) {
    let i = 0;
    for (i, hash = 1779033703 ^ string.length; i < string.length; i++) {
        let bitwise_xor_from_character = hash ^ string.charCodeAt(i);
        hash = Math.imul(bitwise_xor_from_character, 3432918353);
        hash = hash << 13 | hash >>> 19;
    } return () => {
       // Return the hash that you can use as a seed
        hash = Math.imul(hash ^ (hash >>> 16), 2246822507);
        hash = Math.imul(hash ^ (hash >>> 13), 3266489909);
        return (hash ^= hash >>> 16) >>> 0;
    }
}

function JenkinsSimpleFast32(seed_1, seed_2, seed_3, seed_4) {
    return () => {
        seed_1 |= 0; seed_2 |= 0; seed_3 |= 0; seed_4 |= 0;
        let t = seed_1 - (seed_2 << 27 | seed_2 >>> 5) | 0;
        seed_1 = seed_2 ^ (seed_3 << 17 | seed_3 >>> 15);
        seed_2 = seed_3 + seed_4 | 0;
        seed_3 = seed_4 + t | 0;
        seed_4 = seed_1 + t | 0;
        return (seed_4 >>> 0) / 4294967296;
    }
}

let generate_seed = MurmurHash3("String for the Seed Key");
let random_number = JenkinsSimpleFast32(generate_seed(), generate_seed());
for (let i = 0; i < 10; i++) {
    console.log(random_number());
}

输出:

0.513338076416403
0.4737987464759499
0.5743723993655294
0.4811882192734629
0.07753282226622105
0.11416710214689374
0.1270705321803689
0.15759771666489542
0.16906401910819113
0.6846413582097739

使用 seedrandom.js 生成随机数

seedrandom.js 是 David Bau 为种子随机数生成器 (RNG) 设计的库,可在 NPM 和 CDNJS 上使用。 对于本文,我们将使用 CDNJS。

使用 Seedrandom.js 时请记住以下几点。

  1. 您使用 new Math.seedrandom('seed key') 初始化 seedrandom。
  2. 您可以使用 Seedrandom 的 quick() 函数来生成 32 位随机数。
  3. Seedrandom.js 的 int32() 函数返回一个 32 位有符号整数。
  4. 调用不带参数的 seedrandom 会导致创建一个自动播种的基于 ARC4 的 PRNG。 自动播种使用一些值,如累积的局部熵。
  5. Seedrandom 可以将一个对象作为第二个参数。 这个对象是 {entropy: true},结果是不可预测的。
  6. 在不使用 new 关键字的情况下调用 Math.seedrandom 会替换默认的 Math.random()。 替换为新的 Math.seedrandom()。

在这个例子中,我们从 CDNJS 导入了 seedrandom.js。 之后,我们使用它通过种子生成随机数。

代码:

<body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/seedrandom/3.0.5/seedrandom.min.js">script>
    <script>
        let generate_random_number = new Math.seedrandom('Johnson');
        console.log(generate_random_number());
    script>
body>

输出:

0.08103389758898699

我们可以将种子与累积的熵混合。 您需要传递 {entropy: true} 作为 seedrandom 的第二个参数。

代码:

<body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/seedrandom/3.0.5/seedrandom.min.js">script>
    <script>
        let generate_random_number = new Math.seedrandom('Antananarivo', { entropy: true });
        for (let i = 0; i < 5; i++) {
            console.log(generate_random_number());
        }
    script>
body>

输出:

0.8478730572111559
0.963664252064149
0.6002684820777331
0.4026776455839767
0.7579996916288508

此外,quick()int32() 将返回随机的 32 位随机数。 前者将返回一个浮点数,而后者返回一个有符号整数。

代码:

<body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/seedrandom/3.0.5/seedrandom.min.js">script>
    <script>
        let generate_random_number = new Math.seedrandom('DavidBau');
        console.log("With quick():", generate_random_number.quick());
        console.log("With int32():", generate_random_number.int32());
    script>
body>

输出:

With quick(): 0.249648863915354
With int32(): -550219731

你可能感兴趣的:(前端,JavaScript,javascript,开发语言)