PHP中0到1之间的随机浮点数
如何在PHP中生成0到1之间的随机浮点数?
我在寻找相当于Java的Math.random()
的PHP。
你可以使用标准函数。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();
}
mt_rand()
比rand()
更受欢迎,因为它们能产生更多的随机值。return (float) mt_rand() / (float) mt_getrandmax();
- Tim S. 2015-02-20
rand
现在是mt_rand
的一个别名。
- apokryfos 2017-12-12
文件中的例子。
function random_float ($min,$max) {
return ($min+lcg_value()*(abs($max-$min)));
}
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
如果你想在小数点后有更多的数字,可以使用一个更大的数字。
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()));
}
}
更新:忘掉这个答案吧,它对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);
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,如 本德的梦想。
把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代码都没有考虑到这一点,如果它忠实于其文档,可能会出现大约几十亿分之一的故障,但这使得它很难忠实地重现。
这种错误是嵌入式设备中通常遇到的 "只需关闭并重新开启 "问题的常见来源之一。
针对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"
大多数答案都是使用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的类似数字)。
$a = random_bytes(8); $a[0] = "\x3f"; $a[1] = $a[1] | "\xf0"; return unpack('E', $a)[1] - 1.0;
- Nommyde 2021-08-26
<?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));
可能会工作得很好......希望能对你有所帮助。