我有一种方法需要从float
和val
之间选择一个随机st
ed
,但我只想要包含一个端点。 (或者,换句话说,(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
有答案?
我知道在十三个精度范围内获得两个等价值的几率超过(或精确)一个整数的概率基本上是零,但我想知道是否只是为了更好地理解工具本身。
答案 0 :(得分: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)
答案 1 :(得分:0)
要在舍入后生成[st,ed]中的数字,您可以执行random.uniform(st, closest_smaller_than_ed)
。
要获得最接近编辑的编号,该编号小于编辑使用(请参阅this):
numpy.nextafter(ed, ed - 1)
您可能需要检查ed != st
以避免生成不在[st, ed)
中的号码。
同样,要获得val(st,ed)do:
if st == ed:
return ed
st_adjusted = numpy.nextafter(st, st + 1)
return random.uniform(st_adjusted, ed)
修改强>
dimo414的评论是正确的,有ed的值,对于他们来说,获得nearest_smaller_than_ed的概率将为零。证明:
(我将通过名称adj_ed调用nearest_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) < adj_ed
的ed值。如果有这样的ed,那么对于这个ed,没有机会使用我的方法获得adj_ed,因为我们可以得到的最大数字小于adj_ed。
答案是肯定的,就像OP建议的那样,有st = 0; ed = 360
这样的编辑。
这给出了:
>>> 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)
可以给出的最大值是什么(如果它不是,那么一切都好,没有什么可以解决的)。只有这样我们才会按照之前的建议使用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)
。