Random\Randomizer::getFloat

(PHP 8 >= 8.3.0)

Random\Randomizer::getFloat获取均匀选择的浮点数

描述

public Random\Randomizer::getFloat(float $min, float $max, Random\IntervalBoundary $boundary = Random\IntervalBoundary::ClosedOpen): float

从请求的区间中返回一个均匀选择的、等分布的浮点数。

由于精度有限,并非所有实数都能精确地表示为浮点数。如果一个数不能精确表示,它将被舍入到最接近的可表示精确值。此外,浮点数在整个数轴上并不均匀分布。因为浮点数使用二进制指数,所以两个相邻浮点数之间的距离在每个二的幂处加倍。换句话说:从 1.02.0 之间存在与从 2.04.04.08.08.016.0 以及之后的相同数量的可表示浮点数。

出于这个原因,在请求的区间内随机采样一个任意数字(例如,通过除以两个整数)可能会导致偏向分布。必要的舍入会导致某些浮点数被返回的频率高于其他浮点数,尤其是在浮点数密度发生变化的二的幂附近。

Random\Randomizer::getFloat() 实现了一种算法,该算法将从请求的区间内最大可能的精确表示的、等分布的浮点数集中返回一个均匀选择的浮点数。可选择浮点数之间的距离(“步长”)与密度最低的浮点数之间的距离相匹配,即具有较大绝对值的区间边界处的浮点数之间的距离。这意味着,如果区间跨越一个或多个二的幂,则给定区间内的所有可表示浮点数可能不会被返回。步进将从绝对值较大的区间边界开始,以确保步进与精确表示的浮点数对齐。

闭合区间边界始终将包含在可选择浮点数集中。因此,如果区间的长度不是步长的精确倍数,并且绝对值较小的边界是闭合边界,则该边界与其最近的可选择浮点数之间的距离将小于步长。

注意事项

对返回的浮点数进行后处理很可能会破坏均匀等分布,因为数学运算中的中间浮点数会经历隐式舍入。请求的区间应尽可能地匹配所需的区间,并且舍入应仅作为显式操作在将选择的数字显示给用户之前执行。

使用示例值的算法解释

为了举例说明算法的工作原理,请考虑使用 3 位尾数的浮点表示。这种表示能够在连续的二的幂之间表示 8 个不同的浮点值。这意味着在 1.02.0 之间,所有大小为 0.125 的步长都是精确可表示的,而在 2.04.0 之间,所有大小为 0.25 的步长都是精确可表示的。实际上,PHP 的浮点数使用 52 位尾数,并且可以在每个二的幂之间表示 252 个不同的值。这意味着

  • 1.0
  • 1.125
  • 1.25
  • 1.375
  • 1.5
  • 1.625
  • 1.75
  • 1.875
  • 2.0
  • 2.25
  • 2.5
  • 2.75
  • 3.0
  • 3.25
  • 3.5
  • 3.75
  • 4.0
是在 1.04.0 之间精确可表示的浮点数。

现在假设调用了 $randomizer->getFloat(1.625, 2.5, IntervalBoundary::ClosedOpen),即请求一个从 1.625 开始到但不包括 2.5 的随机浮点数。算法首先确定绝对值较大的边界 (2.5) 处的步长。该边界处的步长为 0.25

请注意,请求的区间的长度为 0.875,这不是 0.25 的精确倍数。如果算法从下界 1.625 开始步进,它将遇到 2.125,它不能精确表示,并且会经历隐式舍入。因此,算法从上界 2.5 开始步进。可选择的值为

  • 2.25
  • 2.0
  • 1.75
  • 1.625
2.5 未包含在内,因为请求的区间的上界是开边界。 1.625 已包含在内,即使它与其最近的值 1.75 之间的距离为 0.125,这小于先前确定的步长 0.25。原因是请求的区间在低边界 (1.625) 处是闭合的,闭合边界始终包含在内。

最后,算法随机均匀地选择四个可选择值中的一个并返回它。

为什么除以两个整数不起作用

在前面的示例中,在每个由二的幂分隔的子区间之间,有八个可表示的浮点数。为了举例说明为什么除以两个整数不能很好地生成随机浮点数,请考虑从 0.0 开始到但不包括 1.0 的右开区间中有 16 个等分布的浮点数。它们中的一半是 0.51.0 之间的八个精确可表示的值,另一半是在 0.01.0 之间的值,步长为 0.0625。这些可以通过将 015 之间的随机整数除以 16 来轻松生成,以获得

  • 0.0
  • 0.0625
  • 0.125
  • 0.1875
  • 0.25
  • 0.3125
  • 0.375
  • 0.4375
  • 0.5
  • 0.5625
  • 0.625
  • 0.6875
  • 0.75
  • 0.8125
  • 0.875
  • 0.9375

这个随机浮点数可以通过将其乘以区间的长度 (0.875) 并加上最小值 1.625 来缩放为从 1.625 开始到但不包括 2.75 的右开区间。这种所谓的仿射变换将导致以下值

  • 1.625 舍入为 1.625
  • 1.679 舍入为 1.625
  • 1.734 舍入为 1.75
  • 1.789 舍入为 1.75
  • 1.843 舍入为 1.875
  • 1.898 舍入为 1.875
  • 1.953 舍入为 2.0
  • 2.007 舍入为 2.0
  • 2.062 舍入为 2.0
  • 2.117 舍入为 2.0
  • 2.171 舍入为 2.25
  • 2.226 舍入为 2.25
  • 2.281 舍入为 2.25
  • 2.335 舍入为 2.25
  • 2.390 舍入为 2.5
  • 2.445 舍入为 2.5
请注意,尽管 2.5 是开边界,因此被排除在外,但它仍将被返回。还要注意,2.02.25 被返回的概率是其他值的兩倍。

参数

min

区间的下界。

max

区间的上界。

boundary

指定区间边界是否为可能返回的值。

返回值

minmaxboundary 指定的区间内的均匀选择的、等分布的浮点数。minmax 是否为可能返回的值取决于 boundary 的值。

错误/异常

示例

示例 #1 Random\Randomizer::getFloat() 示例

<?php
$randomizer
= new \Random\Randomizer();

// 请注意,纬度的粒度是经度的两倍。
//
// 纬度值可以是 -90 和 90。
// 经度值可以是 180,但不能是 -180,因为
// -180 和 180 指的是同一个经度。
printf(
"Lat: %+.6f Lng: %+.6f",
$randomizer->getFloat(-90, 90, \Random\IntervalBoundary::ClosedClosed),
$randomizer->getFloat(-180, 180, \Random\IntervalBoundary::OpenClosed),
);
?>

上面的示例将输出类似以下内容

Lat: +69.244304 Lng: -53.548951

备注

注意:

此方法实现了 γ-section 算法,如 »  从区间中提取随机浮点数。 Frédéric Goualard, ACM Trans. Model. Comput. Simul., 32:3, 2022 中所述,以获得所需的特性。

参见

添加一个备注

用户贡献的备注

此页面没有用户贡献的备注。
To Top