在PHP中播种随机数
计算机不是很擅长创建随机数,因为它们使用的方法是确定性的。也就是说,它们从一个数字(称为种子)开始,然后对该数字应用数学运算以生成伪随机数。计算机程序生成的大多数随机数都使用系统提供的种子,通常是当前时间。如果您可以猜出初始值,则可以开始计算出随机生成的数字序列是什么。因此,大多数随机数都不适合加密。
为了使计算机生成随机数,已经做了很多工作。著名的是,旧金山的CloudFlare办事处有一堵熔岩灯墙,用于产生随机性,以便为其服务创建强大的加密技术。www.random.org网站使用大气数据生成随机数,并提供可用于将随机性纳入您的应用程序的服务。
如果您故意设置随机数生成器的种子,则每次生成的数字将相同。那么,为什么要故意植入随机数生成器呢?这可能有两个用途。
许多游戏使用种子随机数来创建其等级。通过使用种子生成级别,可以为不同的玩家生成相同的级别。这也使调试游戏变得更加容易,就好像每次都可以生成相同的情况时,开发人员可以重新创建情况并查看所涉及的变量。对于使用随机函数生成数据的任何程序,情况也是如此。如果您为该随机性设定种子,那么可以预测地控制程序的输出,这使测试变得更加容易。
在计算机的早期,使用种子随机数作为压缩的手段。因此,不是将所有数据保存到磁盘,而是使用种子随机数生成器从单个值和算法重新创建所有数据。精英游戏就是一个很好的例子,您可以访问的数百个恒星系统的所有数据都存储为单个种子随机数生成器。
创建随机数
该rand()函数在PHP中使用MersenneTwister随机数生成器算法在两个数字之间生成一个随机数。下面的示例将生成一个介于1和100之间的数字。
$randomNumber = rand(1, 100);
每次我们调用此函数时,我们都会得到一个不同的随机数。在PHP中也有一个mt_rand()函数,但是从PHP7.1.0开始,rand()和都mt_rand()使用相同的基础算法来生成其编号。
创建种子随机数
要播种随机数,您只需在调用srand()之前在PHP脚本的开始处调用即可rand()。
srand(12345); $randomNumber = rand(1, 100);
现在,添加种子后,每次调用随机数时,我们都会获得确定性值。也就是说,rand()使用设置的种子进行调用将产生相同的结果。上面的脚本每次运行一次都会产生值“91”。
如果我们rand()在循环中运行此函数,则可以生成一个始终相同的随机数列表。
srand(12345); $randomNumbers = []; for ($i = 0; $i < 5; ++$i) { $randomNumbers[] = rand(1, 100); } print_r($randomNumbers);
这将始终打印出以下内容。
Array ( [0] => 91 [1] => 82 [2] => 86 [3] => 54 [4] => 85 )
生成种子随机图像
该序列如何始终相同的一个示例是通过生成填充有种子随机数据的图像。下面的代码块将生成一个包含随机像素的小图像,并使用种子生成图像中像素的位置。
$seed = 1; srand($seed); $gd = imagecreatetruecolor(250, 250); $white = imagecolorallocate($gd, 255, 255, 255); for ($i = 0; $i < 1000; $i++) { $x = rand(1, 250); $y = rand(1, 250); imagesetpixel($gd, round($x),round($y), $white); } imagepng($gd, 'file' . $seed . '-' . time() . '.png');
这将产生以下图像。
第二次运行此命令将再次产生完全相同的图像。第二张图片是另一张图片(不是,实际上是),是在第一张图片几秒钟后创建的。
种子实验
作为一个实验,我想看看哪个随机数是创建随机数的“最佳方法”。是的,我意识到这句话是徒劳的,但我认为值得研究。我创建了一些代码,该代码将10,000,000个数字看成是随机数生成器的种子,并显示出哪个出现次数最多。
// Initialise 'best' variables. $bestSeed = 0; $bestCount = 0; for ($i = 0; $i < 10000000; $i++) { //设置种子。 srand($i); //(重新)设置数字数组。 $numbers = []; while (TRUE) { //生成一个随机数。 $number = rand(1, 100); //如果数字在已创建的数字数组中,请将match设置为true以停止循环。 if (in_array($number, $numbers)) { break(1); } //将数字添加到数字数组。 $numbers[] = $number; } //计算我们添加到数组中的数字。 $count = count($numbers); if ($bestCount < $count) { //如果该计数高于当前的最佳计数,请重置该计数。 $bestCount = $count; $bestSeed = $i; } } echo 'Seed ' . $bestSeed . ' = ' . $bestCount . ' numbers';
运行几秒钟后,得出以下结果。
Seed1777423=53numbers
这意味着在我们发现一个包含53个不同数字的随机数字序列之前,它花费了1,777,423个不同的种子。在接下来的820万个数字中,我们没有发现更高的得分种子。
就像我之前说的那样,出于加密目的而使用rand()或mt_rand()生成随机性是很危险的,因为可以猜测这些值。将这些函数与种子一起使用意味着,如果发现使用的种子,则实际上很容易弄清生成了哪些随机数。