Python3中随机选择(列表)的大O复杂性

Python3中随机选择(列表)的大O复杂性,python,python-3.x,random,complexity-theory,Python,Python 3.x,Random,Complexity Theory,在Python3中,random.choice(列表)的大O复杂性是什么,其中n是列表中元素的数量 编辑:谢谢大家给我答案,现在我明白了。O(1)。或者更准确地说,它相当于大O随机访问时间,用于按您传递的任何顺序查找单个索引,list具有O(1)随机访问索引(就像tuple一样) 一个例子,它是 o(n)< /> >是代码>集合。DeQue/代码>,在中的索引> DeQue>代码>是 o(n)< /代码>(具有较大的常数除数),所以它不贵,除非 DEQu达到数千个元素范围或更高)。因此,基本上,

在Python3中,random.choice(列表)的大O复杂性是什么,其中n是列表中元素的数量

编辑:谢谢大家给我答案,现在我明白了。

O(1)
。或者更准确地说,它相当于大O随机访问时间,用于按您传递的任何顺序查找单个索引,
list
具有
O(1)
随机访问索引(就像
tuple
一样)


一个例子,它是<代码> o(n)< /> >是代码>集合。DeQue/代码>,在<代码>中的索引> DeQue>代码>是<代码> o(n)< /代码>(具有较大的常数除数),所以它不贵,除非<代码> DEQu达到数千个元素范围或更高)。因此,基本上,如果一个

deque
很大,并且你打算从中重复选择随机元素,就不要使用它,坚持使用
list
tuple
str
byte
bytearray
array.array
和其他带有
O(1)的序列类型
索引。

我认为上述答案不正确。我根据经验验证了这个操作的复杂性是
O(n)
。这是我的代码和一个小情节。不过,我对这个理论不太清楚

from time import time
import numpy as np 
import matplotlib.pyplot as plt
N = np.logspace(2, 10, 40)
output = []
for i, n in enumerate(N):
    print(i)
    n = int(n)
    stats = time()
    A = np.random.choice(list(range(n)), n//2)
    output.append(time()-stats)   
plt.plot(N, output)
这是我得到的情节,在我看来是线性的。

随机选择(列表)的复杂性是O(logn),其中n是列表中元素的数量

cpython实现用于获取伪随机索引,然后返回该索引处的项


瓶颈是使用拒绝采样生成[0,n]范围内的数字的函数。该函数通过调用k为cel(logn)来生成k个伪随机位。这些位表示范围内的数字[0,2**k)。重复此过程,直到生成的位数小于n。对伪随机数生成器的每次调用都以O(k)运行,其中k是生成的位数,即O(logn).

虽然这个问题是关于
random.choice
的,以前关于它的答案有几种解释,但当我搜索
np.random.choice
的复杂性时,我没有找到答案,所以我决定解释一下
np.random.choice

选择(a,size=None,replace=True,p=None)。假设
a.shape=(n,)
size=m

更换时:

如果未指定
p
(假设为均匀分布),则
np.random.choice
的复杂性为O(m),如果指定
p
,则复杂性为O(n+n log m)

github代码可以在这里找到

当未指定
p
时,
choice
通过
randint
生成索引数组,并返回
a[index]
,因此复杂性为O(m)。(我假设randint生成随机整数的操作为O(1)。)

当指定
p
时,函数首先计算
p
的前缀和。然后从[0,1]中提取m个样本,然后使用二进制搜索在每个提取的样本的前缀和中找到相应的间隔。可以找到使用二进制搜索的证据。因此此过程为O(n+m log n)。如果在这种情况下需要更快的方法,可以使用,这需要O(n)时间进行预处理,O(m)时间对m个项目进行采样


没有替换时:(有点复杂,也许我将来会完成。)

如果未指定
p
,则复杂性与
np.permutation(n)
相同,即使m仅为1。请参阅详细信息


如果指定了
p
,那么预期的复杂度至少是$n\log n\log\frac{n}{n+1-m}$。

如果规范中没有说明,那么它可能依赖于实现。我无法想象为什么它不是
O(1)
。它只需要从
0
len(list)
中选择一个随机数
i
,然后返回
list[i]
。它们都是常量时间操作。如果Python列表被实现为链表,它将是
O(n)
,因为获取列表的长度是线性的,访问选定的元素也是线性的。但是因为它们实际上是数组,所以一切都是常量。使用“较大”常量除数是什么意思?@StefanPochmann:这里的实现细节,但CPython的
deque
是每个存储的块的链接列表(左侧和右侧的块可能不完整)。在长度达到峰值66之前,任何查找都不需要进行任何块遍历,甚至多达1000个元素,您最多仍要查看17个块(最多必须遍历其中的一半以进行索引),而Python解释器的开销通常会淹没小的C级成本,比如在链表中遍历8个跃点。哦,太酷了,我不知道它会这样做。但是很有意义。谢谢,很高兴知道。@CGFoX:wighted choice for
choices
O(n log m)
,其中
n
是所做选择的数量,
m
是权重的总数(它将每个选择的累积权重平分,即
O(log m)
起作用,并执行
n
次)。因此,根据从中选择的顺序,每个选择仍然是
O(1)
(不包括像
deque
)这样的情况,对选择进行加权只会稍微贵一点。@CGFoX:提供累积权重而不是相对权重可以将预处理从相对保存到累积。要改进这一点,您需要制作一个边带
列表
,以描述每个索引
x
的次数(例如,如果索引
0
的可能性为10%,而
1
的可能性为90%,则您应该创建一个列表
[0,1,1,1,1,1,1,1,1,1,1,1,1]
,从中进行
O(1)
选择,然后执行
O(1)
查找原始源
列表
)。但该边带
列表
有一个最小值