我有很多元组列表,例如
actions = [ [('d', 'r'), ... ('c', 'e'),('', 'e')],
[('r', 'e'), ... ('c', 'e'),('d', 'r')],
... ,
[('a', 'b'), ... ('c', 'e'),('c', 'h')]
]
我想找到元组的同时出现。
我尝试了this question的提示,但是被接受的答案太慢了。例如,在具有1494个元组列表的列表中,得到的字典大小为18225703,花了几个小时才能运行2个元组共现。因此,由于我的清单更大,所以简单的排列和计数似乎不是答案。
我希望输出在某种程度上提取同时出现的最常见的对(2)或更多对(最多3,4,5)元组。以上一个列表为例:
('c', 'e'),('d', 'r')
在搜索对时会很常见,因为它们经常同时出现。有没有有效的方法来实现这一目标?
答案 0 :(得分:0)
我认为没有希望有更快的算法:您必须计算组合来计算它们。但是,如果您对共现阈值不感兴趣,可以降低算法的复杂度。在这两种情况下,都希望减少空间复杂度。
让我们举一个小例子:
>>> actions = [[('d', 'r'), ('c', 'e'),('', 'e')],
... [('r', 'e'), ('c', 'e'),('d', 'r')],
... [('a', 'b'), ('c', 'e'),('c', 'h')]]
This answer可能是大型列表的最佳选择,但是可以避免创建中间列表。首先,在所有当前的元素对上创建一个可迭代的元素(在您的情况下,元素也是对的,但这没关系):
>>> import itertools
>>> it = itertools.chain.from_iterable(itertools.combinations(pair_list, 2) for pair_list in actions)
如果要查看结果,则必须消耗可迭代的内容:
>>> list(it)
[(('d', 'r'), ('c', 'e')), (('d', 'r'), ('', 'e')), (('c', 'e'), ('', 'e')), (('r', 'e'), ('c', 'e')), (('r', 'e'), ('d', 'r')), (('c', 'e'), ('d', 'r')), (('a', 'b'), ('c', 'e')), (('a', 'b'), ('c', 'h')), (('c', 'e'), ('c', 'h'))]
然后计算排序对(用新鲜的it
!)
>>> it = itertools.chain.from_iterable(itertools.combinations(pair_list, 2) for pair_list in actions)
>>> from collections import Counter
>>> c = Counter((a,b) if a<=b else (b,a) for a,b in it)
>>> c
Counter({(('c', 'e'), ('d', 'r')): 2, (('', 'e'), ('d', 'r')): 1, (('', 'e'), ('c', 'e')): 1, (('c', 'e'), ('r', 'e')): 1, (('d', 'r'), ('r', 'e')): 1, (('a', 'b'), ('c', 'e')): 1, (('a', 'b'), ('c', 'h')): 1, (('c', 'e'), ('c', 'h')): 1})
>>> c.most_common(2)
[((('c', 'e'), ('d', 'r')), 2), ((('', 'e'), ('d', 'r')), 1)]
至少在空间上,此解决方案应该是有效的,因为一切都是惰性的,并且Counter
的元素数是同一列表中元素的组合数,即最多{{1 }},其中N(N-1)/2
是所有列表中不同元素的数量(“最多”是因为某些元素不会彼此“相遇”,因此也不会发生任何组合)。
时间复杂度为N
,其中O(M . L^2)
是列表数,M
是最大列表的大小。
我假设列表中的所有元素都是不同的。关键思想是如果某个元素仅出现在一个列表中,则该元素在此游戏中绝对没有机会击败任何人:它将L
与他所有的元素同时出现邻居和0与其他列表的元素。如果有很多“孤儿”,在处理计算组合之前将它们删除可能会有用:
1
现在,尝试相同的算法:
>>> d = Counter(itertools.chain.from_iterable(actions))
>>> d
Counter({('c', 'e'): 3, ('d', 'r'): 2, ('', 'e'): 1, ('r', 'e'): 1, ('a', 'b'): 1, ('c', 'h'): 1})
>>> orphans = set(e for e, c in d.items() if c <= 1)
>>> orphans
{('a', 'b'), ('r', 'e'), ('c', 'h'), ('', 'e')}
注意理解:括号内没有方括号。
如果N个元素的列表中有K个孤儿,则该列表的时间复杂度从>>> it = itertools.chain.from_iterable(itertools.combinations((p for p in pair_list if p not in orphans), 2) for pair_list in actions)
>>> c = Counter((a,b) if a<=b else (b,a) for a,b in it)
>>> c
Counter({(('c', 'e'), ('d', 'r')): 2})
降为N(N-1)/2
,即(如果我没记错的话){{1} }组合少。
这可以概括为:如果一个元素存在于两个或更少的列表中,则该元素与其他元素最多同时出现2次,依此类推。
如果仍然很慢,请切换到更快的语言。