【问题标题】:In PHP, how do I generate a big pseudo-random number?在 PHP 中,如何生成一个大的伪随机数?
【发布时间】:2010-12-01 13:51:45
【问题描述】:

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

mt_rand($lower, $upper);

我看到的更接近的是gmp_random(),但是它不允许我仅指定每个肢体的位数(我不知道它是什么)的下限和上限。

编辑:Axsuuls 的答案似乎与我想要的非常接近,并且与 gmp_random 非常相似,但是在一种情况下似乎只有一个缺陷。

假设我不想得到一个随机数:

  • 1225468798745475454898787465154

和:

  • 1225468798745475454898787465200

所以如果函数调用BigRandomNumber():

BigRandomNumber($length = 31);

这可以轻松返回超出指定边界的 9999999999999999999999999999999。

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

BigRandomNumber('1225468798745475454898787465154', '1225468798745475454898787465200');

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

作为参考,我认为解决方案可能必须使用function supplied in this question

编辑:上面的帖子已被删除,这里是:

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;
  }
}

【问题讨论】:

  • 您是在寻找随机数还是随机数字字符串? .. 你打算用这个函数调用的结果做什么?如果您想要的值是 > PHP_INT_MAX ,那么操作它就成了一个问题..
  • 您的最后一个链接(“此问题中提供的功能”)已损坏。
  • @robguinness:修复它。
  • 在我看来,像问题描述的那样的函数对于需要生成随机大数的人来说非常有用。我决定在我的应用程序中使用给出的函数作为接受的答案,但在这样做之前,我运行了一些测试来测量它的性能。它表现得很好,我很乐意分享结果。我将首先尝试通过编辑已接受的答案来添加它们。

标签: php random gmp bcmath


【解决方案1】:

这个问题是十年前问的,但对于来自谷歌的人来说,答案是 gmp_random_range("min_value", "max_value") 函数。

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

