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

回答 13 浏览 3.5万 2013-01-03

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

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

Goaler444 提问于2013-01-03
有几个例子rand()的文档中Michael Berkowski 2013-01-03
这不是要给我返回一个整数吗?Goaler444 2013-01-03
Goaler 作为程序员,你的工作是利用算术将int值变成浮动值。这是在文档中的。@dystroy为了方便你,把它复制到这里。Michael Berkowski 2013-01-03
好的,谢谢 =)抱歉给您带来不便(我更习惯于使用Java)。Goaler444 2013-01-03
我不明白为什么这个问题被关闭。在我看来,它是完全合法的。我浏览了一下文档,没有找到答案。提名重新开放。Erick Robertson 2013-01-25
13 个回答
#1楼 已采纳
得票数 44

你可以使用标准函数。lcg_value().

这里是rand() docs上给出的另一个函数。

// auxiliary function
// returns random number with flat distribution from 0 to 1
function random_0_1() 
{
    return (float)rand() / (float)getrandmax();
}
Denys Séguret 提问
Denys Séguret 修改于2018-03-29
在PHP4和5中,mt_rand()rand()更受欢迎,因为它们能产生更多的随机值。return (float) mt_rand() / (float) mt_getrandmax();Tim S. 2015-02-20
在PHP7.1+中,rand现在是mt_rand的一个别名。apokryfos 2017-12-12
getrandmax和mt_getrandmax即使在64位系统上也会返回2147483647。所以你可以用random_int(0, 99999999999)/1000000000000000来提高分辨率。Nommyde 2021-08-26
#2楼
得票数 23

文件中的例子。

function random_float ($min,$max) {
   return ($min+lcg_value()*(abs($max-$min)));
}
Pierrickouw 提问于2013-01-03
+1 这是一个比较通用的方法,它将返回一个以给定的最小和最大浮动值为界限的随机浮动值。Erick Robertson 2013-01-30
#3楼
得票数 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

如果你想在小数点后有更多的数字,可以使用一个更大的数字。

Paul D Martini 提问于2013-11-20
#4楼
得票数 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()));
    }
}
YarikKotsur 提问于2013-10-16
YarikKotsur 修改于2015-03-21
#5楼
得票数 4

更新:忘掉这个答案吧,它对php -v > 5.3不起作用。

那什么是

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

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

 floatVal(rand(1, 15).'.'.rand(1, 9)); 
john Smith 提问于2013-08-30
john Smith 修改于2016-01-09
至少有一个:D,但请注意,这种 "造数 "与算术无关,尽管如此,它仍然是有效的。john Smith 2013-12-04
你得到的是什么错误? 我刚刚用5.4.36试了一下,我的第一个例子就成功了。john Smith 2015-02-18
啊,我明白了,sandbox.onlinephpfunctions.com/code/…john Smith 2015-02-18
#6楼
得票数 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

viruseg 提问于2016-08-01
viruseg 修改于2016-08-01
#7楼
得票数 3
$random_number = rand(1,10).".".rand(1,9);
Mohit Varshney 提问于2015-01-08
Pang 修改于2018-10-25
#8楼
得票数 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";
Tahir Yasin 提问于2016-01-09
#9楼
得票数 1

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

许多做这种心不在焉的事情,都会出现几十亿分之一的异常结果。如果你反过来思考执行操作,这就变得很明显。

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

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

把0到1说成是浮点数,可能有点像当你想要从0开始的10个值时,错误地把0到10说成是ints,而不是0到9。在这种情况下,由于可能的浮动值范围很广,那么它更像是不小心说了0到1000000000,而不是0到999999。

对于64位来说,溢出是非常罕见的,但在这种情况下,一些随机函数的内部是32位的,所以发生这种二十五亿分之一的机会也不是不可能的事。

标准的解决方案倒是希望像这样。

mt_rand() / (getrandmax() + 1)

在分布上也可能存在通常不明显的小差异,例如在0到9之间,那么你可能会发现由于精度的原因,0比9的可能性略大,但这通常是在十亿分之一左右,不像上述问题那么严重,因为上述问题会对本来无懈可击的计算产生一个无效的意外越界的数字。

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

这在错误报告中是一个有趣的来源。例如,任何使用lcg_value的PHP代码都没有考虑到这一点,如果它忠实于其文档,可能会出现大约几十亿分之一的故障,但这使得它很难忠实地重现。

这种错误是嵌入式设备中通常遇到的 "只需关闭并重新开启 "问题的常见来源之一。

jgmjgm 提问于2020-08-24
jgmjgm 修改于2020-08-24
#10楼
得票数 1

针对PHP7的解决方案。在[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"
mpen 提问于2016-10-11
mpen 修改于2022-08-08
(PHP_INT_MAX - 1) / PHP_INT_MAX正好是浮点数1.0000。所以这个函数包括了1。如果你不想包括1,请使用15位数或更少: random_int(0, 9999999999999) / 1000000000000000Nommyde 2021-08-26
@Nommyde 接得好...。我以为这可能只是显示的四舍五入,但看来是真的。我将对我的答案进行一些调整。mpen 2021-08-27
#11楼
得票数 0

大多数答案都是使用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-Indian字节顺序和Intel-style IEEE754表示法的64位机器。(x64兼容的计算机会有这个功能)。不幸的是,PHP不允许位移超过int32大小的边界,所以你必须为Big-Endian写一个单独的函数。

你应该替换掉这一行。

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

与它的big-endian对应关系。

    $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的类似数字)。

aphid 提问于2017-05-17
有一个更简单的方法来生成1和2之间的随机浮点数。然后你可以直接减去1。$a = random_bytes(8); $a[0] = "\x3f"; $a[1] = $a[1] | "\xf0"; return unpack('E', $a)[1] - 1.0;Nommyde 2021-08-26
@Nommyde 浮点数的精度取决于数值的绝对大小。通过减法,你会失去(一定量的)精度。在1.5和2之间的值,这只是1比特,但非常接近1的值将失去大量的精度。我相信我的答案允许所有非正常值都是可能的,或者说是2^52 * (2^10 - 1)个值,约占所有可能的浮点数的1/4,而那个方法只有2^52个可能的值,仅占所有可能的浮点数的1/1024。(读者可以做个练习。找出一个用其中一种方法而不是另一种方法生成的数字的例子)。aphid 2021-08-30
#12楼
得票数 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;
pwqwerty123 提问于2017-05-23
#13楼
得票数 -3

怎么样。

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

可能会工作得很好......希望能对你有所帮助。

EnrageDev 提问于2013-01-03
jgmjgm 修改于2020-08-24
-1 这不会提供0和1之间的随机分布。考虑到当随机数返回24时,它变成了0.24。真的,我想你要找的是0.000024。另一个问题是,一半的结果会在0.1和0.2之间,因为rand会在100000和199999的范围内。Erick Robertson 2013-01-30
标签