在php中mt_rand()是一個常用的偽隨機數生成函數是一個常用的偽隨機數生成函數是一個常用的偽隨機數生成函數它使用的是它使用的是它使用的是它使用的是它使用的是它使用的是它使用的是tw算法算法相比提供了更高質量的隨機數。為了更好地理解它的行為提供了更高質量的隨機數。為了更好地理解它的行為mt_getrandmax()來獲取當前環境中mt_rand()可能生成的最大值。但在實際使用過程中
mt_getrandmax()的作用是返回mt_rand()所能生成的最大隨機整數2147483647 (即2^31-1)
例如:
<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之間的小數),php編譯配置的差異,導致某些極端情況的隨機分佈不一致。 ,導致某些極端情況的隨機分佈不一致。
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_getrandmax() 。
很多人認為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的種子在未明確設置的情況下默認是基於當前時間和進程ID的。如果在多個並發請求中同時調用mt_rand()而未顯式mt_srand() ,可能會得到相同的隨機序列。
解決方案:
為每個線程或請求設置不同的種子:
<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() ;
用整數直接控制範圍更安全;
高並發或多線程中考慮手動設定種子;
對於安全性要求高的場景對於安全性要求高的場景tandy_int()或randy_bytes()代替。
Mt_rand()系列函數時更加得心應手
https://gitbox.net/php/mt_rand_examples