有没有办法为 random.uniform 设置打开和关闭端点?
Is there a way to set open and closed endpoints for random.uniform?
我有一个方法需要从 st
和 ed
之间的范围内 select 随机 float
val
,但我只想要包括的端点之一。 (或者,换句话说,(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 位的浮点数范围内获得两个等效值的几率,跨度超过(或恰好)一个整数基本上为零,但我会想知道是否只是为了更好地了解工具本身。
从广义上讲,您的解决方案似乎是合理的。如果您有一个 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。此外,对于浮点数,情况会变得更糟,而不是更好。由于浮点数不能精确表示所有实数,因此完全有可能(甚至可能)您的任何一个端点都不存在,因此 cannot 永远不会被返回,无论您指定的边界如何。
@EllaShar 有一个有趣的位旋转想法,但我不会在实践中推荐它。如果您确实需要精确控制边界,请坚持您原来的解决方案,它更简单、更具可读性、更清晰正确。
顺便说一句,如果你有一个 RNG returns 范围内的数字 [a, b)
并且你想获得范围 (a, b]
内的数字,你可以简单地取反你的范围, 例如
-random.randrange(-b, -a)
要生成一个在 [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的值对他们来说,得到closest_smaller_than_ed的概率为零。证明:
(我会用名字 adj_ed 来称呼 closest_smaller_than_ed)
方法 random.uniform(st, adj_ed)
等于 st + (adj_ed - st) * random.rand()
。当 random.rand()
给出小于 1 的最接近数字时,此处达到最大值,即 numpy.nextafter(1, 0)
.
所以问题是,是否有一个 ed 值使得 st + (adj_ed - st) * numpy.nextafter(1, 0) < adj_ed
。如果有这样一个ed,那么对于这个ed,用我的方法是不可能得到adj_ed的,因为我们能得到的最大数小于adj_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
编辑2:
我有一个新想法,但我不确定它是否能解决所有问题。
如果我们首先检查 random.uniform(st, 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
所以我们只是 return random.uniform(st, ed)
.
我有一个方法需要从 st
和 ed
之间的范围内 select 随机 float
val
,但我只想要包括的端点之一。 (或者,换句话说,(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 位的浮点数范围内获得两个等效值的几率,跨度超过(或恰好)一个整数基本上为零,但我会想知道是否只是为了更好地了解工具本身。
从广义上讲,您的解决方案似乎是合理的。如果您有一个 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。此外,对于浮点数,情况会变得更糟,而不是更好。由于浮点数不能精确表示所有实数,因此完全有可能(甚至可能)您的任何一个端点都不存在,因此 cannot 永远不会被返回,无论您指定的边界如何。
@EllaShar 有一个有趣的位旋转想法,但我不会在实践中推荐它。如果您确实需要精确控制边界,请坚持您原来的解决方案,它更简单、更具可读性、更清晰正确。
顺便说一句,如果你有一个 RNG returns 范围内的数字 [a, b)
并且你想获得范围 (a, b]
内的数字,你可以简单地取反你的范围, 例如
-random.randrange(-b, -a)
要生成一个在 [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的值对他们来说,得到closest_smaller_than_ed的概率为零。证明:
(我会用名字 adj_ed 来称呼 closest_smaller_than_ed)
方法 random.uniform(st, adj_ed)
等于 st + (adj_ed - st) * random.rand()
。当 random.rand()
给出小于 1 的最接近数字时,此处达到最大值,即 numpy.nextafter(1, 0)
.
所以问题是,是否有一个 ed 值使得 st + (adj_ed - st) * numpy.nextafter(1, 0) < adj_ed
。如果有这样一个ed,那么对于这个ed,用我的方法是不可能得到adj_ed的,因为我们能得到的最大数小于adj_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
编辑2:
我有一个新想法,但我不确定它是否能解决所有问题。
如果我们首先检查 random.uniform(st, 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
所以我们只是 return random.uniform(st, ed)
.