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 循环更快)