【问题标题】:Random Float between 0 and 1 in PHPPHP中0到1之间的随机浮点数
【发布时间】:2012-12-17 22:49:49
【问题描述】:

如何在 PHP 中生成 0 到 1 之间的随机浮点数?

我正在寻找与 Java 的 Math.random() 等效的 PHP。

【问题讨论】:

  • 有几个例子in the rand() docs
  • 不会返回一个整数吗?
  • Goaler 作为程序员,您的工作是使用算术从 int 值生成浮点值。它在文档中。为方便起见,@dystroy 已将其复制到此处。
  • 好的,谢谢 =) 给您带来的不便很抱歉(我更习惯 Java)
  • 我不明白为什么这个问题被关闭了。这对我来说似乎完全合法。我浏览了文档并找不到答案。提名重新开放。

标签: php random


【解决方案1】:

您可以使用标准函数:lcg_value()

这是rand() 文档中给出的另一个函数:

// auxiliary function
// returns random number with flat distribution from 0 to 1
function random_0_1() 
{
    return (float)rand() / (float)getrandmax();
}

【讨论】:

  • 在 PHP 4 和 5 中,mt_rand() 优于 rand(),因为它们会生成更多随机值。 return (float) mt_rand() / (float) mt_getrandmax();
  • 在 PHP 7.1+ 中 rand 现在是 mt_rand 的别名
  • getrandmax 和 mt_getrandmax 即使在 64 位系统上也返回 2147483647。因此,您可以使用 random_int(0, 999999999999999) / 1000000000000000 来提高分辨率
【解决方案2】:

文档示例:

function random_float ($min,$max) {
   return ($min+lcg_value()*(abs($max-$min)));
}

【讨论】:

  • +1 这是一种更通用的方法,它将返回一个随机浮点值,该值由给定的最小和最大浮点值限制。
【解决方案3】:
rand(0,1000)/1000 returns:
0.348 0.716 0.251 0.459 0.893 0.867 0.058 0.955 0.644 0.246 0.292

如果您希望小数点后有更多位数,请使用更大的数字

