【问题标题】:Python - something faster than 2 nested for loopsPython - 比 2 个嵌套 for 循环更快的东西
【发布时间】: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) 渐近性能)。

标签: python performance list dictionary for-loop


【解决方案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') 非常感谢!
    猜你喜欢
    • 2019-04-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-07
    • 1970-01-01
    • 2010-10-05
    • 2012-04-15
    • 1970-01-01
    相关资源
    最近更新 更多