First of all, remember to seed your PRNGs properly. 下面的大多数生成器没有内置种子设定程序,但接受一个或多个32位值来初始化PRNG的状态 . 而不是只使用低熵的"1"或"123"播种并且可能导致相关性(这不好),最好使用分布均匀的数据初始化PRNG .
function xfnv1a(str) {
for(var i = 0, h = 2166136261 >>> 0; i < str.length; i++)
h = Math.imul(h ^ str.charCodeAt(i), 16777619);
return function() {
h += h << 13; h ^= h >>> 7;
h += h << 3; h ^= h >>> 17;
return (h += h << 5) >>> 0;
}
}
// Create a xfnv1a state:
var seed = xfnv1a("apples");
// Output four 32-bit hashes to provide the seed for sfc32.
var rand = sfc32(seed(), seed(), seed(), seed());
// Or: output one 32-bit hash to provide the seed for mulberry32.
var rand = mulberry32(seed());
// Obtain sequential random numbers like so:
rand();
rand();
function sfc32(a, b, c, d) {
return function() {
a >>>= 0; b >>>= 0; c >>>= 0; d >>>= 0;
var t = (a + b) | 0;
a = b ^ b >>> 9;
b = c + (c << 3) | 0;
c = (c << 21 | c >>> 11);
d = d + 1 | 0;
t = t + d | 0;
c = c + t | 0;
return (t >>> 0) / 4294967296;
}
}
function mulberry32(a) {
return function() {
var t = a += 0x6D2B79F5;
t = Math.imul(t ^ t >>> 15, t | 1);
t ^= t + Math.imul(t ^ t >>> 7, t | 61);
return ((t ^ t >>> 14) >>> 0) / 4294967296;
}
}
function xoshiro128ss(a, b, c, d) {
return function() {
var t = b << 9, r = a * 5; r = (r << 7 | r >>> 25) * 9;
c ^= a; d ^= b;
b ^= c; a ^= d; c ^= t;
d = d << 11 | d >>> 21;
return (r >>> 0) / 4294967296;
}
}
12 回答
不,它不是,但是编写自己的生成器相当容易,或者更好地使用现有的生成器 . 退房:this related question .
另请参阅David Bau的博客more information on seeding .
注意:尽管(或者更确切地说,由于)简洁和明显的优雅,这种算法在随机性方面绝不是高质量的算法 . 寻找例如this answer中列出的那些更好的结果 .
(最初改编自对另一个答案的评论中提出的一个聪明的想法 . )
您可以将
seed
设置为任意数字,只需避免零(或Math.PI的任意倍数) .在我看来,这个解决方案的优雅来自缺少任何"magic"数字(除了10000,代表你必须扔掉以避免奇怪模式的最小位数 - 请参阅值为10,100,1000的结果) . 简洁也很好 .
它比Math.random()稍微慢一点(比例为2或3),但我相信它与用JavaScript编写的任何其他解决方案一样快 .
不,但这是一个简单的伪随机生成器,Multiply-with-carry的实现我改编自Wikipedia(之后已被删除):
编辑:通过使其重置m_z来修复种子功能
编辑2:严重的实施缺陷已得到修复
我在纯JavaScript中实现了许多优秀,简短,快速的可复制PRNG函数 . 所有这些都可以播种并提供高质量的数字 .
First of all, remember to seed your PRNGs properly. 下面的大多数生成器没有内置种子设定程序,但接受一个或多个32位值来初始化PRNG的状态 . 而不是只使用低熵的"1"或"123"播种并且可能导致相关性(这不好),最好使用分布均匀的数据初始化PRNG .
值得庆幸的是,哈希算法非常擅长从短字符串生成PRNG的种子 . 即使两个字符串相似,良好的散列函数也会产生非常不同的结果 . 这是一个基于FNV1a-Mulvey哈希的简单示例:
此功能使用Bret Mulvey的modified FNV1a 32位散列函数 . 对返回函数的每次后续调用都会生成一个新的"random"哈希值,以用作PRNG中的种子 .
以下是您可以使用它的方法:
这当然是功能性的JS,但它可以被客观化 .
向货物(发电机)前进 .
sfc32
这个gem来自PractRand随机数测试套件,它可以毫无问题地通过 . PractRand据称甚至比TestU01更严格 . 它在JS中也非常快(xoshiro128 **稍快,但质量更差) . 这可能是我选择的PRNG .
Mulberry32
Mulberry32也非常快,质量很好(作者声称它通过了所有gjrand的测试) . 如果你只需要一个简单但不错的PRNG,我会推荐这个 .
它具有32位的状态和232的完整周期 . 如果你只想用一个32位整数播种而不关心birthday problem,这是理想的选择 . 与sfc32 / xoshiro128 **中的340亿人相比,有43亿州 .
xoshiro128 **
截至2018年5月,
xoshiro128**
是xorshift家族的新成员 . 它提供128位状态,速度超快 .这个PRNG是Blackman / Vigna的最新版本,他也使用了谷歌Chrome中的xorshift128和xoroshiro . 它是少数现代32位PRNG之一 . xoroshiro64**也很有希望,但只有64位状态,并且很大程度上被xoshiro取代 .
使用如下:
作者声称它很好地通过了随机性测试(albeit with caveats) . 其他研究人员指出,在BigCrush(特别是LinearComp和BinaryRank)中进行了一些测试失败 . 但实际上并不重要,特别是如果将32位值转换为0-1之间的浮点数,就像这些PRNG一样 . 但是,如果您以笨拙的方式依赖低位,则可能会导致问题 .
JSF
这是Bob Jenkins(2007)的JSF或
smallprng
,是制作ISAAC和SpookyHash的人 . 它在PractRand上测试performs well应该非常快 . 假设平均周期长度为2 ^ 126但尚未正式确定 .此版本不需要单独的种子功能 . 但结果是,只有32位可以播种,并且该版本运行预先运行jsf()20次以分散初始状态,这可能是昂贵的 .
像这样使用:
如果需要,可以直接初始化整个128位状态并删除for循环 . 我决定保留原始结构,因为作者验证了给定配置中每个可能的32位种子的周期长度 .
Lehmer LCG
这个仅用于提供其他答案中提到的选项的更好替代方案,例如
Math.sin
或Math.PI
方法,这些方法在不同平台上不太一致或可靠 . 这个LCG实现是 extremely fast 但只有一个31位的状态,并且未能通过前面提到的生成器通过飞行颜色的一些统计测试 .虽然这是一个单行 - 这很好:) . 这是Park–Miller in 1988 & 1993提出的最小标准RNG,并在C11中实现为
minstd_rand
. 请记住,状态和周期仅为31位(31位给出20亿个可能状态,32位给出两倍) . 这是其他人试图取代的PRNG类型 .除非你真的需要速度并且不关心随机性质量(无论是什么是随机的?)或31位状态/周期大小,否则它会使用它 . 非常适合游戏果酱或演示或其他东西 .
使用如下:
似乎还有其他乘法器可以使您获得完整的32位状态 . 我不知道这些是否比Park-Miller更好/更差,但在这里它们是完整的 .
这些乘数来自:P . L'Ecuyer:1997年4月30日的不同尺寸和良好晶格结构的线性同余发电机表 .
AnttiSykäri的算法很简单 . 当你调用Math.seed(s)时,我最初做了一个替换Javascript的Math.random的变体,但是后来Jason评论说返回函数会更好:
这为您提供了Javascript不具备的另一项功能:多个独立的随机生成器 . 如果您希望同时运行多个可重复的模拟,那么这一点尤为重要 .
请参阅Pierre L'Ecuyer的工作,可追溯到20世纪80年代末和90年代初 . 还有其他人 . 如果您不是专家,自己创建一个(伪)随机数生成器是非常危险的,因为很可能结果不是统计随机的或者是一个小周期 . 皮埃尔(和其他人)已经将一些易于实现的好(伪)随机数生成器组合在一起 . 我使用他的一个LFSR发电机 .
https://www.iro.umontreal.ca/~lecuyer/myftp/papers/handstat.pdf
菲尔特洛伊
编写自己的伪随机生成器非常简单 .
Dave Scotese的建议很有用,但正如其他人所指出的那样,它的分布并不均匀 .
但是,这不是因为sin的整数参数 . 这只是因为罪的范围,恰好是一个圆的一维投影 . 如果你采取圆的角度,它将是均匀的 .
因此,而不是sin(x)使用arg(exp(i * x))/(2 * PI) .
如果您不喜欢线性顺序,请将其与xor混合一点 . 实际因素也无关紧要 .
要生成n个伪随机数,可以使用以下代码:
另请注意,当需要真正的熵时,不能使用伪随机序列 .
如今,许多在Javascript中需要可播种随机数生成器的人正在使用David Bau's seedrandom module .
结合以前的一些答案,这是您正在寻找的可种子随机函数:
我编写了一个返回种子随机数的函数,它使用Math.sin来获得一个长随机数并使用种子从中选择数字 .
使用 :
它将返回您的种子数,第一个参数是任何字符串值;你的种子 . 第二个参数是将返回多少位数 .
固定种子的简单方法:
对于0到100之间的数字 .