我有这样的清单:
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的方式。这只是一个小例子,但如果我有大量的名单怎么办?
答案 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缓存。