我们说我有一套未分类的项目:
input = set([45, 235, 3, 77, 55, 80, 154])
我需要从此输入中获取随机值,但是在特定范围内。例如。当我有
ran = [50, 100]
我希望它返回77或55或80.在python中获取大型集合的最快方法是什么?
答案 0 :(得分:6)
使用set
这是正确的方法因为元素没有排序。这将导致O(N)
解决方案针对边界测试每个元素。
我建议将数据转换为排序列表,然后您可以使用bisect
查找开始和放大列表。结束边界值的索引,然后在切片列表上应用random.choice
:
import bisect,random
data = sorted([45, 235, 3, 77, 55, 80, 154])
def rand(start,stop):
start_index = bisect.bisect_left(data,start)
end_index = bisect.bisect_right(data,stop)
return data[random.randrange(start_index,end_index)]
print(rand(30,100))
bisect
在排序列表上的复杂度为O(log(N))
。然后选择random.randrange
的索引。
bisect
在主流平台上使用已编译的代码,因此除了低复杂性外,它还非常高效。
通过执行极限测试来验证边界:
print(rand(235,235))
按预期打印235
(总是很难确保数组在使用随机数据时不会超出范围)
(如果您想在运行时更新数据,也可以使用bisect
插入元素,因为set
复杂度+ O(log N)
比list
慢。插入ipython
,当然但你不能拥有一切)
答案 1 :(得分:2)
您没有澄清您是否可以使用numpy
,但也要求"最快"所以我将包含numpy
方法的完整性。在这种情况下," python_method
"方法是answer given by Jean-François Fabre here
import numpy as np
import bisect,random
data = np.random.randint(0, 60, 10000)
high = 25
low = 20
def python_method(data, low, high):
data = sorted(data)
start_index = bisect.bisect_left(data,low)
end_index = bisect.bisect_right(data,high)
return data[random.randrange(start_index,end_index)]
def numpy_method(data, low, high):
return np.random.choice(data[(data >=low) & (data <= high)])
时序:
%timeit python_method(data, low, high)
2.34 ms ± 11.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit numpy_method(data, low, high)
33.2 µs ± 72.4 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
但是,显然,如果您多次使用该函数,那么您只需sort
一次列表,这样就可以将Python运行时降低到相同的水平。
%timeit new_data = sorted(data)
2.33 ms ± 39.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
如果您需要在单个范围内获得多个结果, numpy
会再次提前,因为您可以在一次通话中获得这些结果。
编辑:
如果输入数组已经排序,并且您确定可以利用它(从sorted()
中取出timeit
),那么纯python方法就会胜出选择单个值:
%timeit python_method(data, low, high)
5.06 µs ± 16.3 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
未经修改的numpy
方法给出:
%timeit numpy_method(data, low, high)
20.5 µs ± 668 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
所以,据我所知,在列表已经排序的情况下,你只想要一个结果,pure-python方法获胜。如果您想要在该范围内获得多个结果,那么它可能会有所不同,但我会针对randrange
进行基准测试。
答案 2 :(得分:1)
from random import randint
input = set([45, 235, 3, 77, 55, 80, 154])
ran = [50, 100]
valid_values = []
for i in input:
if ran[0] <= i <= ran[1]:
valid_values.append(i)
random_index = randint(0, len(valid_values)-1)
print(valid_values[random_index])
答案 3 :(得分:0)
这是我的建议,我觉得可读,易懂和简短:
import random
inputSet = set([45, 235, 3, 77, 55, 80, 154])
ran = [50,100]
# Get list of elements inside the range
a = [x for x in inputSet if x in range(ran[0],ran[1])]
# Print a random element
print(random.choice(a)) # randomly 55, 77 or 80
请注意,我没有使用名称input
作为已定义的集合,因为它是一个保留的内置符号。