给出2个列表:
a = [3,4,5,5,5,6]
b = [1,3,4,4,5,5,6,7]
我想找到“重叠”:
c = [3,4,5,5,6]
我也喜欢它,如果我可以提取“剩余”部分a和b不在c中。
a_remainder = [5,]
b_remainder = [1,4,7,]
注意:a中有三个5,b有两个。 b里面有两个4,有一个。
结果列表c应该有两个5(由列表b限制)和一个4(由列表a限制)。
这给了我想要的东西,但我不禁想到有更好的方法。
import copy
a = [3,4,5,5,5,6]
b = [1,3,4,4,5,5,6,7]
c = []
for elem in copy.deepcopy(a):
if elem in b:
a.pop(a.index(elem))
c.append(b.pop(b.index(elem)))
# now a and b both contain the "remainders" and c contains the "overlap"
另一方面,对于我要求的是什么比“重叠”和“剩余”更准确的名称?
Python 2.7中提供的collection.Counter
可用于实现完全符合您需要的多字符集。
a = [3,4,5,5,5,6]
b = [1,3,4,4,5,5,6,7]
a_multiset = collections.Counter(a)
b_multiset = collections.Counter(b)
overlap = list((a_multiset & b_multiset).elements())
a_remainder = list((a_multiset - b_multiset).elements())
b_remainder = list((b_multiset - a_multiset).elements())
print overlap, a_remainder, b_remainder
在集合的语言中,重叠是“交集”,余数是“集合差异”。如果你有不同的项目,你不必自己做这些操作,如果你有兴趣,请查看http://docs.python.org/library/sets.html。
由于我们不使用不同的元素,因此您的方法是合理的。如果你想让它运行得更快,你可以为每个列表创建一个字典,并将数字映射到每个数组中的元素数量(例如,在a,3-> 1,4-> 1,5-> 2等中)。然后,您将遍历映射a,确定该字母是否存在,减少其计数并将其添加到新列表中
未经测试的代码,但这是个主意
def add_or_update(map,value):
if value in map:
map[value]+=1
else
map[value]=1
b_dict = dict()
for b_elem in b:
add_or_update(b_dict,b_elem)
intersect = []; diff = [];
for a_elem in a:
if a_elem in b_dict and b_dict[a_elem]>0:
intersect.add(a_elem);
for k,v in diff:
for i in range(v):
diff.add(k);
使用python set
intersection = set(a) & set(b)
a_remainder = set(a) - set(b)
b_remainder = set(b) - set(a)
好吧,冗长,但有点酷(类似于collections.Counter
的想法,但更多的自制):
import itertools as it
flatten = it.chain.from_iterable
sorted(
v for u,v in
set(flatten(enumerate(g)
for k, g in it.groupby(a))).intersection(
set(flatten(enumerate(g)
for k, g in it.groupby(b))))
)
基本思想是将每个列表放入一个新的列表中,该列表将每个对象附加一个计数器,编号为重复项 - 这样你就可以在这些元组上使用set
操作了。
稍微冗长一点:
aa = set(flatten(enumerate(g) for k, g in it.groupby(a)))
bb = set(flatten(enumerate(g) for k, g in it.groupby(b)))
# aa = set([(0, 3), (0, 4), (0, 5), (0, 6), (1, 5), (2, 5)])
# bb = set([(0, 1), (0, 3), (0, 4), (0, 5), (0, 6), (0, 7), (1, 4), (1, 5)])
cc = aa.intersection(bb)
# cc = set([(0, 3), (0, 4), (0, 5), (0, 6), (1, 5)])
c = sorted(v for u,v in cc)
# c = [3, 4, 5, 5, 6]
groupby
- 生成包含相同元素的列表列表(但由于语法需要g for k,g in it.groupby(a)
来提取每个列表)enumerate
- 在每个子列表的每个元素后附加一个计数器flatten
- 创建一个列表set
- 转换为一套intersection
- 找到共同的元素sorted(v for u,v in cc)
- 摆脱计数器并对结果进行排序最后,我不确定剩下的人是什么意思;它似乎应该是我的aa-cc
和bb-cc
但我不知道你在哪里得到a_remainder = [4]
:
sorted(v for u,v in aa-cc)
# [5]
sorted(v for u,v in bb-cc)
# [1, 4, 7]
来自freenode的#python中kerio的回复:
[ i for i in itertools.chain.from_iterable([k] * v for k, v in \
(Counter(a) & Counter(b)).iteritems())
]
试试difflib.SequenceMatcher(),“用于比较任何类型序列对的灵活类”......
快速尝试:
a = [3,4,5,5,5,6]
b = [1,3,4,4,5,5,6,7]
sm = difflib.SequenceMatcher(None, a, b)
c = []
a_remainder = []
b_remainder = []
for tag, i1, i2, j1, j2 in sm.get_opcodes():
if tag == 'replace':
a_remainder.extend(a[i1:i2])
b_remainder.extend(b[j1:j2])
elif tag == 'delete':
a_remainder.extend(a[i1:i2])
elif tag == 'insert':
b_remainder.extend(b[j1:j2])
elif tag == 'equal':
c.extend(a[i1:i2])
现在...
>>> print c
[3, 4, 5, 5, 6]
>>> print a_remainder
[5]
>>> print b_remainder
[1, 4, 7]
Aset = Set(a);
Bset = Set(b);
a_remainder = a.difference(b);
b_remainder = b.difference(a);
c = a.intersection(b);
但如果你需要c
有重复,订单对你很重要,
你可能会寻找w:Longest common subsequence problem
我不认为你应该真的使用这个解决方案,但我借此机会练习lambda函数,这就是我想出来的:)
a = [3,4,5,5,5,6]
b = [1,3,4,4,5,5,6,7]
dedup = lambda x: [set(x)] if len(set(x)) == len(x) else [set(x)] + dedup([x[i] for i in range(1, len(x)) if x[i] == x[i-1]])
default_set = lambda x: (set() if x[0] is None else x[0], set() if x[1] is None else x[1])
deduped = map(default_set, map(None, dedup(a), dedup(b)))
get_result = lambda f: reduce(lambda x, y: list(x) + list(y), map(lambda x: f(x[0], x[1]), deduped))
c = get_result(lambda x, y: x.intersection(y)) # [3, 4, 5, 6, 5]
a_remainder = get_result(lambda x, y: x.difference(y)) # [5]
b_remainder = get_result(lambda x, y: y.difference(x)) # [1, 7, 4]
我很确定izip_longest会简化这一点(不需要default_set
lambda),但我用Python 2.5进行了测试。
以下是计算中使用的一些中间值,以防有人想要理解这一点:
dedup(a) = [set([3, 4, 5, 6]), set([5]), set([5])]
dedup(b) = [set([1, 3, 4, 5, 6, 7]), set([4, 5])]
deduped = [(set([3, 4, 5, 6]), set([1, 3, 4, 5, 6, 7])), (set([5]), set([4, 5])), (set([5]), set([]))]