如何提高python中大型列表的性能
我有一个大列表,上面有1000万个整数(排序为“alist”)。我需要的是得到一些整数(来自“blist”)和列表中相邻整数之间的最小距离。我通过找到我寻找的整数的位置,得到前后的项目,并测量差异:如何提高python中大型列表的性能,python,performance,list,optimization,Python,Performance,List,Optimization,我有一个大列表,上面有1000万个整数(排序为“alist”)。我需要的是得到一些整数(来自“blist”)和列表中相邻整数之间的最小距离。我通过找到我寻找的整数的位置,得到前后的项目,并测量差异: alist=[1, 4, 30, 1000, 2000] #~10 million integers blist=[4, 30, 1000] #~8 million integers for b in blist: position=alist.index(b) distance=
alist=[1, 4, 30, 1000, 2000] #~10 million integers
blist=[4, 30, 1000] #~8 million integers
for b in blist:
position=alist.index(b)
distance=min([b-alist[position-1],alist[position+1]-b])
这个操作必须重复数百万次,不幸的是,在我的机器上需要花费很长时间。有没有办法提高这段代码的性能?我使用Python2.6,Python3不是一个选项。我非常喜欢这种计算的Numpy模块 在您的情况下,这将是(这是一个很长的答案,可以分解为更有效的): 如果blist的第一个元素是alist的第一个元素,那么它将不起作用。 我想:
alist = [b[0] - 1] + alist + [b[-1] + 1]
这是一个肮脏的解决办法
基准“还在运行”可能是我的电脑出了故障
alist = sorted(list(np.random.randint(0, 10000, 10000000)))
blist = sorted(list(alist[1000000:9000001]))
a_array = np.asarray(alist)
b_array = np.asarray(blist)
矢量化解
%%timeit
a_index = np.searchsorted(a_array, b_array)
a_array_left = a_array[a_index - 1]
a_array_right = a_array[a_index + 1]
min_distance = np.min([b_array - a_array_left, a_array_right - b_array], axis=0)
1 loop, best of 3: 591 ms per loop
%%timeit
a_index = np.searchsorted(a_array, b_array)
a_array_left = a_array[a_index - 1]
a_array_right = a_array[a_index + 1]
min_distance = np.min([b_array - a_array_left, a_array_right - b_array], axis=0)
10 loops, best of 3: 53.2 ms per loop
二进制搜索解决方案
%%timeit
for b in blist:
position = bisect.bisect_left(alist, b)
distance = min([b-alist[position-1],alist[position+1]-b])
Still running..
%%timeit
for b in blist:
position = bisect.bisect_left(alist, b)
distance = min([b-alist[position-1],alist[position+1]-b])
1 loop, best of 3: 1.57 s per loop
欧普解
%%timeit
for b in blist:
position=alist.index(b)
distance=min([b-alist[position-1],alist[position+1]-b])
Still running..
%%timeit
for b in blist:
position=alist.index(b)
distance=min([b-alist[position-1],alist[position+1]-b])
Still running..
较小的输入
alist = sorted(list(np.random.randint(0, 10000, 1000000)))
blist = sorted(list(alist[100000:900001]))
a_array = np.asarray(alist)
b_array = np.asarray(blist)
矢量化解
%%timeit
a_index = np.searchsorted(a_array, b_array)
a_array_left = a_array[a_index - 1]
a_array_right = a_array[a_index + 1]
min_distance = np.min([b_array - a_array_left, a_array_right - b_array], axis=0)
1 loop, best of 3: 591 ms per loop
%%timeit
a_index = np.searchsorted(a_array, b_array)
a_array_left = a_array[a_index - 1]
a_array_right = a_array[a_index + 1]
min_distance = np.min([b_array - a_array_left, a_array_right - b_array], axis=0)
10 loops, best of 3: 53.2 ms per loop
二进制搜索解决方案
%%timeit
for b in blist:
position = bisect.bisect_left(alist, b)
distance = min([b-alist[position-1],alist[position+1]-b])
Still running..
%%timeit
for b in blist:
position = bisect.bisect_left(alist, b)
distance = min([b-alist[position-1],alist[position+1]-b])
1 loop, best of 3: 1.57 s per loop
欧普解
%%timeit
for b in blist:
position=alist.index(b)
distance=min([b-alist[position-1],alist[position+1]-b])
Still running..
%%timeit
for b in blist:
position=alist.index(b)
distance=min([b-alist[position-1],alist[position+1]-b])
Still running..
我非常喜欢这种计算的Numpy模块 在您的情况下,这将是(这是一个很长的答案,可以分解为更有效的): 如果blist的第一个元素是alist的第一个元素,那么它将不起作用。 我想:
alist = [b[0] - 1] + alist + [b[-1] + 1]
这是一个肮脏的解决办法
基准“还在运行”可能是我的电脑出了故障
alist = sorted(list(np.random.randint(0, 10000, 10000000)))
blist = sorted(list(alist[1000000:9000001]))
a_array = np.asarray(alist)
b_array = np.asarray(blist)
矢量化解
%%timeit
a_index = np.searchsorted(a_array, b_array)
a_array_left = a_array[a_index - 1]
a_array_right = a_array[a_index + 1]
min_distance = np.min([b_array - a_array_left, a_array_right - b_array], axis=0)
1 loop, best of 3: 591 ms per loop
%%timeit
a_index = np.searchsorted(a_array, b_array)
a_array_left = a_array[a_index - 1]
a_array_right = a_array[a_index + 1]
min_distance = np.min([b_array - a_array_left, a_array_right - b_array], axis=0)
10 loops, best of 3: 53.2 ms per loop
二进制搜索解决方案
%%timeit
for b in blist:
position = bisect.bisect_left(alist, b)
distance = min([b-alist[position-1],alist[position+1]-b])
Still running..
%%timeit
for b in blist:
position = bisect.bisect_left(alist, b)
distance = min([b-alist[position-1],alist[position+1]-b])
1 loop, best of 3: 1.57 s per loop
欧普解
%%timeit
for b in blist:
position=alist.index(b)
distance=min([b-alist[position-1],alist[position+1]-b])
Still running..
%%timeit
for b in blist:
position=alist.index(b)
distance=min([b-alist[position-1],alist[position+1]-b])
Still running..
较小的输入
alist = sorted(list(np.random.randint(0, 10000, 1000000)))
blist = sorted(list(alist[100000:900001]))
a_array = np.asarray(alist)
b_array = np.asarray(blist)
矢量化解
%%timeit
a_index = np.searchsorted(a_array, b_array)
a_array_left = a_array[a_index - 1]
a_array_right = a_array[a_index + 1]
min_distance = np.min([b_array - a_array_left, a_array_right - b_array], axis=0)
1 loop, best of 3: 591 ms per loop
%%timeit
a_index = np.searchsorted(a_array, b_array)
a_array_left = a_array[a_index - 1]
a_array_right = a_array[a_index + 1]
min_distance = np.min([b_array - a_array_left, a_array_right - b_array], axis=0)
10 loops, best of 3: 53.2 ms per loop
二进制搜索解决方案
%%timeit
for b in blist:
position = bisect.bisect_left(alist, b)
distance = min([b-alist[position-1],alist[position+1]-b])
Still running..
%%timeit
for b in blist:
position = bisect.bisect_left(alist, b)
distance = min([b-alist[position-1],alist[position+1]-b])
1 loop, best of 3: 1.57 s per loop
欧普解
%%timeit
for b in blist:
position=alist.index(b)
distance=min([b-alist[position-1],alist[position+1]-b])
Still running..
%%timeit
for b in blist:
position=alist.index(b)
distance=min([b-alist[position-1],alist[position+1]-b])
Still running..
我建议使用二进制搜索。使其速度更快,不需要额外的内存,只需稍作更改。不要使用列表索引(b),只需使用 如果您的
blist
也被排序,您也可以使用非常简单的增量搜索,搜索当前b
不是从alist
的开头,而是从上一个b
的索引
Python 2.7.11的基准测试和包含1000万和800万整数的列表:
389700.01 seconds Andy_original (time estimated)
377100.01 seconds Andy_no_lists (time estimated)
6.30 seconds Stefan_binary_search
2.15 seconds Stefan_incremental_search
3.57 seconds Stefan_incremental_search2
1.21 seconds Jacquot_NumPy
(0.74 seconds Stefan_only_search_no_distance)
安迪的原件大约需要4.5天,所以我只使用了blist
的每100000个条目,并按比例放大。二进制搜索速度快得多,增量搜索速度更快,NumPy比它们都快,尽管它们都只需要几秒钟
最后一个花费0.74秒的条目是我的增量搜索,没有distance=min(…)
行,因此它是不可比较的。但它显示搜索只需要2.15秒中的34%。所以我所能做的就不多了,因为现在大部分时间都是由distance=min(…)
计算完成的
Python 3.5.1的结果类似:
509819.56 seconds Andy_original (time estimated)
505257.32 seconds Andy_no_lists (time estimated)
8.35 seconds Stefan_binary_search
4.61 seconds Stefan_incremental_search
4.53 seconds Stefan_incremental_search2
1.39 seconds Jacquot_NumPy
(1.45 seconds Stefan_only_search_no_distance)
包含所有版本和测试的完整代码:
def Andy_original(alist, blist):
for b in blist:
position = alist.index(b)
distance = min([b-alist[position-1], alist[position+1]-b])
def Andy_no_lists(alist, blist):
for b in blist:
position = alist.index(b)
distance = min(b-alist[position-1], alist[position+1]-b)
from bisect import bisect_left
def Stefan_binary_search(alist, blist):
for b in blist:
position = bisect_left(alist, b)
distance = min(b-alist[position-1], alist[position+1]-b)
def Stefan_incremental_search(alist, blist):
position = 0
for b in blist:
while alist[position] < b:
position += 1
distance = min(b-alist[position-1], alist[position+1]-b)
def Stefan_incremental_search2(alist, blist):
position = 0
for b in blist:
position = alist.index(b, position)
distance = min(b-alist[position-1], alist[position+1]-b)
import numpy as np
def Jacquot_NumPy(alist, blist):
a_array = np.asarray(alist)
b_array = np.asarray(blist)
a_index = np.searchsorted(a_array, b_array) # gives the indexes of the elements of b_array in a_array
a_array_left = a_array[a_index - 1]
a_array_right = a_array[a_index + 1]
distance_left = np.abs(b_array - a_array_left)
distance_right = np.abs(a_array_right - b_array)
min_distance = np.min([distance_left, distance_right], axis=0)
def Stefan_only_search_no_distance(alist, blist):
position = 0
for b in blist:
while alist[position] < b:
position += 1
from time import time
alist = list(range(10000000))
blist = [i for i in alist[1:-1] if i % 5]
blist_small = blist[::100000]
for func in Andy_original, Andy_no_lists:
t0 = time()
func(alist, blist_small)
t = time() - t0
print('%9.2f seconds %s (time estimated)' % (t * 100000, func.__name__))
for func in Stefan_binary_search, Stefan_incremental_search, Stefan_incremental_search2, Jacquot_NumPy, Stefan_only_search_no_distance:
t0 = time()
func(alist, blist)
t = time() - t0
print('%9.2f seconds %s' % (t, func.__name__))
def Andy_原创(列表,blist):
对于blist中的b:
位置=列表索引(b)
距离=最小值([b-alist[位置-1],alist[位置+1]-b])
def Andy_no_列表(列表、blist):
对于blist中的b:
位置=列表索引(b)
距离=最小值(b-alist[位置-1],alist[位置+1]-b)
从对分导入左对分
def Stefan_二进制搜索(列表、blist):
对于blist中的b:
位置=左二等分(A,b)
距离=最小值(b-alist[位置-1],alist[位置+1]-b)
def Stefan_增量搜索(列表、blist):
位置=0
对于blist中的b:
当[position]
我建议使用二进制搜索。使其速度更快,不需要额外的内存,只需稍作更改。不要使用列表索引(b),只需使用
如果您的blist
也被排序,您也可以使用非常简单的增量搜索,搜索当前b
不是从alist
的开头,而是从上一个b
的索引
Python 2.7.11的基准测试和包含1000万和800万整数的列表:
389700.01 seconds Andy_original (time estimated)
377100.01 seconds Andy_no_lists (time estimated)
6.30 seconds Stefan_binary_search
2.15 seconds Stefan_incremental_search
3.57 seconds Stefan_incremental_search2
1.21 seconds Jacquot_NumPy
(0.74 seconds Stefan_only_search_no_distance)
安迪的原件大约需要4.5天,所以我只使用了blist
的每100000个条目,并按比例放大。二进制搜索速度快得多,增量搜索速度更快,NumPy比它们都快,尽管它们都只需要几秒钟
最后一个花费0.74秒的条目是我在没有distan的情况下的增量搜索