Python嵌套用于循环数组比较 - 优化的可能性?

时间:2015-03-14 02:59:38

标签: python arrays

我正在尝试优化嵌套的for循环,将数组中的元素与数组中的其余元素进行比较

有两个部分,第一部分是例如,一个数组有3个元素,每个元素都是一个字典:

[{"someKey_1":"a"}, {"someKey_1":"b"}, {"somekey_1":"a"}]


第一次迭代(第一个元素与第二个元素比较):

测试两个元素的“someKey”键,因为a!= b,然后我们什么都不做


第2次迭代(第1个元素与第3个元素比较):

测试两个元素的“someKey”键,因为a == a,我们做一些逻辑


代码:

for idx, val in enumerate(set_of_pk_values):
    for idx_2, val_2 in enumerate(set_of_pk_values):
        if (val['someKey'] == val_2['someKey'] and idx != idx_2):
                #Some Logic

第二部分与上一个例子(列表中的3个项目)非常相似,在同一个字典中,我们有一个与一个键相关联的数组(现在有一个字典,在数组的每个元素中都有两个键) ,让我们说:

[{"someKey_1":[b,f]}{"someKey_2":a}, 
{"someKey_1":[e,f]}{"someKey_2":b}, 
{"somekey_1":[h,k]}{"someKey_2":c}]

第一次迭代(第一个元素与第二个元素比较):

使用键遍历数组:someKey_1

b==b(第二个元素的someKey_2),然后做一些逻辑

f!=b(第二个元素的someKey_2),没有逻辑完成


第二次迭代(第一个元素与第三个元素比较):

使用键遍历数组:someKey_1

b==c(第三个元素的someKey_2),然后做一些逻辑

f!=c(第3个元素的someKey_2),没有逻辑完成


代码:

for idx, val in enumerate(set_of_pk_values):
    for idx_2, val_2 in enumerate(set_of_pk_values):
        for pred in val['someKey_1']:
            if(val_2['someKey_2'] == pred):
                #Some Logic

目前第一个嵌套循环的运行时间:21秒,第二个嵌套循环约为19秒。与其他流程(1-2秒)相比,这部分显然是一个瓶颈。

有人能指出我如何优化这段简单但非常耗时的代码吗?

3 个答案:

答案 0 :(得分:5)

首先,我相信这应该发布在CodeReview上,而不是StackOverflow。

StackOverflow用于获取无法正常工作的代码的帮助 CodeReview用于获取有效的代码的帮助,但是你想要做得更好。

其次,这里有一些关于优化它的建议:

  • 不要在循环中enumerate()
  • 在第一个场景中使用切片以避免无意义的比较。

以下是我将如何重写您的第一个场景:

# Check if two dictionary with the key 'predecessor' are the same,
# and they are NOT the same index (otherwise it would be comparing themselves)
# set_of_pk_values is an array of dictionaries.
for idx, val in enumerate(set_of_pk_values):
    for val_2 in set_of_pk_values[idx+1:]:  # Note the slice and lack of enumerate
        if (val['predecessor'] == val_2['predecessor']):  # Don't waste time checking indexes
            # Do Something Here, also we don't want to compare itself, because it will be true

而不是for\if,请使用if in

for idx, val in enumerate(set_of_pk_values):
    if val in set_of_pk_values[idx+1:]:
        # Do Something Here, also we don't want to compare itself, because it will be true.

如果你真的想要枚举,因为你只需要多次相同的枚举,我只需要在循环外执行一次并将其存储在变量中,然后循环它。这就是我的意思:

我不对,下面的内容不起作用,请参阅评论。

# Doesn't work, see comments.
# from itertools import islice
# whatIEnumerate = enumerate(set_of_pk_values)
# for idx, val in whatIEnumerate:
    # for idx_2, val_2 in islice(whatIEnumerate, idx+1, None):
        # ...

答案 1 :(得分:4)

第1部分的优化

原始

伙计,这很糟糕:

for idx, val in enumerate(set_of_pk_values):
    for idx_2, val_2 in enumerate(set_of_pk_values):
        if (val['someKey'] == val_2['someKey'] and idx != idx_2):
            do_stuff()

第1步

只需跳过您已经尝试过的元素的索引(==是可交换的):

for idx, val in enumerate(set_of_pk_values[:-1]):
    for val_2 in set_of_pk_values[idx+1:]
        if (val['someKey'] == val_2['someKey']):
            do_stuff()

步骤0.1

重命名。它很丑。

for idx, first_dic in enumerate(set_of_pk_values[:-1]):
    for second_dic in set_of_pk_values[idx+1:]
        if (first_dic['someKey'] == second_dic['someKey']):
            do_stuff()

第2步

现在,每次循环迭代中的if都很麻烦。通过过滤缩小列表来替换它:

hits = []
for idx, first_dic in enumerate(set_of_pk_values[:-1]):
    hits += (first_dic['someKey'], filter(lambda dic: dic['someKey'] == first_dic['someKey'], set_of_pk_values[idx:1]) ) 

hits现在包含一个匹配元组列表:hits[i] = ( mathing first id ,匹配列表,其中包含idx>第一个元素)

第3步

字典查找很昂贵。使用operator.itemgetter替换它们:

from operator import itemgetter
getter = itemgetter("someKey")
hits = []
for idx, first_dic in enumerate(set_of_pk_values[:-1]):
    hits += (getter(first_dic), filter(lambda dic: getter(dic) == getter(first_dic), set_of_pk_values[idx:1]) )

第4步

坐下来看看。 for循环的迭代并不真正依赖于最后一次迭代的状态。列表理解的时间。

from operator import itemgetter
getter = itemgetter("someKey")
hits = [ ( getter(first_dic), filter(lambda dic: getter(dic) == getter(first_dic), set_of_pk_values[idx:-1]) ) for idx, first_dic in enumerate(set_of_pk_values[:-1])]

答案 2 :(得分:3)

Python中的迭代比C中的迭代要慢。最好使用Python库在C中进行迭代。有趣的是,这里没有人提到itertools ......

itertools.combinations在C中创建唯一组合,然后返回组合的生成器:

import itertools
import operator
getter = operator.itemgetter('someKey_1')

for a, b in itertools.combinations(set_of_pk_values, 2):
    if getter(a) == getter(b):
        # logic?