43

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

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

4

13 回答 13

42

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

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

// auxiliary function
// returns random number with flat distribution from 0 to 1
function random_0_1() 
{
    return (float)rand() / (float)getrandmax();
}
于 2013-01-03T14:56:15.183 回答
23

文档中的示例:

function random_float ($min,$max) {
   return ($min+lcg_value()*(abs($max-$min)));
}
于 2013-01-03T14:58:11.127 回答
7
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

或者如果您想要小数点后的更多位数,请使用更大的数字

于 2013-11-20T16:12:12.613 回答
7
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()));
    }
}
于 2013-10-16T10:32:16.657 回答
4

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

关于什么

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

?

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

 floatVal(rand(1, 15).'.'.rand(1, 9)); 
于 2013-08-30T09:25:18.407 回答
4
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

于 2016-08-01T04:35:44.583 回答
3
$random_number = rand(1,10).".".rand(1,9);
于 2015-01-08T11:04:43.580 回答
1
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";
于 2016-01-09T14:46:56.953 回答
1

这个问题要求一个从 0 到 1 的值。对于大多数数学目的,这通常是无效的,尽管程度最低。按照惯例,标准分布是 0 >= N < 1。您应该考虑是否真的想要包含 1 的东西。

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

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

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

当您想要从零开始的十个值时,将 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 代码,如果它符合其文档,则可能会出现大约数十亿分之一的故障,但这使得忠实地重现变得非常困难。

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

于 2020-08-24T15:04:53.433 回答
0

我在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;
于 2017-05-23T12:15:38.860 回答
0

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

这种更复杂的方法将为您提供更精细的分布:

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-size 的边界,所以你必须为 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 的类似数字)的输出。

于 2017-05-17T17:35:11.823 回答
0

PHP 7 的解决方案。在[0,1). 即包括0,不包括1。

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

感谢Nommyde在评论中指出我的错误。

>>> number_format((2**53-1)/2**53,100)
=> "0.9999999999999998889776975374843459576368331909179687500000000000000000000000000000000000000000000000"
>>> number_format((2**53)/(2**53+1),100)
=> "1.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
于 2016-10-11T21:05:09.040 回答
-3

关于什么:

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

可能会工作正常......希望它可以帮助你。

于 2013-01-03T14:59:47.357 回答