在php中mt_rand()是一个常用的伪随机数生成函数它使用的是它使用的是mersenne twister
mt_getrandmax()的作用是返回mt_rand()所能生成的最大随机整数所能生成的最大随机整数2147483647
例如:
<span><span><span class="hljs-keyword">echo</span></span><span> </span><span><span class="hljs-title function_ invoke__">mt_getrandmax</span></span><span>(); </span><span><span class="hljs-comment">// 输出:2147483647</span></span><span>
</span></span>
问题出现点:如果你基于该返回值来进行归一化处理(如生成 0到1
mt_rand()支持两个参数:最小值和最大值。如果你将最大值设置为大于mt_getrandmax()的值、会怎样?
<span><span><span class="hljs-variable">$min</span></span><span> = </span><span><span class="hljs-number">0</span></span><span>;
</span><span><span class="hljs-variable">$max</span></span><span> = </span><span><span class="hljs-title function_ invoke__">mt_getrandmax</span></span><span>() + </span><span><span class="hljs-number">1</span></span><span>;
</span><span><span class="hljs-variable">$rand</span></span><span> = </span><span><span class="hljs-title function_ invoke__">mt_rand</span></span><span>(</span><span><span class="hljs-variable">$min</span></span><span>, </span><span><span class="hljs-variable">$max</span></span><span>); </span><span><span class="hljs-comment">// 可能产生警告或不准确的结果</span></span><span>
</span></span>
问题出现点:虽然php
很多人认为mt_rand()可以生成真正“均匀”的随机数0到100之间的随机浮点数:
<span><span><span class="hljs-variable">$randFloat</span></span><span> = </span><span><span class="hljs-title function_ invoke__">mt_rand</span></span><span>() / </span><span><span class="hljs-title function_ invoke__">mt_getrandmax</span></span><span>();
</span><span><span class="hljs-variable">$result</span></span><span> = </span><span><span class="hljs-title function_ invoke__">floor</span></span><span>(</span><span><span class="hljs-variable">$randFloat</span></span><span> * </span><span><span class="hljs-number">101</span></span><span>); </span><span><span class="hljs-comment">// 希望是 0-100 的整数</span></span><span>
</span></span>
问题出现点:由于浮点运算和整数舍入的问题、这种方法可能会导致边界值(如 0 或100))
更稳妥的写法:
<span><span><span class="hljs-variable">$result</span></span><span> = </span><span><span class="hljs-title function_ invoke__">mt_rand</span></span><span>(</span><span><span class="hljs-number">0</span></span><span>, </span><span><span class="hljs-number">100</span></span><span>); </span><span><span class="hljs-comment">// 直接使用整数区间</span></span><span>
</span></span>
或者如果你真的需要一个浮点数:
<span><span><span class="hljs-function"><span class="hljs-keyword">function</span></span></span><span> </span><span><span class="hljs-title">mt_rand_float</span></span><span>(</span><span><span class="hljs-params"><span class="hljs-variable">$min</span></span></span><span> = </span><span><span class="hljs-number">0</span></span><span>, </span><span><span class="hljs-variable">$max</span></span><span> = </span><span><span class="hljs-number">1</span></span><span>) {
</span><span><span class="hljs-keyword">return</span></span><span> </span><span><span class="hljs-variable">$min</span></span><span> + (</span><span><span class="hljs-title function_ invoke__">mt_rand</span></span><span>() / </span><span><span class="hljs-title function_ invoke__">mt_getrandmax</span></span><span>()) * (</span><span><span class="hljs-variable">$max</span></span><span> - </span><span><span class="hljs-variable">$min</span></span><span>);
}
</span></span>
在需要使用随机数生成一个特定概率分布(例如权重选择、正态分布等)时错误地使用mt_getrandmax()可能会使结果偏离预期。例如、某些开发者会用以下方式实现带权重的随机选择:
<span><span><span class="hljs-variable">$rand</span></span><span> = </span><span><span class="hljs-title function_ invoke__">mt_rand</span></span><span>(</span><span><span class="hljs-number">1</span></span><span>, </span><span><span class="hljs-title function_ invoke__">mt_getrandmax</span></span><span>());
</span></span>
然后将这个值映射到各权重区间中。问题是、特别是当总权重数量远小于mt_getrandmax()时。
更安全的做法是:
在已知最大权重总和的前提下、直接使用:
<span><span><span class="hljs-variable">$rand</span></span><span> = </span><span><span class="hljs-title function_ invoke__">mt_rand</span></span><span>(</span><span><span class="hljs-number">1</span></span><span>, </span><span><span class="hljs-variable">$totalWeight</span></span><span>);
</span></span>
这样可以确保精度和分布合理性。
虽然mt_rand()的随机性足够日常使用但在高频调用(如循环中快速生成多个随机数 但在高频调用(如循环中快速生成多个随机数)、可能会遇到重复值。
例如:
<span><span><span class="hljs-keyword">for</span></span><span> (</span><span><span class="hljs-variable">$i</span></span><span> = </span><span><span class="hljs-number">0</span></span><span>; </span><span><span class="hljs-variable">$i</span></span><span> < </span><span><span class="hljs-number">1000000</span></span><span>; </span><span><span class="hljs-variable">$i</span></span><span>++) {
</span><span><span class="hljs-variable">$r</span></span><span> = </span><span><span class="hljs-title function_ invoke__">mt_rand</span></span><span>();
</span><span><span class="hljs-comment">// 统计重复率</span></span><span>
}
</span></span>
问题出现点: Mersenne Twister
解决方案:
为每个线程或请求设置不同的种子:
<span><span><span class="hljs-title function_ invoke__">mt_srand</span></span><span>(</span><span><span class="hljs-title function_ invoke__">crc32</span></span><span>(</span><span><span class="hljs-title function_ invoke__">uniqid</span></span><span>(</span><span><span class="hljs-string">''</span></span><span>, </span><span><span class="hljs-literal">true</span></span><span>)));
</span></span>
当然、PHP 7.1起已不推荐手动调用MT_SRAND()、PHP
mt_rand()和mt_getrandmax()是php提供的强大但需要谨慎使用的函数。常见的误区包括错误理解mt_getrandmax()
要避免这些问题、应注意:
不要假设MT_GETRANDMAX()会随系统变化;
不应将mt_rand()的最大值设为超过mt_getrandmax() ;
用整数直接控制范围更安全;
高并发或多线程中考虑手动设定种子;
对于安全性要求高的场景对于安全性要求高的场景and random_int()或random_bytes()代替。
掌握这些细节可以让你在使用可以让你在使用mt_rand()系列函数时更加得心应手、避免潜在バグ。更多实例可参考:
https://gitbox.net/php/mt_rand_examples