PHP 随机数函数 rand() 与 mt_rand()
PHP 中 rand()与mt_rand()都是用于产生一个指定范围内单独随机数的函数,如果需要产生多个不重复的随机数,请参考:PHP生成指定范围内的N个不重复的随机数。
既然他们都是用于产生一个随机数,那么他们有什么区别呢?
rand() 函数默认使用 libc 随机数发生器,很多老的 libc 的随机数发生器具有一些不确定和未知的特性而且效率很低;
mt_rand() 则是用了 Mersenne Twister 中已知的特性作为随机数发生器,它产生随机数值的平均速度比 libc 提供的 rand() 快四倍。
所以,PHP 中 mt_rand()函数是非正式用来替换rand()的。
rand — 产生一个随机整数 (PHP 4, PHP 5, PHP 7)
rand ( void ) : int
rand ( int $min , int $max ) : int
参数
min 返回的最低值(默认:0)
max 返回的最高值(默认:getrandmax())
返回值 A pseudo random value between min
(or 0) and max
(or getrandmax(), inclusive)
如果没有提供可选参数 min 和 max,rand() 返回 0 到 getrandmax() 之间的伪随机整数。例如想要 5 到 15(包括 5 和 15)之间的随机数,用rand(5, 15)。
说明:在某些平台下(例如 Windows)getrandmax() 只有 32767。如果需要的范围大于 32767,那么指定 min 和 max 参数就可以生成更大的数了,或者考虑用 mt_rand() 来替代之。
mt_rand() 推荐,速度更快
mt_rand — 生成更好的随机数 (PHP 4, PHP 5, PHP 7)
mt_rand ( void ) : int
mt_rand ( int $min , int $max ) : int
参数
min 可选的、返回的最小值(默认:0)
max 可选的、返回的最大值(默认:mt_getrandmax())
返回值 返回 min (或者 0) 到 max (或者是到 mt_getrandmax() ,包含这个值)之间的随机整数。
很多老的 libc 的随机数发生器具有一些不确定和未知的特性而且很慢。PHP 的 rand() 函数默认使用 libc 随机数发生器。mt_rand() 函数是非正式用来替换它的。该函数用了 » Mersenne Twister 中已知的特性作为随机数发生器,它可以产生随机数值的平均速度比 libc 提供的 rand() 快四倍。
如果没有提供可选参数 min 和 max,mt_rand() 返回 0 到 mt_getrandmax() 之间的伪随机数。例如想要 5 到 15(包括 5 和 15)之间的随机数,用mt_rand(5, 15)。
总结
min, max 参数皆为可选,规定随机数产生的范围。
如果没有提供可选参数 min 和 max,则返回 0 到 RAND_MAX 之间的伪随机整数。
例如,想要 1 到 100(包括 1 和 100)之间的随机数,用 rand(1, 100) 或 mt_rand(1,100)。
mt_rand()是更好地随机数生成器(推荐使用),因为它跟rand()相比播下了一个更好地随机数种子;而且性能上比rand()快4倍,mt_getrandmax()所表示的数值范围也更大。
注: 自 PHP 4.2.0 起,PHP产生随机数都不再需要用 srand() 或 mt_srand() 函数产生随机种子,已经会自动完成。
PHP生成指定范围内的N个不重复的随机数
有时候需要生成指定范围内一定数量的不重复随机数,例如生成100个20位随机数,具体怎么设计这个生产随机数的函数呢?
方法1:可以将随机产生的数存入数组,并在存入的同时去除重复的值,即可生成一定数量的不重复随机数。
方法2:可以把指定范围内的数值存进数组,再使用shuffle($array)
打乱这个数组,然后再截取其中一定数量的值。
说明:方法2的做法,在指定的随机数范围太大的时候会产生一个较大的数组。
下面给出第一种做法的代码,第二种做法更简单,大家可以尝试下
/** * 生成一定数量的不重复随机数,指定的范围内整数的数量必须比要生成的随机数数量大 * https://mimvp.com in 2019.05.20 * * @param string $min : 指定随机数的最小值(包含) * @param string $max : 指定随机数的最大值(包含) * @param string $num : 指定生成数量 * @return array $result : 返回指定数量的随机数结果数组 * * array_flip() 函数返回一个反转后的数组,如果同一值出现了多次,则最后一个键名将作为它的值,所有其他的键名都将丢失 */ function generate_unique_rand($min, $max, $num) { $count = 0; $result = array(); while ($count < $num) { $result[] = mt_rand($min, $max); // $result = array_flip(array_flip($result)); // 通过两次反转让随机数唯一, 使用 array_unique 替代 $result = array_unique($result); // 返回唯一性元素的数组 $count = count($result); } shuffle($result); // shuffle() 函数把数组中的元素按随机顺序重新排列, 打乱数组重新赋予数组新的下标 return $result; } //生成10个1到100范围内的不重复随机数 $unique_rand_array = generate_unique_rand(10, 99, 10); // 10, 90, 91(极限测试,死循环) sort($unique_rand_array); // 对返回的随机数排序, 方便查看 print( sprintf("<br><br> count: %d, unique_rand_array: %s", count($unique_rand_array), json_encode($unique_rand_array)) ); shuffle($unique_rand_array); // 对返回的随机数排序, 随机顺序重新排列 print( sprintf("<br><br> count: %d, unique_rand_array: %s", count($unique_rand_array), json_encode($unique_rand_array)) );
运行的结果:
count: 10, unique_rand_array: [11,34,37,45,72,79,85,87,90,96] count: 10, unique_rand_array: [79,90,96,72,11,85,87,37,45,34]
说明:
1、生成随机数时我们用了 mt_rand() 函数。这个函数生成随机数的平均速度要比 rand() 快几倍。
2、去除数组中的重复值时用了“翻转法”,就是用 array_flip() 把数组的 key 和 value 交换两次,这种做法在去除数组重复值的同时效率也比用 array_unique() 快得多。
3、返回数组前,先使用 shuffle() 为数组赋予新的键名,保证键名是 0-n 连续的数字。如果不进行此步骤,可能在删除重复值时造成键名不连续,如果用for遍历的时候会有问题,但如果用foreach或不需要遍历的时候可以不需要shuffle。
mt_rand() 和 rand() 速度测试
测试代码
<?php /** * 功能: mt_rand() 和 rand() 速度测试 * 版权: https://mimvp.com * 日期: 2019-05-20 */ function microtime_float() { list($usec, $sec) = explode(" ", microtime()); return ((float)$usec + (float)$sec); } print("<br><br> microtime_float(): " . microtime_float()); function test_rand_mtrand($count=10000) { print("<br><br> count: $count"); // rand() $time_start = microtime_float(); for($i=0; $i<$count; ++$i) { rand(); } $time_end = microtime_float(); $time_cost = $time_end - $time_start; echo "<br> rand() time_cost ---------- $time_cost seconds"; // mt_rand() $time_start = microtime_float(); for($i=0; $i<$count; ++$i) { mt_rand(); } $time_end = microtime_float(); $time_cost = $time_end - $time_start; echo "<br> mt_rand() time_cost ----- $time_cost seconds"; } test_rand_mtrand(1000); // 1千次 test_rand_mtrand(10000); // 1万次 test_rand_mtrand(100000); // 10万次 test_rand_mtrand(1000000); // 100万次 test_rand_mtrand(10000000); // 1000万次(耗时较长) ?>
运行结果:
microtime_float(): 1559201781.7574 count: 1000 rand() time_cost ---------- 0.00091695785522461 seconds mt_rand() time_cost ----- 0.00093388557434082 seconds count: 10000 rand() time_cost ---------- 0.008936882019043 seconds mt_rand() time_cost ----- 0.010462999343872 seconds count: 100000 rand() time_cost ---------- 0.076805114746094 seconds mt_rand() time_cost ----- 0.074769020080566 seconds count: 1000000 rand() time_cost ---------- 0.857342004776 seconds mt_rand() time_cost ----- 0.76551508903503 seconds count: 10000000 rand() time_cost ---------- 7.561311006546 seconds mt_rand() time_cost ----- 7.7968890666962 seconds
结论:两者的循环效率实际上差不多,没有传说中的相差4倍
参考推荐:
版权所有: 本文系米扑博客原创、转载、摘录,或修订后发表,最后更新于 2019-08-10 05:05:34
侵权处理: 本个人博客,不盈利,若侵犯了您的作品权,请联系博主删除,莫恶意,索钱财,感谢!