在 JavaScript 中使用种子生成随机数
本文介绍如何使用种子从 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 时请记住以下几点。
-
您使用
new Math.seedrandom('seed key')
初始化 seedrandom。 - 您可以使用 Seedrandom 的 quick() 函数来生成 32 位随机数。
-
Seedrandom.js 的
int32()
函数返回一个 32 位有符号整数。 - 调用不带参数的 seedrandom 会导致创建一个自动播种的基于 ARC4 的 PRNG。 自动播种使用一些值,如累积的局部熵。
- Seedrandom 可以将一个对象作为第二个参数。 这个对象是 {entropy: true},结果是不可预测的。
- 在不使用 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
相关文章
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 选择器的不同方法。你还将找到许多有用的
jQuery 中的 Window.onload 与 $(document).ready
发布时间:2024/03/24 浏览次数:180 分类:JavaScript
-
本教程演示了如何在 jQuery 中使用 Window.onload 和 $(document).ready 事件。