在PHP中,我如何生成一个大的伪随机数?

16

我正在寻找一种使用PHP生成随机数的方法,类似于:

mt_rand($lower, $upper);

我所见过的最接近的函数是gmp_random(),然而它不允许我指定下限和上限,只能指定每个limb的位数(我不知道这是什么)。

编辑:Axsuuls的答案似乎非常接近我想要的,并且与gmp_random非常相似,但在一个场景中存在一个缺陷。

假设我想要获取一个介于以下两个数字之间的随机数:

  • 1225468798745475454898787465154

和:

  • 1225468798745475454898787465200

因此,如果函数命名为BigRandomNumber():

BigRandomNumber($length = 31);

这样的代码可能会返回超出指定范围的9999999999999999999999999999999。

我应该如何使用最小/最大边界而不是长度值?

BigRandomNumber('1225468798745475454898787465154', '1225468798745475454898787465200');

这应该返回一个在1225468798745475454898787465 [154 .. 200]之间的随机数。

提供参考,我认为解决方案可能需要使用此问题中提供的函数

编辑:上述帖子已被删除,在此是它:

function compare($number1, $operator, $number2) {
  $x = bccomp($number1, $number2);

  switch($operator) {
    case '<':
      return -1===$x;
    case '>':
      return 1===$x;
    case '=':
    case '==':
    case '===':
      return 0===$x;
    case '!=':
    case '!==':
    case '<>':
      return 0!==$x;
  }
}

2
你是在寻找一个随机数还是一个随机数字串?你打算用这个函数调用的结果做什么?如果你想要的值大于PHP_INT_MAX,那么操作它就成了一个问题。 - Scott Evernden
您最后提供的链接(“function supplied in this question”)已经损坏了。 - robguinness
在我看来,像问题描述的这样的函数对于需要生成随机大数的任何人都非常有用。我决定在我的应用程序中使用给出的被接受答案的函数,但在此之前,我进行了一些测试以衡量其性能。它表现得相当不错,我很乐意分享结果。我将首先尝试通过编辑被接受的答案来添加它们。 - robguinness
14个回答

17

尝试以下方法:

function BigRandomNumber($min, $max) {
  $difference   = bcadd(bcsub($max,$min),1);
  $rand_percent = bcdiv(mt_rand(), mt_getrandmax(), 8); // 0 - 1.0
  return bcadd($min, bcmul($difference, $rand_percent, 8), 0);
}

数学计算如下:将最大值和最小值之间的差乘以一个随机百分比,然后加上最小值(取整到整数)。


使用您的方法,只会有大约1亿种可能性。 - Robert L
所以将精度提高到16。实际上,这是生成1个随机数并将其“缩放”到适当范围的唯一有效方法。我不是统计学家。 - Robert K
1
为了模仿mt_rand()函数的功能,bcsub($max,$min);也应该是bcadd(bcsub($max, $min), 1);。 - Alix Axel
这段代码导致我的生产环境崩溃,我花了8个小时来找出问题所在。最终我发现即使$max的值大于1亿,它也只会生成最大数为1亿的随机数。在PHP 7.0中,请使用random_int函数。 - Jure Potocnik

7

你真正需要知道的是相对差异;如果它很小,那么你可以从0到最大差异中生成一个数字,然后将最小值加上。


这是这里最出色的答案。 - Shoe

3
这将给你更多的零位于你的巨大随机数中,你也可以指定巨大随机数的长度(你的巨大随机数能以0开头吗?如果不能,这也可以很容易地实现)。
<?php

$randNumberLength = 1000;  // length of your giant random number
$randNumber = NULL;

for ($i = 0; $i < $randNumberLength; $i++) {
    $randNumber .= rand(0, 9);  // add random number to growing giant random number

}

echo $randNumber;

?>

祝你好运!


你可以在最后将其转换为 int 以去除任何左侧的零。 - Vinko Vrsalovic
@Vinko,如果你将随机数转换为整数,你会得到科学计数法表示的数字。 - Alix Axel
@Axsuul:这是一个不错的方法,但我想指定数字的上下边界而不是数字长度,我该怎么做? - Alix Axel

1
你可以创建一些较小的随机数并将它们组合起来。不过我不确定你实际需要多大的随机数。

我有同样的想法,不确定生成的数字会有多随机。 - Alix Axel
1
啊呀,这几乎是随机的,但主要问题在于没有一个数字会以零开头。因此,在某些罕见情况下,零会更少见。 - Ólafur Waage
这真的取决于你的应用 - 我不会在加密或模拟中使用两个PRNG相加的方法,因为可能会出现Olafur指出的偏斜问题。 - Calyth
Calyth:这是针对一个概率对象的,我这里没有处理任何敏感信息。 - Alix Axel
生成的数字在范围(最小值-最大值)上分布不均。这可以通过分析在0和2之间生成随机数的方式来轻松验证,方法是将0到1之间的随机数相加。概率为0的可能性为25%(两个零),1的可能性为50%(0和1或1和0),2的可能性为25%(两个一)。 - soulmerge
显示剩余2条评论

1

大整数取值范围为1-20。 因此,使用mt_rand($lower, $upper)生成一个数字,并与另一个mt_rand($lower, $upper)相乘。

