基于许多索引替换列表中的值的最佳方法

时间:2014-06-26 01:34:50

标签: python

我有这样的清单:

l = [1,2,3,4,5,6,7,8,9,10]

idx = [2,5,7]

我想使用idx中的索引将l中的值替换为0。现在我做:

for i in idx:
    l[i] = 0

这给:l = [1,2,0,4,5,0,7,0,9,10]

是否有更好,更快,更pythonic的方式。这只是一个小例子,但如果我有大量的名单怎么办?

3 个答案:

答案 0 :(得分:2)

如果您正在谈论巨大的列表,那么您应该尝试不创建新列表,因为除了输入列表之外,新列表还需要内存空间。

现在,让我们考虑您要设置为0的索引。这些索引包含在列表(idx)中,该列表本身可以与包含数字的列表(l)一样长。所以,如果你要做这样的事情:

for i in range(len(l)):
    if i in idx:
        l[i] = 0

需要O(mn)时间,其中m是idx中元素的数量,n是l中元素的数量。这是一个非常慢的算法。

现在,你真的不能比O(m)快得多,因为你必须考虑idx中的每个元素。但由于m严格限制在n以上,因此它绝对是一个更好的策略来代替idx

for i in idx:
    l[i] = 0

但我们要考虑idx可能包含l无效索引的元素(即idx中至少有一个元素的值大于l中的最大索引)。然后,你可以这样做:

for i in idx:
    if i<len(l):
        l[i] = 0

或:

for ind in (i for i in idx if i<len(L)):
    l[ind] = 0

现在,这会进行O(m)比较,这可能会有所改进。例如,如果对idx进行了排序,则修改后的二进制搜索可以提供具有有效索引的idx的适当切片:

def binSearch(L, idx, i=0, j=None):  # note that the list is not sliced, unlike some common binary search implementations. This saves on additional space
    if not idx:
        return pad
    if j==None:
        j = len(idx)-1
    mid = (i+j)//2
    if idx[mid] == len(L)-1:
        return mid
    elif idx[mid] > len(L)-1:
        return binSearch(L, idx, i, mid-1)
    else:
        return binSearch(L, idx, mid+1, j)

现在,你可以只替换有效的索引而不进行任何比较:

for ind in range(binSearch(L, idx)):
    l[idx[ind]] = 0

请注意,此方法需要O(log m)时间才能在binSearch上首先应用idx

如果idx已经排序,这将有效。但是,如果这是一个无效的假设,那么您可能希望自己对其进行排序,这将花费O(m log m)时间,这将比上述O(m)实现慢。


然而,如果idx足够大,您可以使用multiprocessing尝试分布式方法:

import multiprocessing as mp

def replace(l, idx):
    numWorkers = mp.cpu_count()*2 -1
    qIn = mp.Queue(maxsize=len(idx))
    qOut = mp.Queue()
    procs = [mp.Process(target=slave, args=(L, qIn, qOut)) for _ in range(numWorkers)]
    for p in procs:
        p.start()

    for i in idx:
        qIn.put(i)

    numFinished = 0
    while numFinished != numWorkers:
        i = qOut.get()
        if i is None:
            numFinished += 1
            continue
        l[i] = 0


def slave(L, qIn, qOut):
    for i in iter(qIn.get, None):
        if i< len(L):
            qOut.put(i)
    qOut.put(None)

当然,您也可以通过将binSearch添加到分布式解决方案来进一步改进这一点,但我会留给您。

答案 1 :(得分:1)

不要为索引创建另一个列表。代替:

l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
index = 1
while index < len(l):
    if index == 2:
        l[index] = 0
    elif index == 5:
        l[index] = 0
    elif index == 7:
        l[index] = 0
    index += 1
print(l)

如果将所有语句组合在一行中并带有“或”语句,则不必使用“elif”语句。例如:

l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
index = 1
while index < len(l):
    if (index == 2) or (index == 5) or (index == 7):        
        l[index] = 0
    index += 1
print(l)

答案 2 :(得分:0)

我认为这很好。你可以编写一个列表理解,如下所示:

[v if i not in idx else 0 for i, v in enumerate(l)]

或者通过迭代l

来更改它
for i, v in enumerate(l):
    if i in idx:
        l[i] = 0 

但我发现阅读起来更难,而且很可能更慢。我认为任何其他解决方案都不会在很大程度上打败你,忽略CPU缓存。