我有一个(大)整数列表列表,例如,
a = [
[1, 2],
[3, 6],
[2, 1],
[3, 5],
[3, 6]
]
大多数对将出现两次,其中整数的顺序无关紧要(即[1, 2]
等同于[2, 1]
)。我现在想找到只出现一次的对,并得到一个布尔列表来表示。对于上面的例子,
b = [False, False, False, True, False]
由于a
通常很大,我想避免使用显式循环。可能会建议映射到frozenset
,但我不确定这是否过度。
答案 0 :(得分:14)
ctr = Counter(frozenset(x) for x in a)
b = [ctr[frozenset(x)] == 1 for x in a]
我们可以使用Counter来获取每个列表的计数(将列表转到冻结集以忽略顺序),然后对每个列表检查它是否只出现一次。
答案 1 :(得分:6)
以下是使用NumPy的解决方案比建议的frozenset
解决方案快10倍:
a = numpy.array(a)
a.sort(axis=1)
b = numpy.ascontiguousarray(a).view(
numpy.dtype((numpy.void, a.dtype.itemsize * a.shape[1]))
)
_, inv, ct = numpy.unique(b, return_inverse=True, return_counts=True)
print(ct[inv] == 1)
排序速度很快,并确保原始数组中的边[i, j]
,[j, i]
相互识别。比frozenset
或tuple
s。
不同阵列尺寸的速度比较:
情节是用
创建的from collections import Counter
import numpy
import perfplot
def fs(a):
ctr = Counter(frozenset(x) for x in a)
b = [ctr[frozenset(x)] == 1 for x in a]
return b
def with_numpy(a):
a = numpy.array(a)
a.sort(axis=1)
b = numpy.ascontiguousarray(a).view(
numpy.dtype((numpy.void, a.dtype.itemsize * a.shape[1]))
)
_, inv, ct = numpy.unique(b, return_inverse=True, return_counts=True)
res = ct[inv] == 1
return res
perfplot.show(
setup=lambda n: numpy.random.randint(0, 10, size=(n, 2)),
kernels=[fs, with_numpy],
labels=['frozenset', 'numpy'],
n_range=[2**k for k in range(12)],
xlabel='len(a)',
logx=True,
logy=True,
)
答案 2 :(得分:2)
您可以从头到尾扫描列表,同时将map
个遇到的对保留到第一个位置。无论何时处理一对,都要检查以前是否遇到过它。如果是这种情况,则b中的第一个遭遇索引和当前遭遇索引都必须设置为False。否则,我们只是将当前索引添加到遇到的对的映射中,并且不对b进行任何更改。 b最初将开始全部True
。为了保持与[1,2]
和[2,1]
相当的东西,我首先简单地对该对进行排序,以获得稳定的表示。代码看起来像这样:
def proc(a):
b = [True] * len(a) # Better way to allocate this
filter = {}
idx = 0
for p in a:
m = min(p)
M = max(p)
pp = (m, M)
if pp in filter:
# We've found the element once previously
# Need to mark both it and the current value as "False"
# If we encounter pp multiple times, we'll set the initial
# value to False multiple times, but that's not an issue
b[filter[pp]] = False
b[idx] = False
else:
# This is the first time we encounter pp, so we just add it
# to the filter for possible later encounters, but don't affect
# b at all.
filter[pp] = idx
idx++
return b
时间复杂度为O(len(a))
这很好,但空间复杂度也是O(len(a))
(对于filter
),所以这可能不是那么好。根据您的灵活程度,您可以使用近似过滤器,例如布隆过滤器。
答案 3 :(得分:2)
#-*- coding : utf-8 -*-
a = [[1, 2], [3, 6], [2, 1], [3, 5], [3, 6]]
result = filter(lambda el:(a.count([el[0],el[1]]) + a.count([el[1],el[0]]) == 1),a)
bool_res = [ (a.count([el[0],el[1]]) + a.count([el[1],el[0]]) == 1) for el in a]
print result
print bool_res
给出:
[[3, 5]]
[False, False, False, True, False]
答案 4 :(得分:-1)
使用字典表示O(n)解决方案。
a = [ [1, 2], [3, 6], [2, 1], [3, 5], [3, 6] ]
dict = {}
boolList = []
# Iterate through a
for i in range (len(a)):
# Assume that this element is not a duplicate
# This 'True' is added to the corresponding index i of boolList
boolList += [True]
# Set elem to the current pair in the list
elem = a[i]
# If elem is in ascending order, it will be entered into the map as is
if elem[0] <= elem[1]:
key = repr(elem)
# If not, change it into ascending order so keys can easily be compared
else:
key = repr( [ elem[1] ] + [ elem[0] ])
# If this pair has not yet been seen, add it as a key to the dictionary
# with the value a list containing its index in a.
if key not in dict:
dict[key] = [i]
# If this pair is a duploicate, add the new index to the dict. The value
# of the key will contain a list containing the indeces of that pair in a.
else:
# Change the value to contain the new index
dict[key] += [i]
# Change boolList for this to True for this index
boolList[i] = False
# If this is the first duplicate for the pair, make the first
# occurrence of the pair into a duplicate as well.
if len(dict[key]) <= 2:
boolList[ dict[key][0] ] = False
print a
print boolList