如何在Python中加速嵌套for循环

时间:2016-07-08 09:57:23

标签: python for-loop nested

我有以下Python 2.7代码:

listOfLists = []
for l1_index, l1 in enumerate(L1):
    list = []
    for l2 in L2:
        for l3_index,l3 in enumerate(L3):
            if (L4[l2-1] == l3):
                value = L5[l2-1] * l1[l3_index]
                list.append(value)
                break
    listOfLists.append(list)

,L1,L2,L3,L4,L5列表为:

L1 = [[0.60, 0.95, 0.38, 1.02, 0.29, 0.43], [0.40, 0.09, 0.87, 0.85, 0.70, 0.46], [0.67, 0.91, 0.66, 0.79, 0.86, 0.06], [0.59, 1.81, 0.05, 1.88, 0.20, 0.48], [0.64, 0.34, 0.37, 1.39, 0.56, 0.27], [0.56, 0.34, 0.68, 2.79, 0.18, 0.42], [0.42, 1.67, 0.04, 0.44, 0.25, 0.94], [0.32, 1.92, 0.95, 2.85, 0.95, 0.96], [0.50, 0.68, 0.84, 1.79, 0.35, 0.09], [0.34, 0.66, 0.85, 0.35, 0.38, 0.59], [0.50, 0.79, 0.45, 2.93, 0.50, 0.92], [0.11, 0.11, 0.93, 1.11, 0.81, 0.49]]  # a list of 12 sublists
L2 = [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
L3 = [480, 120, 35, 0, 520, 300]
L4 = [120, 120, 120, 0, 300, 35, 35, 520, 300, 480, 120, 480, 0, 35, 0, 0, 300]
L5 = [5.4, 2.83, 1.16, 6.9, 0.76, 2.15, 5.61, 3.12, 1.57, 0.08, 5.36, 0.2, 1.2, 1.4, 2.9, 2.1, 3.5]

这些只是例子;实际上,这些列表包含数十万个数字。解释器需要几十秒来计算三个嵌套的for循环。

是否有可能以某种方式加速此代码,例如使用itertools或任何其他模块/功能?

编辑:我不能使用非标准的python 2.7模块(numpy,scipy ......)

3 个答案:

答案 0 :(得分:3)

@Rogalski是对的,你肯定需要重新考虑算法(至少尝试)。

但是如果你找不到更好的算法,我认为在使用嵌套循环的同时,你可以通过一些技巧加快速度。请注意,我会将L *列表视为一些全局变量,我不需要将其传递给每个函数。因此,您需要将这些列表保持为新函数可见或将它们添加为参数。

首先,尝试清理。例如,您似乎永远不会使用l1_index,因此您可以摆脱它。然后,您可以将第一个循环内发生的所有内容移动到函数中。它将如下所示:

listOfLists = []
for l1 in L1:
    listOfLists.append(create_list(l1))

def create_list(l1):
    list = []
    for l2 in L2:
        for l3_index,l3 in enumerate(L3):
            if (L4[l2-1] == l3):
                value = L5[l2-1] * l1[l3_index]
                list.append(value)
                break
    return list

这很好,但是理解比循环更快,附加(here你可以找到关于这个主题的好文章)。第一个循环非常简单,所以让它折叠成listOfLists = [create_list(l1) for l1 in L1]。我们可以在create_list函数

上执行相同的内循环提取
list_of_lists = [create_list(l) for l in L1]

def create_list(l):
    return [find_next(l, element) for element in L2]

def find_next(l, element):
    for l3_index, l3_element in enumerate(L3):
        if (L4[element - 1] == l3_element):
            return L5[element - 1] * l[l3_index] 

现在看起来更具可读性,应该更快一点。您也可以尝试使用内置列表函数来查找列表中的元素(l3_index = l3.index(L4[element-1]),),但我不知道它是否会更快。

请注意,lambdas并不比通常以相同方式执行相同操作的函数更快。但它们会破坏堆栈跟踪,从而使代码更难调试。从itertools开始,你可以使用组合,但是你需要预先生成list_of_lists,因为订单上没有合同给你。拉链不是你需要的。

代码的一个问题是你在每一轮嵌套循环中循环L3。解决此问题的方法是添加一些预先计算。你需要的是知道L4的每个元素对应的L3索引。你可以这样做:

# this will allow you to get index by element at a constant time
# and it only takes O(N)
L3_dict = {element:index for element,index in enumerate(L3)}

list_of_lists = [create_list(l) for l in L1]

def create_list(l):
    return [find_next(l, element) for element in L2]

def find_next(l, element):
    # if you use dict, you reduce time of this method from O(N) to constant
    # as both access to dict item by key and to list item by index
    # are done in a constant time
    l3_index = L3_dict[L4[element-1]]
    return L5[element-1] * l[l3_index]

答案 1 :(得分:2)

既然你说[[L5[l2 - 1] * sl1 for sl1, l3 in zip(l1, L3) for l2 in L2 if L4[l2 - 1] == l3] for l1 in L1] ,你就是这样做的诀窍:

{{1}}

此代码比for循环快25%。但相信我,无论谁在我的代码中写下这个,我都会向他开枪。

答案 2 :(得分:0)

以下代码是@spacegoing和@Alissa的组合,产生最快的结果:

L3_dict = {l3:l3_index for l3_index,l3 in enumerate(L3)}

list_of_lists = [[L5[l2 - 1] * l1[L3_dict[L4[l2-1]]]
     for l2 in L2]
 for l1 in L1]

感谢@spacegoing和@Alissa的耐心和时间。