【问题标题】:Is there a way to set open and closed endpoints for random.uniform?有没有办法为 random.uniform 设置开放和封闭端点?
【发布时间】:2015-04-17 20:48:24
【问题描述】:

我有一种方法需要从sted 之间的范围内选择一个随机的floatval,但我只想要包含其中一个端点。 (或者,换句话说,(st, ed][st, ed)。)我目前的解决方案是:

import random
val = None
st, ed = 0, 360
  ## In application, what I want is an angle, but I've made it generic as an example.
while not (st <= val < ed): # which of st or ed to reject is up to you.
    val = random.uniform(st, ed)

...但是有没有办法(通过参数或符号)告诉random.uniform(或类似的函数)包含或排除一个端点或另一个端点?或许NumPy 有答案?

我知道在具有 13 个精度的浮点范围内获得两个等效值的几率基本上为零,但我想知道是否只是为了更好地理解工具本身。

【问题讨论】:

  • 不包括上限
  • @PadraicCunningham 不根据文档:“根据四舍五入获取范围 [a, b) 或 [a, b] 中的随机数”
  • @EllaShar 除非我弄错了,这就是“取决于舍入”的意思;由于浮点运算不精确,对于ab 的某些值,可以从random.uniform(a, b) 获得b
  • 如果考虑到舍入误差,那么 uniform 实际上并不统一,是吗?
  • 另外,一个包含右边界的 uniform 调用的显式示例:random.uniform(2.**53, 2.**53+2)

标签: python random set endpoint


【解决方案1】:

从广义上讲,您的解决方案似乎是合理的。如果您有一个 RNG 提供的值范围大于您需要的范围,则您丢弃无效结果并继续计算。显然,如果您的无效范围太大,这可能会永远运行下去,但最终您可能不得不做出牺牲。

事实上,这就是 Python 首先生成随机值的方式。来自random._randbelow()

k = n.bit_length()  # don't use (n-1) here because n can be 1
r = getrandbits(k)          # 0 <= r < 2**k
while r >= n:
    r = getrandbits(k)
return r

它不断生成低于2**k 的随机整数,直到结果低于所需的n。就像你想要做的那样。

也就是说,对于您的具体情况,您不需要这样做 - 担心浮点范围的端点是徒劳的。

首先,正如@user2357112 指出的那样,考虑均匀分布的开放与封闭端点在某种程度上是一种迂腐的做法。在实数行(或任何子集)上,随机选择端点的概率为 0。此外,对于浮点数,情况会变得更糟,而不是更好。由于浮点不能精确地表示所有实数,因此完全有可能(甚至很可能)您的任何一个端点都不存在,因此无论您指定的边界如何,都无法返回

@EllaShar 有个有趣的点子,但我不会在实践中推荐它。如果您确实需要精确控制边界,请坚持原来的解决方案,它更简单、更易读且更清晰正确。


顺便说一句,如果您有一个 RNG 返回范围 [a, b) 中的数字,并且您想要获取范围 (a, b] 中的数字,您可以简单地否定您的范围,例如

-random.randrange(-b, -a)

【讨论】:

    【解决方案2】:

    要在舍入后生成 [st, ed) 中的数字,您可以执行random.uniform(st, closest_smaller_than_ed)

    要获得最接近 ed 且小于 ed 的数字,请使用(请参阅this):

    numpy.nextafter(ed, ed - 1)
    

    您可能需要检查ed != st 以避免生成不在[st, ed) 中的数字。

    同样,要在 (st, ed] 中获取 val:

    if st == ed:
        return ed
    st_adjusted = numpy.nextafter(st, st + 1)
    return random.uniform(st_adjusted, ed)
    

    编辑:

    dimo414 的评论是正确的,有 ed 的值对他们来说,得到最接近的_smaller_than_ed 的概率为零。证明:

    (我会用 adj_ed 这个名字来称呼最近的_smaller_than_ed)

    方法random.uniform(st, adj_ed) 等于st + (adj_ed - st) * random.rand()。当random.rand() 给出小于 1 的最接近的数字,即numpy.nextafter(1, 0) 时,达到此处的最大值。

    所以问题是,有没有像st + (adj_ed - st) * numpy.nextafter(1, 0) &lt; adj_ed 这样的 ed 值。如果有这样一个ed,那么对于这个ed,用我的方法是没有机会得到adj_ed的,因为我们能得到的最大数小于adj_ed。

    答案是肯定的,有这样一个 ed:st = 0; ed = 360,正如 OP 所建议的那样。

    这给出了:

    >>> st = 0
    >>> ed = 360
    >>> adj_ed = numpy.nextafter(ed, ed - 1)
    >>> adj_1 = numpy.nextafter(1, 0)
    >>> st + (adj_ed - st) * adj_1
    359.99999999999989
    >>> adj_ed
    359.99999999999994
    >>> st + (adj_ed - st) * adj_1 == numpy.nextafter(adj_ed, adj_ed - 1)
    True
    

    EDIT2:

    我有一个新想法,但我不确定它是否能解决所有问题。

    如果我们首先检查random.uniform(st, ed) 可以给出的最大值是 ed 怎么办(如果不是,那么一切正常,没有什么可解决的)。只有这样我们才能使用我之前建议的random.uniform(st, closest_smaller_than_ed)

    def does_uniform_include_edge(st, ed):
        adj_1 = numpy.nextafter(1, 0)
        return st + (ed - st) * adj_1 == ed
    
    def uniform_without_edge(st, ed):
        if does_uniform_include_edge(st, ed):
            adj_ed = numpy.nextafter(ed, ed - 1)
            # if there is an ed such that does_uniform_include_edge(st, adj_ed) is False then this solution is wrong. Is there?
            return random.uniform(st, adj_ed)
        return random.uniform(st, ed)
    
    print(uniform_without_edge(st, ed))
    

    对于st = 0; ed = 360,我们有does_uniform_include_edge(st, ed) == False,所以我们只返回random.uniform(st, ed)

    【讨论】:

    • 但是如果closest_smaller_than_ed没有被uniform四舍五入,就会错误地排除在结果范围之外,不是吗?
    • 可以肯定,这是一个聪明的解决方案。它可能有效。但是很难确定,也很容易一不小心就做错了。 OP 的解决方案虽然在最坏的情况下效率略低,但更简单,更容易在心理上验证。
    • 我真的很想证明这一点。或者证明它是错误的。同时,这是一种矫枉过正,但如果我的不起作用,您可以使用 OP 的解决方案 - 只需插入:if not does_uniform_include_edge(st, adj_ed): OP's solution
    猜你喜欢
    • 1970-01-01
    • 2014-08-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-09
    • 2019-12-22
    • 2013-08-14
    相关资源
    最近更新 更多