【讨论】:

    【解决方案2】:

    大整数的范围是 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 值更大的输出
    【解决方案3】:

    生成“n”个随机字符并不是一个真正的选择,因为 random('9999999999') 在理论上仍然可以返回 1...

    这是一个非常简单的函数:

    function bcrand($max) { 
        return bcmul($max, (string)mt_rand() / mt_getrandmax() ); 
    }
    

    注意它不会返回 N 位的随机性,只是调整比例

    【讨论】:

      【解决方案4】:

      尝试以下方法:

      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);
      }
      

      数学如下:将最小值和最大值之间的差乘以随机百分比,然后加上最小值(四舍五入为 int)。

      【讨论】:

      • 用你的方法,大概只有一亿种可能性。
      • 所以将精度提高到 16。确实,这是生成 1 个随机数然后将其“缩放”到适当范围内的唯一有效方法。我不是统计学家。
      • 模仿 mt_rand() 函数 bcsub($max,$min);也应该是 bcadd(bcsub($max, $min), 1);
      • 这段代码破坏了我的生产,我花了 8 个小时试图弄清楚这个 hack 出了什么问题。经过这么多小时后,我发现即使 $max 大于该值,这也会产生最大数量 1 亿。在 PHP 7.0 中使用:random_int.
      【解决方案5】:

      经过测试和工作

      <?php 
      
      $min = "1225468798745475454898787465154";
      $max = "1225468798745475454898787465200";
      
      $bigRandNum = bigRandomNumber($min,$max);
      echo "The Big Random Number is: ".$bigRandNum."<br />";
      
      function bigRandomNumber($min,$max) {
          // take the max number length
          $number_length = strlen($max);
      
          // Set the counter
          $i = 1;
      
          // Find the base and the min and max ranges
          // Loop through the min to find the base number
          while ($i <= $number_length) {
              $sub_string = substr($min, 0, $i);
      
              // format pattern
              $format_pattern = '/'.$sub_string.'/';
              if (!preg_match($format_pattern, $max)) {
                  $base = $sub_string;
      
                  // Set the min and max ranges
                  $minRange = substr($min, ($i - 1), $number_length);
                  $maxRange = substr($max, ($i - 1), $number_length);
      
                  // End while loop, we found the base
                  $i = $number_length;
              }
              $i++;
          }
          // find a random number with the min and max range
          $rand = rand($minRange, $maxRange);
      
          // add the base number to the random number
          $randWithBase = $base.$rand;
      
          return $randWithBase;
      }
      
      ?>
      

      【讨论】:

        【解决方案6】:

        这可能有效:

        • 将数字拆分为包含 9 个或更少数字(“其余”)的数组... 9 个字符,因为我的机器上的最大 rand 数为 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);
        }
        

        【讨论】:

          【解决方案7】:
          /* 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 的大小相比足够高,则偏差可以忽略不计。

          【讨论】:

            【解决方案8】:

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

            <?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 生成一个警告,但由于我们只使用数字,所以禁止它应该是安全的。

            现在,我不得不说,我很好奇你为什么首先需要这个。

            【讨论】:

              【解决方案9】:

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

              【讨论】:

              • 这是这里最精彩的答案。
              【解决方案10】:

              这是伪代码:

              
              // generate a random number between N1 and N2
              
              rangesize = N2 - N1 + 1
              randlen = length(rangesize) + 4 // the 4 is to get more digits to reduce bias
              temp = BigRandomNumber(randlen) // generate random number, "randlen" digits long
              temp = temp mod rangesize
              output N1 + temp
              
              

              注意事项:

              • 这里的所有算术(第二行除外)都必须是任意精度:为此请使用 bcmath 库
              • 在第二行中,“length”是位数,所以 1025 的“length”应该是 4

              【讨论】:

              • BigRandomNumber() 是缺少的函数。此外,您假设范围大小将小于 BigRandomNumber() 的上限。如果 BigRandomNumber() 的随机生成器是 mt_rand(),则该假设可能不成立,如果不是,则必须编写它,这就是这个问题的意义所在。
              • 我的回答中的BigRandomNumber函数是指Axsuul的回答。
              【解决方案11】:

              选择你的发言权和你的随机数在它的范围内。

              1225468798745475454898787465154 + rand(0, 6)
              

              【讨论】:

                【解决方案12】:
                $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);
                

                完全未经测试:-)

                【讨论】:

                  【解决方案13】:

                  这会给你的巨型随机数提供更多的零,你还可以指定巨型随机数的长度(你的巨型随机数可以以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 以消除左零的任何 amonut。
                  • @Vinko,如果您将随机数转换为 int,您将获得科学计数形式的数字。
                  • @Axsuul:这是一个很好的方法,但是我想指定数字的上下边界而不是数字长度我该怎么做?
                  【解决方案14】:

                  您可以做的是创建一些较小的随机数并将它们组合起来。但不确定您实际需要多大。

                  【讨论】:

                  • 我也有同样的想法,但不确定生成的数字有多随机。
                  • 它几乎和较小的随机数一样随机。
                  • 是的,它几乎是随机的,主要问题是没有一个数字会以零开头。因此,在极少数情况下,零会不太常见。
                  • 您可以完美地组合随机数。鉴于随机性的假设,即使将它们组合在一起,您也不会在随机性中丢失任何东西。只需按位连接它们就足够了。例如,您可以查看 Java 的 java.util.Random.nextDouble(),它只是从两个较小的随机数构建一个 53 位随机数。
                  • 这真的取决于您的应用程序 - 我不会使用为加密或模拟添加 2 个 PRNG 的 PRNG,因为 Olafur 指出可能存在偏差问题。
                  猜你喜欢
                  • 2014-05-18
                  • 1970-01-01
                  • 2014-10-30
                  • 2011-02-08
                  • 2015-08-07
                  • 1970-01-01
                  • 2022-06-19
                  • 2023-01-03
                  • 1970-01-01
                  相关资源
                  最近更新 更多