PHP 中的 0 到 1 之间的随机浮点数


Random Float between 0 and 1 in PHP

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

我正在寻找相当于Java Math.random()的PHP。

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

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

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

文档中的示例:

function random_float ($min,$max) {
   return ($min+lcg_value()*(abs($max-$min)));
}
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()));
    }
}
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

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

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

怎么样

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

这对我来说非常有效,它不仅适用于 0 - 1,例如在 1.0 - 15.0 之间

 floatVal(rand(1, 15).'.'.rand(1, 9)); 
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

$random_number = rand(1,10).".".rand(1,9);

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

此问题要求一个介于 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代码如果忠实于其文档,可能会出现故障大约几十亿分之一,但这会使忠实地复制变得非常困难。

这种由一个错误关闭是嵌入式设备中通常遇到的"只需将其关闭并再次打开"问题的常见来源之一。

大多数答案都使用mt_rand。但是,mt_getrandmax()通常只返回2147483647 。这意味着你只有 31 位信息,而双精度有一个 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 的类似数字(的输出。

我在 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;

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

可能会正常工作...希望对您有所帮助。