迹忆客 专注技术分享

当前位置:主页 > 学无止境 > WEB前端 > JavaScript >

在 JavaScript 中使用种子生成随机数

作者:迹忆客 最近更新:2023/06/05 浏览次数:

本文介绍如何使用种子从 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

转载请发邮件至 1244347461@qq.com 进行申请,经作者同意之后,转载请以链接形式注明出处

本文地址:

相关文章

Do you understand JavaScript closures?

发布时间:2025/02/21 浏览次数:108 分类:JavaScript

The function of a closure can be inferred from its name, suggesting that it is related to the concept of scope. A closure itself is a core concept in JavaScript, and being a core concept, it is naturally also a difficult one.

Do you know about the hidden traps in variables in JavaScript?

发布时间:2025/02/21 浏览次数:178 分类:JavaScript

Whether you're just starting to learn JavaScript or have been using it for a long time, I believe you'll encounter some traps related to JavaScript variable scope. The goal is to identify these traps before you fall into them, in order to av

How much do you know about the Prototype Chain?

发布时间:2025/02/21 浏览次数:150 分类:JavaScript

The prototype chain can be considered one of the core features of JavaScript, and certainly one of its more challenging aspects. If you've learned other object-oriented programming languages, you may find it somewhat confusing when you start

用 jQuery 检查复选框是否被选中

发布时间:2024/03/24 浏览次数:102 分类:JavaScript

在本教程中学习 jQuery 检查复选框是否被选中的所有很酷的方法。我们展示了使用直接 DOM 操作、提取 JavaScript 属性的 jQuery 方法以及使用 jQuery 选择器的不同方法。你还将找到许多有用的

扫一扫阅读全部技术教程

社交账号
  • https://www.github.com/onmpw
  • qq:1244347461

最新推荐

教程更新

热门标签

扫码一下
查看教程更方便