random.sample的时间复杂度

时间:2012-05-07 13:55:27

标签: python time-complexity

在另一个帖子中,我看到二进制堆加权随机样本的时间复杂度等于O(n * log(m)),其中n是选择的数量,m是可供选择的节点数。

我想知道Python使用的未加权随机样本的时间复杂度为random.sample。时间复杂度只是O(n)还是完全不同于其他东西?

2 个答案:

答案 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)的元组中,而不管nk是什么。如果是序列,则无需进行预处理。

然后,如果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] ,但我不确定如何证明。如果我出错了,请随时发表评论。