在python中找到n个子序列的最小总和的最快方法

时间:2017-04-04 10:10:46

标签: arrays performance list python-3.x numpy

给定列表L,查找所有连续长度n子序列的最快方法是什么,并查找最小总和的索引。这是我的(慢)版本:

from random import randrange
import numpy as np
L = np.array([randrange(-5000, 5000) for _ in range(10 * 10**6)])   #random array
subarray_length = 3                                                 #for example
sublist_sums = [sum(L[i:i+subarray_length]) for i 
                         in range(0, len(L) - (subarray_length - 1))]

candidate_idx = 0
candidate_sum = sublist_sums[0]

for idx, val in enumerate(sublist_sums[1:]):
    if val < candidate_sum:
        candidate_sum, candidate_idx = val, idx + 1

print(candidate_idx)

MWE

L = [6,5,4,3,2,1]

# sublists: [[6,5,4], [5,4,3], [4,3,2], [3,2,1] ]

subarray_length = 3
sublist_sums = [sum(L[i:i+subarray_length]) for i 
                         in range(0, len(L) - (subarray_length - 1))]

# sublist_sums = [15, 12, 9, 6]

for idx, val in enumerate(sublist_sums[1:]):
    if val < candidate_sum:
        candidate_sum, candidate_idx = val, idx + 1

candidate_idx = 3  

# 3 is the index of the first element of the length 3 sublist 
# with the smallest sum of all 3 length consecutive sublists

2 个答案:

答案 0 :(得分:2)

这是一种方法 -

# Based on http://stackoverflow.com/a/14314054/3293881 by @Jaime
def moving_sum(a, n) :
    ret = np.cumsum(a)
    ret[n:] = ret[n:] - ret[:-n]
    return ret[n - 1:]

sums = moving_sum(L, subarray_length)
min_idx = sums.argmin()
candidate_sum, candidate_idx = sums[min_idx], min_idx

或者,我们可以使用np.convolve来计算窗口求和,如此 -

sums = np.convolve(L,np.ones(subarray_length,dtype=int),'valid')

使用Scipy's uniform 1D filter -

获取sums的另一种方法
from scipy.ndimage.filters import uniform_filter1d as unif1d

W = subarray_length
a = np.array(L,dtype=float)

hW = (W-1)//2
sums = W*unif1d(a,size=W, mode='constant')[hW:-hW]

答案 1 :(得分:1)

与@Divakar的答案相比,性能稍差,您可以使用as_strided来收集子阵列并从那里开始:

import numpy as np

def min_sub_sum(arr,n):
    shape = (arr.shape[0]-n+1,n)
    strides = arr.strides
    subs = np.lib.stride_tricks.as_strided(arr,shape,strides*2)
    sums = np.einsum('ij->i',subs)
    return np.argmin(sums)

L = np.array([x for x in range(10000,0,-1)])
n = 5

print(min_sub_sum(L,n))
# 9995