您将获得N
和int K[]
。
手头的任务是在0 to N-1
之间生成一个相等的概率随机数,这在K中是不存在的。
N
严格来说是一个整数>= 0
。
并且K.length
是&lt; N-1。并0 <= K[i] <= N-1
。还假设K已排序,K的每个元素都是唯一的。
您将获得一个函数uniformRand(int M)
,该函数生成0 to M-1
范围内的均匀随机数。并假设此函数的复杂度为O(1)。
示例:
N = 7
K = {0,1,5}
该函数应该返回任意随机数{2,3,4,6} 概率。
我可以得到一个O(N)解决方案:首先生成0到N-K.length之间的随机数。并将如此生成的随机数映射到不在K中的数字。第二步将复杂度设为O(N)。可以在O(log N)中做得更好吗?
答案 0 :(得分:5)
您可以使用K []中的所有数字介于0和N-1之间且不同的事实。
对于您的示例案例,您生成一个0到3的随机数。假设您得到一个随机数r
。现在,您在数组K []上进行二进制搜索。
Initialize i = K.length/2
。
查找K[i] - i
。这将为您提供0到i范围内数组中缺少的数字。
For example K[2] = 5. So 3 elements are missing from K[0] to K[2] (2,3,4)
因此,您可以决定是否必须在数组K的第一部分或下一部分中进行剩余搜索。这是因为您知道r
。
此搜索会为您提供log(K.length)
编辑:例如,
N = 7
K = {0, 1, 4} // modified the array to clarify the algorithm steps.
the function should return any random number { 2, 3, 5, 6 } with equal probability.
在0
和N-K.length
= random{0-3}
之间生成的随机数。假设我们得到3
。 因此我们需要数组K中的第4个缺失数字。
对数组K[]
进行二进制搜索。
Initial i = K.length/2 = 1
。现在我们看到K[1] - 1 = 0
。因此,i = 1
之前没有数字缺失。因此,我们搜索数组的后半部分。
现在i = 2. K[2] - 2 = 4 - 2 = 2
。因此,在2
索引之前有i = 2
个缺失的数字。但我们需要第4个缺失的元素。所以我们再次必须在数组的后半部分进行搜索。
现在我们到达一个空数组。我们现在该做什么?如果我们在K[j] & K[j+1]
之间找到一个空数组,那么它只是意味着数组K[j]
中缺少K[j+1]
和K
之间的所有元素。
因此,数组中缺少K[2]
以上的所有元素,即5
和6
。我们需要4th element
我们已经丢弃2 elements
。因此,我们将选择第6
个元素。
答案 1 :(得分:3)
二进制搜索。
(与其他答案不完全相同 - 数字仅在最后生成)
从K
开始。
通过查看当前值及其索引,我们可以确定左侧的可选数字(不在K
中的数字)。
同样,通过加入N
,我们可以确定右侧的可选数字。
现在随机向左或向右移动,根据每边可选数字的数量进行加权。
在选定的子阵列中重复,直到子阵列为空。
然后在数组中子阵列之前和之后的数字组成的范围内生成一个随机数。
运行时间为O(log |K|)
,自|K| < N-1
起,O(log N)
。
数字计数和权重的精确数学可以从下面的例子中得出。
现在让我们说(为了浓缩目的)K
也可以包含N
或更大的值。
然后,我们不是从整个K
开始,而是从位于min(N, |K|)
的子阵列开始,然后从中间开始。
很容易看到N
中的K
位置(如果存在)将为>= N
,因此这个选定的范围包括我们可以生成的任何可能的数字。
从这里开始,我们需要对N
进行二分搜索(这会给我们一个点,左边的所有值都是< N
,即使找不到N
也是如此)(上述算法不处理包含大于K
的值的N
。
然后我们只运行上面的算法,子阵列以最后一个值< N
结束。
运行时间为O(log N)
,或者更具体地说,O(log min(N, |K|))
。
N = 10
K = {0, 1, 4, 5, 8}
所以我们从中间开始 - 4
。
鉴于我们处于索引2,我们知道左边有2个元素,值为4,所以左边有4 - 2 = 2
个可选值。
同样,右侧有10 - (4+1) - 2 = 3
个可选值。
现在我们左侧的概率为2/(2+3)
,右侧的概率为3/(2+3)
。
假设我们走对了,我们的下一个中间值是5
。
我们处于此子阵列的第一个位置,之前的值为4
,因此我们左侧有5 - (4+1) = 0
个可选值。
右边有10 - (5+1) - 1 = 3
个可选值。
我们不能左转(概率为0)。如果我们走对了,我们的下一个中间值将是8
。
左侧有2
个可选值,右侧有1
个。
如果我们离开,我们就会有一个空的子阵列。
然后我们会在5
和8
之间生成一个数字,其概率为6
或7
。
答案 2 :(得分:1)
这可以通过基本解决这个问题来解决:
找到不在给定数组中的
rth
最小数字K,受制于 问题中的条件。
为此考虑 隐式 数组D,由
定义 D[i] = K[i] - i for 0 <= i < L, where L is length of K
我们还设置了D[-1] = 0
和D[L] = N
我们还定义了K[-1] = 0
。
注意,我们实际上并不需要构建D
。另请注意,D
已排序(所有元素均为非负数),因为K[]
中的数字是唯一且不断增加的。
现在我们提出以下要求:
索赔:要找到不在K []中的最小数字,我们需要找到最合适的r&#39;在D中(发生在由j定义的位置),其中r&#39; D中的最大数字是&lt;河这样的r&#39;存在,因为D [-1] = 0.一旦我们找到这样的r&#39; (和j),我们正在寻找的数字是r-r&#39; + K [j]。
证明:基本上r' and j
的定义告诉我们r'
中缺少0 to K[j]
个r
个数字,而且0 to K[j+1]
个数字超过K[j]+1 to K[j+1]-1
r-r'
失踪。因此,K[j] + r-r'
中的所有数字都丢失了(而且这些数字的数量至少为(r',j)
),我们寻找的数字就在其中,由r
给出。
<强>算法:强>
为了找到D
我们需要做的就是r
中{{1}}的(修改的)二进制搜索,即使我们找到{{{}},我们也会继续向左移动1}}在数组中。
这是一个O(log K)算法。
答案 3 :(得分:0)
如果您多次运行,可能需要加快您的生成操作:O(log N)
时间是不可接受的。
制作一个空数组G
。从零开始,在K
的值前进时向上计数。如果某个值不在K
中,请将其添加到G
。如果它在K
中,请不要添加它并继续K
指针。 (这依赖于K
被排序。)
现在你有一个只有可接受数字的数组G
。
使用随机数生成器从G中选择一个值。
这需要O(N)
准备工作,每一代都在O(1)
时间内完成。查看N
次后,所有操作的摊还时间为O(1)
。
Python模型:
import random
class PRNG:
def __init__(self, K,N):
self.G = []
kptr = 0
for i in range(N):
if kptr<len(K) and K[kptr]==i:
kptr+=1
else:
self.G.append(i)
def getRand(self):
rn = random.randint(0,len(self.G)-1)
return self.G[rn]
prng=PRNG( [0,1,5], 7)
for i in range(20):
print prng.getRand()