在另一个帖子中,我看到二进制堆加权随机样本的时间复杂度等于O(n * log(m)),其中n是选择的数量,m是可供选择的节点数。
我想知道Python使用的未加权随机样本的时间复杂度为random.sample。时间复杂度只是O(n)还是完全不同于其他东西?
答案 0 :(得分:3)
Python源:random.py
(第267行)。
以下是相关位:
315 selected = set()
316 selected_add = selected.add
317 for i in range(k):
318 j = randbelow(n)
319 while j in selected:
320 j = randbelow(n)
321 selected_add(j)
322 result[i] = population[j]
它基本上将随机索引“掷骰子”变成population
。如果它获得了已经在集合selected
中的索引,则重新滚动。冲洗,起泡并重复k
次(其中k
是您要求的样品数量。)
所请求的样本数量似乎为O(n)
。对于小集合有一些优化,但事物的主要是上面的主循环。
编辑:
我认为,当请求的样本数量k
占总人口n
的很大一部分时,行305-313是一个特例。我们不是从整个总体中滚动随机元素(如果我们与我们已经选择的元素发生碰撞就重新滚动),我们明确地维护一个我们尚未选择的元素列表。我们保证每次都会得到一个新的元素,但权衡是我们必须维护这个列表。
如果我在解释这个错误,请随时在下面发表评论。
303 result = [None] * k
304 setsize = 21 # size of a small set minus size of an empty list
305 if k > 5:
306 setsize += 4 ** _ceil(_log(k * 3, 4)) # table size for big sets
307 if n <= setsize:
308 # An n-length list is smaller than a k-length set
309 pool = list(population)
310 for i in range(k): # invariant: non-selected at [0,n-i)
311 j = randbelow(n-i)
312 result[i] = pool[j]
313 pool[j] = pool[n-i-1] # move non-selected item into vacancy
314 else:
315 selected = set()
316 selected_add = selected.add
317 for i in range(k):
318 j = randbelow(n)
319 while j in selected:
320 j = randbelow(n)
321 selected_add(j)
322 result[i] = population[j]
323 return result
答案 1 :(得分:0)
来源:https://hg.python.org/cpython/file/ab500b297900/Lib/random.py
这个答案与上面的Li-aung Yip相似,但是我认为精度对于某些应用可能很重要。
在人口为n
且样本为k
的情况下,复杂度可能会受到人口输入类型的影响:如果人口是一组,则{ {3}}会将其复制到O(n)
的元组中,而不管n
或k
是什么。如果是序列,则无需进行预处理。
然后,如果k
足够大(请参阅下文),random.sample将使用以下循环(random.py的第305-313行),由于人口副本。
O(n)
与 if k > 5:
setsize += 4 ** _ceil(_log(k * 3, 4)) # table size for big sets
if n <= setsize:
# An n-length list is smaller than a k-length set
pool = list(population)
for i in range(k): # invariant: non-selected at [0,n-i)
j = randbelow(n-i)
result[i] = pool[j]
pool[j] = pool[n-i-1] # move non-selected item into vacancy
不同的平均情况复杂度的唯一希望是,当O(n)
足够小时,可以使用以下循环:
k
在一般情况下似乎为else:
selected = set()
selected_add = selected.add
for i in range(k):
j = randbelow(n)
while j in selected:
j = randbelow(n)
selected_add(j)
result[i] = population[j]
,但我不确定如何证明。如果我出错了,请随时发表评论。