如何生成范围内(1,n)
但不在某个列表中的随机数(i,j)
?
示例:范围是(1,500)
,列表是[1,3,4,45,199,212,344]
。
注意:列表可能未排序
一种方法是拒绝抽样:
x
在 (1, 500) 范围内生成一个数字x
在您的不允许值列表中吗?(可以使用哈希集进行此检查。)
x
是你的随机值,完成如果您的允许值集明显大于不允许的值集,这将正常工作:
如果有G
可能的好值和可能的坏值,那么您必须从这些值B
中采样的预期次数,直到您获得好的价值是(相关几何分布的期望)。(您可以感觉到检查这一点。随着趋于无穷,期望趋于 1。趋于无穷,期望趋于无穷。)x
G + B
(G + B) / G
G
B
另一种方法是列出L
所有允许的值,然后是 sample L[rand(L.count)]
。
当列表长度为 1 时,我通常使用的技术是在 中生成一个随机整数r
,[1,n-1]
如果r
大于或等于该单个非法值,则递增r
。
这可以概括为k
小长度列表,k
但需要对该列表进行排序(您不能以随机顺序进行比较和增量)。如果列表长度适中,那么在排序之后,您可以从 bsearch 开始,并将跳过的值的数量添加到r
,然后递归到列表的其余部分。
对于长度列表k
,不包含大于或等于 的值n-k
,您可以进行更直接的替换:生成随机r
in [1,n-k]
,然后遍历列表测试是否r
等于list[i]
。如果它被设置r
为n-k+i
(这假设list
是从零开始的)并退出。
如果某些列表元素位于[n-k,n]
.
在这一点上,我可以尝试投资一些聪明的东西,但到目前为止,我所拥有的似乎足以满足均匀分布,其值k
远小于
n
......
n-k
,另一个是其余的(这可以就地完成)。r
生成[1,n-k]
r
然后list[i]
设置r
为n-k+i
并转到步骤 5)。r
在步骤 3 中没有更改,那么我们就完成了。观察:
r
被移入危险区域。k
接近n
,上层(已排序)列表的最大大小会增加。k
,如果更多值出现在上列表中(排序越大),在下列表中获得命中的机会就会缩小,从而降低需要进行排序的可能性。细化:
显然,对于 large k
,事情变得非常复杂,但在这种情况下,列表中r
允许解决的漏洞相对较少。这肯定可以被利用。
如果需要许多具有相同列表和限制的随机值,我可能会提出不同的建议。我希望非法值列表不是先前调用此函数的结果列表,因为如果是,那么您将不想要任何这些——相反,您会想要一个 Fisher-Yates shuffle。
如前所述,如果可能,拒绝抽样将是最简单的。但是,如果您不想使用它,您可以将范围和不允许的值转换为集合并找出差异。然后,您可以从中选择一个随机值。
假设您希望范围在 [1,n] 但不在 [i,j] 中,并且您希望它们均匀分布。
在 Python 中
total = range(1,n+1)
disallowed = range(i,j+1)
allowed = list( set(total) - set(disallowed) )
return allowed[random.randrange(len(allowed))]
(请注意,这很可能并不完全一致,max_rand%len(allowed) != 0
但在大多数实际应用中,这将非常接近)
我假设您知道如何在 [1, n) 中生成随机数,并且您的列表也像上面的示例一样排序。
假设您有一个包含 k 个元素的列表。制作一个 map(O(logn)) 结构,如果 k 变高,它将确保速度。将列表中的所有元素放入映射中,其中元素值将是键,“好”值将是值。稍后我将解释“好”的价值。因此,当我们拥有地图时,只需在 [1, n - k - p) 中找到一个随机数(稍后我将解释什么是 p),如果该数字在地图中,则将其替换为“好”值。
“GOOD”值 -> 让我们从第 k 个元素开始。它的好值是它自己的值 + 1,因为下一个元素对我们来说是“好”的。现在让我们看看第 (k-1) 个元素。我们假设它的好值再次是它自己的值 + 1。如果这个值等于第 k 个元素,那么第 (k-1) 个元素的“好”值是第 k 个“好”值 + 1。还有您将不得不存储最大的“好”值。如果最大值超过 n,则 p(从上面)将为 p = 最大 - n。
当然,我建议您仅在 k 很大时才这样做,否则@Timothy Shields 的方法是完美的。