Python - 比 2 个嵌套 for 循环更快的东西

Posted

技术标签:

【中文标题】Python - 比 2 个嵌套 for 循环更快的东西【英文标题】:Python - something faster than 2 nested for loops 【发布时间】:2017-07-06 03:17:03 【问题描述】:
def fancymatching(fname1, fname2):
#This function will do much smarter and fancy kinds of compares
    if (fname1 == fname2):
        return 1
    else:
        return 0

personlist = [
 
'pid':'1',
'fname':'john',
'mname':'a',
'lname':'smyth',
, 
'pid':'2',
'fname':'john',
'mnane':'a',
'lname':'smith',
, 
'pid':'3',
'fname':'bob',
'mname':'b',
'lname':'nope',

]

for person1 in personlist:
    for person2 in personlist:
        if person1['pid'] >= person2['pid']:
            #don't check yourself, or ones that have been
        continue
        if fancymatching(person1['fname'], person2['fname']):
            print (person1['pid'] + " matched " + person2['pid'])

我正在尝试改进上述代码的想法。它可以工作,但如果 personlist 变得非常大(比如数百万),我觉得肯定有比 2 个 for 循环更快的东西。

代码所做的是获取一个字典列表,并对每个字典的值与其他字典运行一个花哨的模糊匹配函数。因此,这并不像将所有字典与其他字典进行比较那么简单。我想要一种在每个字典上运行一个函数的方法,也许 2 个 for 循环是正确的方法吗?任何建议都会有所帮助!

【问题讨论】:

如果对模糊匹配一无所知,则无法比嵌套循环更快地执行此操作(但是,如果您的第二个循环从 person1 的下一个开始迭代,则可以将其提高 2 倍。这样,如果(a,b) 已评估 (b,a) 未评估。匹配是否具有传递性? 使用itertools.combinations 可能比写出你自己的循环快一点,但不会快很多(同样的O(N**2) 渐近性能)。 【参考方案1】:

添加到 MSeifert 的答案中,如果您的匹配取决于 fname1 == fname2 那么您可以对列表进行排序然后分组:即:

from itertools import combinations, groupby

keyfunc = lambda x: x['fname']
data = sorted(personlist, key= keyfunc)
for key, group in groupby(data, key):
    #every element in group will now match
    for person1, person2 in combinations(group, 2):
        print(person1['fname'], person2['fname'])

显然,如果您更改匹配函数,您将需要更改键函数,以便它为任何匹配的元素返回相同的值,而为任何不匹配的元素返回不同的值。这确实依赖于这样一个关键函数存在这一事实,而对于任意匹配函数并不总是如此。

【讨论】:

【参考方案2】:

您可以使用itertools.combinations,它本质上是相同的双循环,但它的迭代速度更快,因为它是用 C 编写的(这只会减少常数因子,您仍然有 O(n**2) 运行时行为)并且您不需要if person1['pid'] >= person2['pid']: continue 不再是(这已经内置在 combinations 函数中了)。

from itertools import combinations

for person1, person2 in combinations(personlist, 2):
    print(person1['fname'], person2['fname'])

哪个打印:

('john', 'john')
('john', 'bob')
('john', 'bob')

但是如果您的fancymatching 允许,那么您也可以对您的值进行分组(O(n) 运行时)。例如,在您的情况下,您只匹配相同的 'fname'-values。

>>> matches = 
>>> for person in personlist:
...     matches.setdefault(person['fname'], []).append(person)
>>> matches
'bob': ['fname': 'bob', 'lname': 'nope', 'mname': 'b', 'pid': '3'],
 'john': ['fname': 'john', 'lname': 'smyth', 'mname': 'a', 'pid': '1', 
          'fname': 'john', 'lname': 'smith', 'mnane': 'a', 'pid': '2']

但这只有在您的fancymatching 允许这样的分组时才有可能。这对你的情况是正确的,但如果它更复杂,它可能不是。

【讨论】:

您是否忘记了选择性打印的 if 条件? @Spade 你什么意思? 我认为 OP 只希望在匹配时打印它if fancymatching(person1['fname'], person2['fname']): 啊,我不是故意的,我只是想演示一下combinations 的工作原理。它如何将两个循环和第一个 if 减少到一个循环。 :) 这非常适合我。我现在可以在那里扔任何花哨的比赛,例如: if person1['fname'][:1] == person2['fname'][:1]: print (person1['fname'], person2['fname ']) else: print ('nope') 非常感谢!

以上是关于Python - 比 2 个嵌套 for 循环更快的东西的主要内容,如果未能解决你的问题,请参考以下文章

比较列表理解和显式循环(3 个数组生成器比 1 个 for 循环更快)

itertools.product 比嵌套 for 循环慢

Python:为啥弹出队列比 for-in 块更快?

python生成器比嵌套for循环快吗? [关闭]

在JAVA的嵌套循环中,为什 么小循环中的大循环比大循环中的小循环快? [重复]

在 Python 中优化 for 循环以更快地工作