<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh-Hans-CN">
	<id>http://wiki.foofun.cn//index.php?action=history&amp;feed=atom&amp;title=Random_Number_Generator</id>
	<title>Random Number Generator - 版本历史</title>
	<link rel="self" type="application/atom+xml" href="http://wiki.foofun.cn//index.php?action=history&amp;feed=atom&amp;title=Random_Number_Generator"/>
	<link rel="alternate" type="text/html" href="http://wiki.foofun.cn//index.php?title=Random_Number_Generator&amp;action=history"/>
	<updated>2026-04-07T02:28:37Z</updated>
	<subtitle>本wiki上该页面的版本历史</subtitle>
	<generator>MediaWiki 1.37.1</generator>
	<entry>
		<id>http://wiki.foofun.cn//index.php?title=Random_Number_Generator&amp;diff=689&amp;oldid=prev</id>
		<title>Zhang3：创建页面，内容为“随机数生成器（RNG）可以用很多不同的方式实现。 本文解释了其中一些方式。   ==熵（Entropy）== 计算机是确定性设备。 如果程序相同且所有输入相同，则每次计算的结果都相同。 那么，计算机如何生成随机数呢？ 计算机不可以是随机的，但它周围的物理世界可以。 许多物理事件在某种程度上是随机的，或者更严格地说，具有某种程度的熵。 即使在…”</title>
		<link rel="alternate" type="text/html" href="http://wiki.foofun.cn//index.php?title=Random_Number_Generator&amp;diff=689&amp;oldid=prev"/>
		<updated>2022-02-28T06:36:18Z</updated>

		<summary type="html">&lt;p&gt;创建页面，内容为“随机数生成器（RNG）可以用很多不同的方式实现。 本文解释了其中一些方式。   ==熵（Entropy）== 计算机是确定性设备。 如果程序相同且所有输入相同，则每次计算的结果都相同。 那么，计算机如何生成随机数呢？ 计算机不可以是随机的，但它周围的物理世界可以。 许多物理事件在某种程度上是随机的，或者更严格地说，具有某种程度的熵。 即使在…”&lt;/p&gt;