$N1=mt_rand($lower, $upper);
$N2=mt_rand($lower, $upper);
$N3=$N1*N2;

注意:n位数与n位数相乘将得到n*2位数的结果。

这样你就无法获得比最大bigint值更大的输出。 - mandza

0

这个问题似乎是十年前就被问过了,但对于从谷歌搜索而来的人来说,正确的答案是使用gmp_random_range("min_value", "max_value")函数。

$range=gmp_random_range("1225468798745475454898787465154", "1225468798745475454898787465200");
echo gmp_strval($range); // displays value as string

0
$lower = gmp_com("1225468798745475454898787465154");
$upper = gmp_com("1225468798745475454898787465200");

$range_size = gmp_sub($upper, $lower);

$rand = gmp_random(31);
$rand = gmp_mod($rand, $range_size);

$result = gmp_add($rand, $lower);

完全未经测试 :-)


0

这个方法可能适合你。 (我不确定你为什么需要它,所以这可能不是最好的方法,但它应该符合你的要求):

<?php
function bigRandomNumber($min, $max)
{
 // check input first
    if ($max < $min) { return false; }
    // Find max & min length of the number
    $lenMin = strlen ($min);
    $lenMax = strlen ($max);

    // Generate a random length for the random number
    $randLen = $lenMin + mt_rand(0, $lenMax - $lenMin);
    /* Generate the random number digit by digit, 
       comparing it with the min and max values */
 $b_inRange = false;
    for ($i = 0; $i < $randLen; $i++)
 {
  $randDigit = mt_rand(0,9);

  /* As soon as we are sure that the number will stay 
          in range, we can stop comparing it to min and max */
  if (!$b_inRange)
  {
   $tempRand = $rand . $randDigit;
   $tempMin = substr($min, 0, $i+1);
   $tempMax = substr($max, 0, $i+1);
   // Make sure that the temporary random number is in range
   if ($tempRand < $tempMin || $tempRand > $tempMax)
   {
    $lastDigitMin = substr($tempMin, -1);
    $lastDigitMax = substr($tempMax, -1);
    $tempRand = $rand . @mt_rand($lastDigitMin, $lastDigitMax);
   }
   /* Check if $tempRand is equal to the min or to the max value. 
               If it is not equal, then we know it will stay in range */
   if ($tempRand > $tempMin && $tempRand < $tempMax)
   {
    $b_inRange = true;
   }
  }
  else
  {
   $tempRand = $rand . $randDigit;
  }
  $rand = $tempRand;  
 }
 return $rand;
}

我尝试了几次,看起来工作正常。如果需要的话可以进行优化。思路是首先确定一个随机长度使得生成的随机数在可接受范围内,然后通过拼接逐个生成随机数字直到该长度。如果不在范围内,则生成一个新的在范围内的随机数字并拼接。

我利用了PHP将字符串转换为数字的特性,以便利用字符串函数。当然,这会对mt_rand生成警告,但由于我们只使用数字,所以应该安全地忽略它。

现在,我必须说我非常好奇你为什么需要这个。


0
/* Inputs: 
 * min - GMP number or string: lower bound
 * max - GMP number or string: upper bound
 * limiter - GMP number or string: how much randomness to use.
 *  this value is quite obscure (see `gmp_random`, but the default
 *  supplies several hundred bits of randomness, 
 *  which is probably enough.
 * Output: A random number between min (inclusive) and max (exclusive).
*/
function BigRandomNumber($min, $max, $limiter = 20) {
  $range = gmp_sub($max, $min);
  $random = gmp_random();
  $random = gmp_mod($random, $range);
  $random = gmp_add($min, $random);
  return $random;
}

这只是将经典公式 rand_range($min, $max) = $min + rand() % ($max - $min) 翻译为任意精度算术所得到的结果。如果 $max - $min 不是 2 的幂,则它可能会表现出一定程度的偏差,但如果随机性的位数足够高,相对于 $max - $min 的大小而言,偏差将变得可以忽略不计。

0
这可能有效:
  • 将数字拆分为一个包含9个数字或更少的数组(“其余部分”)... 9个字符,因为我的机器上最大的随机数是2147483647。
  • 对于每个“9个或更少数字数组块”,创建一个随机数。
  • 将数组合并,现在您将拥有一个可用的随机数。

演示该想法的示例代码(注意:代码未完成)

function BigRandomNumber($min,$max) {
// Notice: Will only work when both numbers have same length.
echo (strlen($min) !== strlen($max)) ? "Error: Min and Max numbers must have same length" : NULL;
$min_arr = str_split($min);
$max_arr = str_split($max);
// TODO: This loop needs to operate on 9 chars ($i will increment by $i+9)
for($i=0; $i<=count($max_arr); $i++) {
    if($i == 0) {
        // First number: >=first($min) and <=first($max).
        $new_arr[$i] = rand( $min_arr[0], $max_arr[0]);
    } else if($i == count($max_arr)) {
        // Last number <= $max .. not entirely correct, feel free to correct it.
        $new_arr[$i] = rand(0, substr($max,-1));
    } else {
        $new_arr[$i] = rand(0,9);
    }
}
return implode($new_arr);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接