如何提高python中大型列表的性能

如何提高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=

我有一个大列表,上面有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=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的情况下的增量搜索