【讨论】:

    【解决方案4】:
    class SomeHelper
    {
         /**
         * Generate random float number.
         *
         * @param float|int $min
         * @param float|int $max
         * @return float
         */
        public static function rand($min = 0, $max = 1)
        {
            return ($min + ($max - $min) * (mt_rand() / mt_getrandmax()));
        }
    }
    

    【讨论】:

      【解决方案5】:

      更新: 忘记这个答案,它不适用于 php -v > 5.3

      怎么样

      floatVal('0.'.rand(1, 9)); 
      

      这对我来说是完美的,它不仅适用于 0 - 1,例如 1.0 - 15.0 之间

       floatVal(rand(1, 15).'.'.rand(1, 9)); 
      

      【讨论】:

      • 至少一个 :D 但请注意这个“数字构建”与算术无关,但它的工作原理
      • 你得到什么错误?我刚用 5.4.36 试过,我的第一个例子就可以了
      【解决方案6】:
      function mt_rand_float($min, $max, $countZero = '0') {
          $countZero = +('1'.$countZero);
          $min = floor($min*$countZero);
          $max = floor($max*$countZero);
          $rand = mt_rand($min, $max) / $countZero;
          return $rand;
      }
      

      示例:

      echo mt_rand_float(0, 1);
      

      结果:0.2

      echo mt_rand_float(3.2, 3.23, '000');
      

      结果:3.219

      echo mt_rand_float(1, 5, '00');
      

      结果:4.52

      echo mt_rand_float(0.56789, 1, '00');
      

      结果:0.69

      【讨论】:

        【解决方案7】:
        $random_number = rand(1,10).".".rand(1,9);
        

        【讨论】:

          【解决方案8】:
          function frand($min, $max, $decimals = 0) {
            $scale = pow(10, $decimals);
            return mt_rand($min * $scale, $max * $scale) / $scale;
          }
          
          echo "frand(0, 10, 2) = " . frand(0, 10, 2) . "\n";
          

          【讨论】:

            【解决方案9】:

            这个问题要求一个从 0 到 1 的值。对于大多数数学目的来说,这通常是无效的,尽管程度最低。按照惯例,标准分布是 0 >= N

            许多这样做的事情都心不在焉,导致异常结果的几十亿分之一。如果您考虑向后执行操作,这一点就会变得很明显。

            (int)(random_float() * 10) 将返回一个从 0 到 9 的值,每个值的机会均等。如果在十亿分之一中它可以返回 1,那么它很少会返回 10。

            有些人会在事后解决这个问题(决定 10 应该是 9)。将它乘以 2 应该有大约 50% 的机会得到 0 或 1,但也有大约 0.000000000465% 的机会返回像 Bender's dream 这样的 2。

            当您想要从零开始的十个值时,将 0 到 1 说成浮点数可能有点像错误地将 0 到 10 而不是 0 到 9 说成整数。在这种情况下,由于可能的浮点值范围很广,所以更像是不小心说 0 到 1000000000 而不是 0 到 999999999。

            对于 64 位,溢出极为罕见,但在这种情况下,一些随机函数在内部是 32 位的,因此发生二分之一半的机会并非不可信。

            标准解决方案应该是这样的:

            mt_rand() / (getrandmax() + 1)
            

            在分布上也可能存在通常微不足道的差异,例如在 0 到 9 之间,那么由于精度,您可能会发现 0 比 9 的可能性稍高,但这通常在十亿分之一左右,并不像上面的问题,因为上面的问题可能会产生一个无效的意外超出范围的计算,否则将是完美的。

            Java 的 Math.random 也永远不会产生 1 的值。其中一些原因在于,具体解释它的作用是拗口的。它返回一个从 0 到小于 1 的值。这是芝诺的箭,它永远不会达到1。这不是人们通常会说的。相反,人们倾向于说介于 0 和 1 之间或从 0 到 1,但这些都是错误的。

            这在某种程度上是错误报告中的娱乐来源。例如,如果不考虑这一点而使用 lcg_value 的任何 PHP 代码,如果它忠实于其文档,则可能会出现大约数十亿分之一的故障,但这使得忠实地重现变得非常困难。

            这种错误关闭是“只需将其关闭然后再打开”的常见来源之一。嵌入式设备中通常会遇到的问题。

            【讨论】:

              【解决方案10】:

              大多数答案都使用mt_rand。但是,mt_getrandmax() 通常只返回2147483647。这意味着你只有 31 位的信息,而 double 有一个 52 位的尾数,这意味着 0 到 1 之间的数字的密度至少为 2^53

              这种更复杂的方法会让你得到更精细的分布:

              function rand_754_01() {
                  // Generate 64 random bits (8 bytes)
                  $entropy = openssl_random_pseudo_bytes(8);
                  // Create a string of 12 '0' bits and 52 '1' bits. 
                  $x = 0x000FFFFFFFFFFFFF;
                  $first12 = pack("Q", $x);
                  // Set the first 12 bits to 0 in the random string. 
                  $y = $entropy & $first12;
                  // Now set the first 12 bits to be 0[exponent], where exponent is randomly chosen between 1 and 1022. 
                  // Here $e has a probability of 0.5 to be 1022, 0.25 to be 1021, etc. 
                  $e = 1022;     
                  while($e > 1) {   
                      if(mt_rand(0,1) == 0) {
                          break;
                      } else {
                          --$e;
                      }
                  }
                  // Pack the exponent properly (add four '0' bits behind it and 49 more in front)
                  $z = "\0\0\0\0\0\0" . pack("S", $e << 4);
                  // Now convert to a double. 
                  return unpack("d", $y | $z)[1];          
              }
              

              请注意,上述代码仅适用于具有 Litte-Endian 字节顺序和 Intel 风格的 IEEE754 表示的 64 位机器。 (x64-兼容的计算机会有这个)。不幸的是,PHP 不允许位移超过int32 大小的边界,因此您必须为 Big-Endian 编写一个单独的函数。

              你应该替换这一行:

                  $z = "\0\0\0\0\0\0" . pack("S", $e << 4);
              

              与其对应的大端:

                  $z = pack("S", $e << 4) .  "\0\0\0\0\0\0";
              

              只有在函数被大量调用时才显着区别:10^9 或更多。

              测试是否可行

              很明显,尾数遵循一个很好的均匀分布近似值,但大量此类分布的总和(每个分布的概率和幅度都减半)是均匀的就不那么明显了。

              跑步:

              function randomNumbers() {
                  $f = 0.0;
                  for($i = 0; $i < 1000000; ++$i) {
                      $f += \math::rand_754_01();
                  }
                  echo $f / 1000000;
              }
              

              产生0.49999928273099(或接近 0.5 的类似数字)的输出。

              【讨论】:

              • 有一种更简单的方法可以在 1 和 2 之间生成随机浮点数。然后你可以减去 1:$a = random_bytes(8); $a[0] = "\x3f"; $a[1] = $a[1] | "\xf0"; return unpack('E', $a)[1] - 1.0;
              • @Nommyde 浮点精度取决于值的绝对大小。通过减法,您会失去(一些)精度。在 1.5 和 2 之间的值,这只是 1 位,但非常接近 1 的值会损失大量的精度。我相信我的回答允许所有非规范值都是可能的,或者 2^52 * (2^10 - 1) 值,大约是所有可能的浮点数的 1/4,而该方法只有 2^52 个可能的值,只有所有可能的浮点数的 1/1024。 (读者练习:找出一个由一个生成的数字而不是另一个生成的数字的例子。)
              【解决方案11】:

              我在PHP.net找到了答案

              <?php
              function randomFloat($min = 0, $max = 1) {
                  return $min + mt_rand() / mt_getrandmax() * ($max - $min);
              }
              
              var_dump(randomFloat());
              var_dump(randomFloat(2, 20));
              ?>
              
              
              float(0.91601131712832)
              float(16.511210331931)
              

              所以你可以这样做

              randomFloat(0,1);
              

              或者简单的

              mt_rand() / mt_getrandmax() * 1;
              

              【讨论】:

                【解决方案12】:

                PHP 7 的解决方案。在[0,1) 中生成随机数。即包含 0 并排除 1。

                function random_float() {
                    return random_int(0, 2**53-1)/(2**53);
                }
                

                感谢 cmets 中的 Nommyde 指出我的错误。

                >>> number_format((2**53-1)/2**53,100)
                => "0.9999999999999998889776975374843459576368331909179687500000000000000000000000000000000000000000000000"
                >>> number_format((2**53)/(2**53+1),100)
                => "1.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
                

                【讨论】:

                • (PHP_INT_MAX - 1) / PHP_INT_MAX 正好是浮点数 1.0000。所以这个函数包括1。如果你不想包括1,使用15位或更少:random_int(0, 999999999999999) / 1000000000000000
                • @Nommyde 很好...我认为这可能只是显示四舍五入,但它似乎是真的。会稍微调整一下我的答案。
                【解决方案13】:

                怎么样:

                echo (float)('0.' . rand(0,99999));
                

                可能会工作得很好......希望它可以帮助你。

                【讨论】:

                • -1 这不会提供 0 和 1 之间的随机分布。考虑当随机数返回 24 并变为 0.24 时。真的,我认为您正在寻找 0.000024。另一个问题是一半的结果将在 0.1 和 0.2 之间,因为 rand 将在 100000 和 199999 的范围内。
                猜你喜欢
                • 2014-09-22
                • 2011-07-07
                • 2012-12-18
                • 1970-01-01
                • 2023-04-05
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多