从列表范围中获取随机项

时间:2018-03-20 20:46:06

标签: python algorithm random

我们说我有一套未分类的项目:

input = set([45, 235, 3, 77, 55, 80, 154])

我需要从此输入中获取随机值,但是在特定范围内。例如。当我有

ran = [50, 100]

我希望它返回77或55或80.在python中获取大型集合的最快方法是什么?

4 个答案:

答案 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作为已定义的集合,因为它是一个保留的内置符号。