PHP 中 rand()与mt_rand()都是用于产生一个指定范围内单独随机数的函数,如果需要产生多个不重复的随机数,请参考:PHP生成指定范围内的N个不重复的随机数

既然他们都是用于产生一个随机数,那么他们有什么区别呢?

rand() 函数默认使用 libc 随机数发生器,很多老的 libc 的随机数发生器具有一些不确定和未知的特性而且效率很低

mt_rand() 则是用了 Mersenne Twister 中已知的特性作为随机数发生器,它产生随机数值的平均速度比 libc 提供的 rand() 快四倍

所以,PHP 中 mt_rand()函数是非正式用来替换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倍

 

 

参考推荐

PHP 生成png图片代码

ImageMagick 图片处理工具的安装