&lt;p&gt;&lt;b&gt;新页面&lt;/b&gt;&lt;/p&gt;&lt;div&gt;随机数生成器（RNG）可以用很多不同的方式实现。 本文解释了其中一些方式。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==熵（Entropy）==&lt;br /&gt;
计算机是确定性设备。 如果程序相同且所有输入相同，则每次计算的结果都相同。 那么，计算机如何生成随机数呢？ 计算机不可以是随机的，但它周围的物理世界可以。 许多物理事件在某种程度上是随机的，或者更严格地说，具有某种程度的熵。 即使在地球上最好的屏蔽录音室里，录音也会包含一定程度的噪音。 人类在键盘上输入的时间不一致，并且输入准随机文本，比如你正在阅读的文章。 甚至其他计算机也可以提供熵：虽然大多数网络流量完全是机器生成的，但最终网络数据包的确切相对时间来自按下电源按钮的那一刻，这是另一种人类行为。 因此，输入事件的时间和性质可以为我们提供一些熵。&lt;br /&gt;
&lt;br /&gt;
==随机数生成器的类型==&lt;br /&gt;
&lt;br /&gt;
有多种方法可以划分随机数生成器；一种是通过数字的生成方式，另一种是通过数字的使用方式。 这些是重要的区别：前者可能会影响操作系统的设计，后者可能会影响系统的安全性。&lt;br /&gt;
&lt;br /&gt;
===随机数的来源===&lt;br /&gt;
&lt;br /&gt;
有两种随机数：“真”随机数和伪随机数。&lt;br /&gt;
&lt;br /&gt;
对于“真”随机数生成，系统持续测量预期为随机的特定事件集。 这可以是宇宙辐射和原子衰变，也可以是用户输入的时间和时钟抖动。 测量是去偏（de-biased）的，并“搅拌”到一个&amp;lt;em&amp;gt;熵池中，从中可以提取随机数。&lt;br /&gt;
&lt;br /&gt;
伪随机数由一种算法（&amp;lt;em&amp;gt;PRNG&amp;lt;/em&amp;gt;）生成，该算法转换一些内部状态，并根据请求计算输出值。 可以设置初始种子，但在此之后，下一个状态仅取决于前一个状态。 有许多不同的PRNG，下面讨论其中一些。&lt;br /&gt;
&lt;br /&gt;
可以使用一些“真”随机数来为伪随机生成器的状态设定种子，但这并不能使PRNG“真随机”。 根据具体的算法，预测所有下一个输出可能与预测前一个输出一样简单。 正如下一节所讨论的，这可能会产生严重的影响。&lt;br /&gt;
&lt;br /&gt;
===随机数的应用===&lt;br /&gt;
&lt;br /&gt;
大致有两种应用：密码学和其他方面。&lt;br /&gt;
&lt;br /&gt;
为了理解以下部分，我们首先应该回顾一下关于随机数生成器的加密安全意味着什么。 密码学家将安全分为两类：信息论安全和计算安全。 前者是更有力的主张。 如果密码系统在理论上是安全的，那么攻击者在不拥有密钥的情况下，就不能得出任何关于加密消息的信息。 这种密码系统很少见。 由于攻击者不能得出“任何东西”，因此必须隐藏所有内容，包括模式、时间甚至长度。 一个人怎么能隐藏信息的长度呢？ 唯一的选择是根本不发送消息，这违背了密码系统的目的，或者发送无限长的消息。 所以一旦你开始发送，你就永远不会停止。&lt;br /&gt;
&lt;br /&gt;
这就是为什么计算安全性较弱，但更实用的一种：密文必须很难找到，“给定一些合理的计算能力”。 选择这一限制的目的是希望在未来几十年内不会达到。 这还允许用户“泄露”有关密文的一些信息（例如，长度），只要找到原始输入仍然困难。 这些系统通常使用一些很难反转的操作，除非已知密钥，例如[https://en.wikipedia.org/wiki/RSA_(cryptosystem) 大素数分解]。 很明显，密钥不能是可预测的：如果对手知道下一个输出是什么，他们很容易猜测密钥，直到找到正确的密钥。&lt;br /&gt;
&lt;br /&gt;
“真”随机数是加密安全随机性的最佳来源，但是很难建立一个大的、快速补充的熵池，其中每个源都是完全无偏的，并且不受其他源的影响。 要以难以逆转的方式“拉伸”熵，可以使用加密安全随机数生成器（CSPRNG）。 CSPRNGs保证，在看到之前的结果后，在计算上很难猜测下一个输出，如果生成器的状态已知，哪些值在已知输出之前。&lt;br /&gt;
&lt;br /&gt;
==真随机数生成器==&lt;br /&gt;
真正的随机数生成器使用物理设备或现象生成随机数，其不可预测性可以追溯到量子力学定律。&lt;br /&gt;
&lt;br /&gt;
===x86 RDSEED指令===&lt;br /&gt;
较新的x86和x86-64处理器具有用于生成随机数的指令RDSEED。&lt;br /&gt;
要使用RDSEED，首先需要检查指令是否可用。&lt;br /&gt;
&amp;lt;source lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
mov eax, 7     ; set EAX to request function 7&lt;br /&gt;
mov ecx, 0     ; set ECX to request subfunction 0&lt;br /&gt;
cpuid&lt;br /&gt;
shr ebx, 18    ; the result we want is in EBX...&lt;br /&gt;
and ebx, 1     ; ...test for the flag of interest...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果EBX设置为1（或ZF未设置），则该指令可用。 然后可以使用它生成16/32/64位随机数（取决于用作参数的寄存器大小）。 如果生成了随机数，则设置进位标志。&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
mov ecx, 100   ; number of retries&lt;br /&gt;
.retry:&lt;br /&gt;
    rdseed eax&lt;br /&gt;
    jc .done      ; carry flag is set on success&lt;br /&gt;
    loop .retry&lt;br /&gt;
.fail:&lt;br /&gt;
    ; no random number available&lt;br /&gt;
.done:&lt;br /&gt;
    ; random number is in EAX&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
这取决于你和你的用户是否信任RDSEED。 因为它是在硬件中实现的，所以它实际上是一个黑匣子，可能包含各种错误，或者更糟的是，后门。&lt;br /&gt;
&lt;br /&gt;
=== 专用硬件===&lt;br /&gt;
&lt;br /&gt;
有专门用于生成“真”随机数的设备。 从消费者级TPMs到PCIe“加密加速器”都有。 这些是RDSEED/RDRAND的一种泛化，缺点是需要额外的驱动程序来与设备交互，用户可能没有安装这样的设备。&lt;br /&gt;
&lt;br /&gt;
TPMs或Trusted Platform Module是可以安装在现代主板上的小型协处理器。 除了生成随机数，它们还提供[https://en.wikipedia.org/wiki/Trusted_Computing 其他可信计算服务]。 值得注意的是，Windows 11要求有TPM。 它们也可以在CPU上模拟（例如英特尔PTT或AMD fTPM）。 与RDSEED一样，后门可能是一个问题。&lt;br /&gt;
&lt;br /&gt;
===手动采样===&lt;br /&gt;
&lt;br /&gt;
在一个运行的系统中，有很多进程实际上是随机的。 想想每秒钟都会出现的各种中断：精度为百万分之几的计时器、外围I/O、用户与整个系统的交互等等。&lt;br /&gt;
&lt;br /&gt;
挑战在于找到（矛盾的是）可靠随机且难以从外部影响和观察的来源。 对于这些源中的每一个，必须估计它们贡献了多少熵。 测量将各自的熵量添加到池中，而读取则会降低熵。&lt;br /&gt;
&lt;br /&gt;
因为你可以完全控制这种生成方法，所以还可以合并硬件生成器生成的值。 你可以自己决定这些生成器的熵是多少，甚至是0位。&lt;br /&gt;
&lt;br /&gt;
当使用计时作为熵源时，读取的时间戳应尽可能精确。 TSC在这方面做得很好。 衡量从该操作中获得的熵需要了解事件发生的时间窗口和TSC的滴答率。 例如，如果一个TSC的滴答频率为3 GHz，并且一个事件有一个10毫秒的窗口要发生，那么TSC读取可以有3000万个值中的任何一个，这意味着从中获得的熵约为24.8位。 如果TSC较慢，只有1GHz，那么熵将只有约23.2位。&lt;br /&gt;
&lt;br /&gt;
====对抗熵====&lt;br /&gt;
如果对手能够以某种方式观察熵池的状态，并将自己的熵贡献给熵池，那么他们提供熵的方式可能会迫使熵池进入较低的熵状态。 一个简单的例子是，熵源定期检查池中的某个位是否已设置，然后提供熵，直到它被清除。 这样，在大多数情况下，给定的位是清晰的，熵缓冲区可能处于的状态数减少了一半。 DJB的博客上描述了一种更复杂的攻击：https://blog.cr.yp.to/20140205-entropy.html&lt;br /&gt;
&lt;br /&gt;
部分原因还在于，如果用户请求一个随机数，不经修改地公开熵池是不明智的。 如果对手可以访问该池（通过专用的“添加熵”接口或采样事件源），则很容易对其下毒。 用于隐藏确切状态的常用方法是使用加密安全的哈希函数（如SHA-256），结合计数器（例如熵计数器）和salt对池（部分）进行哈希运算。 因为这些散列算法很难反转，所以它的输入不容易猜测。 只有当池中还有一些熵时，才需要这样做。&lt;br /&gt;
&lt;br /&gt;
==加密安全的伪随机数生成器（Cryptographically secure pseudorandom number generators）==&lt;br /&gt;
&lt;br /&gt;
现在跟随一些CSPRNGs。 重要的是要记住，与任何加密一样，如果你打算实际使用它，最好不要自制它。 出错的方式很多，算法越复杂，出错的几率就越大。 当然，对于业余用途来说，这是完全好的；只是不要使用手工制作的TLS密钥源进行网上银行业务。&lt;br /&gt;
&lt;br /&gt;
===x86 RDRAND指令===&lt;br /&gt;
RDRAND比RDSEED稍早，并提供([https://software.intel.com/content/www/us/en/develop/blogs/the-difference-between-rdrand-and-rdseed.html 英特尔声称的）CSPRNG。&lt;br /&gt;
它的存在由CPUID leaf，ECX位30表示：&lt;br /&gt;
&amp;lt;source lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
mov eax, 1     ; set EAX to request function 1&lt;br /&gt;
mov ecx, 0     ; set ECX to request subfunction 0&lt;br /&gt;
cpuid&lt;br /&gt;
shr ecx, 30    ; the result we want is in ECX...&lt;br /&gt;
and ecx, 1     ; ...test for the flag of interest...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果ECX设置为1（或ZF未设置），则该指令可用。用法与RDSEED相同：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
mov ecx, 100   ; number of retries&lt;br /&gt;
.retry:&lt;br /&gt;
    rdrand eax&lt;br /&gt;
    jc .done      ; carry flag is set on success&lt;br /&gt;
    loop .retry&lt;br /&gt;
.fail:&lt;br /&gt;
    ; no random number available&lt;br /&gt;
.done:&lt;br /&gt;
    ; random number is in EAX&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
它由RDSEED读取的同一个熵源自动播种，不能手动播种。 因此，它与RDSEED一样需要注意。&lt;br /&gt;
&lt;br /&gt;
=== Ciphers ===&lt;br /&gt;
&lt;br /&gt;
通过植入一个安全Cipher（如ChaCha20和AES）并运行多个循环来构造CSPRNG是相当常见的，在这些循环中，输出与运行的计数器一起被重新加密。 每隔一段时间，就会创建一个新密钥，可能涉及另一个安全的随机源。&lt;br /&gt;
&lt;br /&gt;
[https://www.bentasker.co.uk/blog/software-development/689-writing-a-chacha20-based-csprng 编写（和植入后门）一个基于CSPRNG的ChaCha20的]可能是一篇关于这个主题的有趣文章，以及它如何以令人惊讶的方式出错。&lt;br /&gt;
&lt;br /&gt;
==伪随机数生成器==&lt;br /&gt;
&lt;br /&gt;
接下来是各种常规PRNG。 它们在质量、简单性和速度的各个方面都有所不同。 仔细阅读每个解释！&lt;br /&gt;
&lt;br /&gt;
=== 标准的示例===&lt;br /&gt;
&lt;br /&gt;
直接取自 [http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf C标准文档（第347页）]：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
//以下函数定义了rand和srand的可移植实现。&lt;br /&gt;
&lt;br /&gt;
static unsigned long int next = 1;  // 注：“unsigned long int”假定为32位宽&lt;br /&gt;
&lt;br /&gt;
int rand(void)  // RAND_MAX假设为32767&lt;br /&gt;
{&lt;br /&gt;
    next = next * 1103515245 + 12345;&lt;br /&gt;
    return (unsigned int) (next / 65536) % 32768;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void srand(unsigned int seed)&lt;br /&gt;
{&lt;br /&gt;
    next = seed;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
这是一个相当标准但平庸的标准 [https://en.wikipedia.org/wiki/Linear_congruential_generator 线性同余发生器]（Linear Congruential Generator-LCG）。 它返回15个随机位。&lt;br /&gt;
&lt;br /&gt;
它基于这个递推公式&lt;br /&gt;
  X&amp;lt;sub&amp;gt;n+1&amp;lt;/sub&amp;gt; = (aX&amp;lt;sub&amp;gt;n&amp;lt;/sub&amp;gt; + c) mod m&lt;br /&gt;
其中，模数m是LCG能产生的最大随机值数。 对于C标准中的示例，a=1103515245、C=12345和m=2&amp;lt;sup&amp;gt;32&amp;lt;/sup&amp;gt;（隐式）。&lt;br /&gt;
LCG的质量在很大程度上取决于这些值，很难找到好的值。 维基百科给出了[https://en.wikipedia.org/wiki/Linear_congruential_generator#Parameters_in_common_use 常见参数列表]。&lt;br /&gt;
&lt;br /&gt;
===斐波那契随机数===&lt;br /&gt;
斐波那契序列的特殊“重拍”可以用来生成随机数。 这是通过有4个“种子”来实现的，这些种子一开始是非常奇怪的值（例如45、80、22、32）。&lt;br /&gt;
seed()函数的作用是：在序列的末尾添加一个新的种子，并删除第一个种子（种子被称为A、B、C和D）。 rand（）函数只返回种子的总和，并用结果调用seed（）。 [[User:Mariuszp|Glidix]]就是这种RNG实现的一个很好的例子。&lt;br /&gt;
&lt;br /&gt;
===线性反馈移位寄存器===&lt;br /&gt;
线性反馈移位寄存器（LFSR）是一种简单的方法，可以在给定非零种子的情况下生成很长的随机数序列。 其思想如下：“n”位LFSR有“n”位（0或1）。 最初，寄存器用“n”位种子填充。 对于下一个要生成的值，从寄存器中“点击”某些位，并将它们异或在一起。 将结果二进制值输入最左边的位，将所有位向右移动一个位置。 从最右侧LFSR移出的位是生成的随机位。&lt;br /&gt;
&lt;br /&gt;
LFSR可以产生的比特序列最终会重复，但通过仔细选择异或比特，LFSR的周期可以增加到最多2&amp;lt;sup&amp;gt;n&amp;lt;/sup&amp;gt;-1比特。 这意味着在一个2&amp;lt;sup&amp;gt;n&amp;lt;/sup&amp;gt;-1位的序列之后，将返回相同的序列。 然而，这是所有伪随机数生成器都具有的一个特性：在不改变种子的情况下，它们最终会重复自己。&lt;br /&gt;
&lt;br /&gt;
下面是一个16位LFSR的示例，使用位11、13、14和16异或作为其输入。 该LFSR的周期为65535位，因此在序列自身重复之前，它将生成一个65535位的伪随机序列。 LFSR产生的下一个位是1（位16的值），下一个输入位是0。&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            1                                       11      13  14      16&lt;br /&gt;
          +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+&lt;br /&gt;
INPUT --&amp;gt; | 0   1   0   0   0   1   0   0   1   1   1   1   0   0   0   1 | --&amp;gt; OUTPUT&lt;br /&gt;
          +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+&lt;br /&gt;
&lt;br /&gt;
INPUT = bit 11 XOR bit 13 XOR bit 14 XOR bit 16&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
更大（和更小）的LFSR也是可能的。 聪明人已经推导出多项式，确保在任何非零输入的情况下，LFSR的周期尽可能大（2&amp;lt;sup&amp;gt;n&amp;lt;/sup&amp;gt;-1）。 这样的多项式被写成x&amp;lt;sup&amp;gt;16&amp;lt;/sup&amp;gt;+x&amp;lt;sup&amp;gt;14&amp;lt;/sup&amp;gt;+x&amp;lt;sup&amp;gt;13&amp;lt;/sup&amp;gt;+x&amp;lt;sup&amp;gt;11&amp;lt;/sup&amp;gt;+1。 对于这个“n”=16的多项式示例，位16、14、13和11必须异或在一起，并作为输入提供，从左开始计数，从1开始。 Polynomials for other values of ''n'' can be found [http://en.wikipedia.org/wiki/Linear_feedback_shift_register#Some_polynomials_for_maximal_LFSRs here on Wikipedia] and on page 5 of [http://www.xilinx.com/support/documentation/application_notes/xapp052.pdf this PDF document].&lt;br /&gt;
&lt;br /&gt;
请注意，种子永远不能为零。 这也意味着，不可能所有寄存器的位值都为零，而在2个寄存器的可能组合中，不允许全零状态。 因此，2个&amp;lt;sup&amp;gt;n&amp;lt;/sup&amp;gt;-1状态是可能的最大值。&lt;br /&gt;
&lt;br /&gt;
=== Wichmann-Hill ===&lt;br /&gt;
1982年，Brian Wichmann和David Hill[https://doi.org/10.2307/2347988 建议]组合三个线性同余生成器，然后对结果进行归一化和求和，得到一个均匀分布在0和1之间的数。 一个常见的例子是：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static uint16_t seed[3]; /* seed with numbers between 1 and 30000 */&lt;br /&gt;
float wichmann_hill(void) {&lt;br /&gt;
  seed[0] = (171 * seed[0]) % 30269;&lt;br /&gt;
  seed[1] = (172 * seed[1]) % 30307;&lt;br /&gt;
  seed[2] = (170 * seed[2]) % 30323;&lt;br /&gt;
  return fmod(seed[0] / 30269.0 + seed[1] / 30307.0 + seed[2] / 30323.0, 1.0);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
已知该版本的周期略小于7万亿（最常见的倍数为30268、30306和30322）。&lt;br /&gt;
&lt;br /&gt;
=== Mersenne Twister ===&lt;br /&gt;
&lt;br /&gt;
Mersenne Twister是一款非常受欢迎的PRNG，可在大多数平台上使用；例如，它是C++11标准库所需的生成器集的一部分。 64位MT-19937版本的C实现如下：&lt;br /&gt;
&lt;br /&gt;
根据维基百科实现：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stddef.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;lt;assert.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#ifdef USE32&lt;br /&gt;
typedef uint32_t word_t;&lt;br /&gt;
#define STATE_SIZE  624&lt;br /&gt;
#define MIDDLE      397&lt;br /&gt;
#define INIT_SHIFT  30&lt;br /&gt;
#define INIT_FACT   1812433253&lt;br /&gt;
#define TWIST_MASK  0x9908b0df&lt;br /&gt;
#define SHIFT1      11&lt;br /&gt;
#define MASK1       0xffffffff&lt;br /&gt;
#define SHIFT2      7&lt;br /&gt;
#define MASK2       0x9d2c5680&lt;br /&gt;
#define SHIFT3      15&lt;br /&gt;
#define MASK3       0xefc60000&lt;br /&gt;
#define SHIFT4      18&lt;br /&gt;
#else&lt;br /&gt;
typedef uint64_t word_t;&lt;br /&gt;
#define STATE_SIZE  312&lt;br /&gt;
#define MIDDLE      156&lt;br /&gt;
#define INIT_SHIFT  62&lt;br /&gt;
#define TWIST_MASK  0xb5026f5aa96619e9&lt;br /&gt;
#define INIT_FACT   6364136223846793005&lt;br /&gt;
#define SHIFT1      29&lt;br /&gt;
#define MASK1       0x5555555555555555&lt;br /&gt;
#define SHIFT2      17&lt;br /&gt;
#define MASK2       0x71d67fffeda60000&lt;br /&gt;
#define SHIFT3      37&lt;br /&gt;
#define MASK3       0xfff7eee000000000&lt;br /&gt;
#define SHIFT4      43&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
#define LOWER_MASK  0x7fffffff&lt;br /&gt;
#define UPPER_MASK  (~(word_t)LOWER_MASK)&lt;br /&gt;
static word_t state[STATE_SIZE];&lt;br /&gt;
static size_t index = STATE_SIZE + 1;&lt;br /&gt;
&lt;br /&gt;
static void seed(word_t s)&lt;br /&gt;
{&lt;br /&gt;
    index = STATE_SIZE;&lt;br /&gt;
    state[0] = s;&lt;br /&gt;
    for (size_t i = 1; i &amp;lt; STATE_SIZE; i++)&lt;br /&gt;
        state[i] = (INIT_FACT * (state[i - 1] ^ (state[i - 1] &amp;gt;&amp;gt; INIT_SHIFT))) + i;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static void twist(void)&lt;br /&gt;
{&lt;br /&gt;
    for (size_t i = 0; i &amp;lt; STATE_SIZE; i++)&lt;br /&gt;
    {&lt;br /&gt;
        word_t x = (state[i] &amp;amp; UPPER_MASK) | (state[(i + 1) % STATE_SIZE] &amp;amp; LOWER_MASK);&lt;br /&gt;
        x = (x &amp;gt;&amp;gt; 1) ^ (x &amp;amp; 1? TWIST_MASK : 0);&lt;br /&gt;
        state[i] = state[(i + MIDDLE) % STATE_SIZE] ^ x;&lt;br /&gt;
    }&lt;br /&gt;
    index = 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static word_t mt_random(void)&lt;br /&gt;
{&lt;br /&gt;
    if (index &amp;gt;= STATE_SIZE)&lt;br /&gt;
    {&lt;br /&gt;
        assert(index == STATE_SIZE || !&amp;quot;Generator never seeded&amp;quot;);&lt;br /&gt;
        twist();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    word_t y = state[index];&lt;br /&gt;
    y ^= (y &amp;gt;&amp;gt; SHIFT1) &amp;amp; MASK1;&lt;br /&gt;
    y ^= (y &amp;lt;&amp;lt; SHIFT2) &amp;amp; MASK2;&lt;br /&gt;
    y ^= (y &amp;lt;&amp;lt; SHIFT3) &amp;amp; MASK3;&lt;br /&gt;
    y ^= y &amp;gt;&amp;gt; SHIFT4;&lt;br /&gt;
&lt;br /&gt;
    index++;&lt;br /&gt;
    return y;&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
有些实现会自动使用seed 5489为生成器设定种子，但这（显然）会导致每次初始化时都有相同的输出。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
它的表现优于上面列出的所有PRNG，但由于其庞大的状态规模，速度相当缓慢。 它在一些统计领域也存在问题。 参考 [https://arxiv.org/abs/1910.06437 &amp;quot;It is high time we let go of the Mersenne Twister&amp;quot;]。&lt;br /&gt;
&lt;br /&gt;
===PCG系列===&lt;br /&gt;
&lt;br /&gt;
[https://www.pcg-random.org/ PCG系列]是PRNG领域的一个相对较新的成员。 最简单的版本使用带有置换运算符的LCG对输出进行置乱。 它是为了通过尽可能多的统计测试而建造的，同时仍然保持小而快。 There is a [https://www.pcg-random.org/pdf/hmc-cs-2014-0905.pdf comprehensive paper] describing the generators and Apache 2.0-licensed code is available: https://www.pcg-random.org/download.html.&lt;br /&gt;
&lt;br /&gt;
请注意，该网站声称，PCG的输出比其他PRNG的输出更难预测，这意味着PCG更安全。 It is possible to predict some generators after [https://pcg.di.unimi.it/pcg.php#claims only three outputs], so it should not be considered &amp;quot;hard to break&amp;quot; and definitely not &amp;quot;more secure&amp;quot;. “非”加密安全PRNG的可预测性通常不是问题。&lt;br /&gt;
&lt;br /&gt;
=== xoshiro系列===&lt;br /&gt;
&lt;br /&gt;
和PCG一样，xoshiro和相关的xoroshiro家族也是相当新的PRNG。&lt;br /&gt;
&lt;br /&gt;
=== 搞一个自创的 ===&lt;br /&gt;
&lt;br /&gt;
如果你仍然不满意，你可能想建立自己的PRNG。 你可以通过编写一些看起来不错的东西，并将其置于自动化的统计测试套件（如[http://simul.iro.umontreal.ca/testu01/tu01.html TestU01]的SmallCrush中，依次Crush，BigCrush。 这些测试非常容易运行，可以快速指出问题，例如：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unif01.h&amp;gt;&lt;br /&gt;
#include &amp;lt;bbattery.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
static uint32_t next = 1;&lt;br /&gt;
unsigned int custom_rand(void) {&lt;br /&gt;
    // This is a terrible generator!&lt;br /&gt;
    next = (next * 0x4B4B9656U) ^ (next * 0x565AC3C3U) + 1;&lt;br /&gt;
    return next * (next - 1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
    unif01_Gen *gen = unif01_CreateExternGenBits(&amp;quot;custom_rand&amp;quot;, custom_rand);&lt;br /&gt;
    bbattery_SmallCrush(gen);  //或者bbattery_Crush，bbattery_BigCrush&lt;br /&gt;
    unif01_DeleteExternGenBits(gen);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SmallCrush将报告该生成器在15次统计测试中有12次失败。 因此，也不需要进行其他速度慢得多的测试。&lt;br /&gt;
&lt;br /&gt;
[[Category:Common Algorithms]]&lt;/div&gt;</summary>
		<author><name>Zhang3</name></author>
	</entry>
</feed>