如何查找存在于两个列表中但具有不同索引的元素

时间:2013-05-13 18:06:01

标签: python algorithm list language-agnostic

我有两个相同长度的列表,其中包含各种不同的元素。我正在尝试比较它们以找到两个列表中存在的元素数量,但它们具有不同的索引。

以下是一些示例输入/输出来说明我的意思:

>>> compare([1, 2, 3, 4], [4, 3, 2, 1])
4
>>> compare([1, 2, 3], [1, 2, 3])
0
# Each item in the first list has the same index in the other
>>> compare([1, 2, 4, 4], [1, 4, 4, 2])
2
# The 3rd '4' in both lists don't count, since they have the same indexes
>>> compare([1, 2, 3, 3], [5, 3, 5, 5])
1
# Duplicates don't count

列表的大小始终相同。

这是我到目前为止的算法:

def compare(list1, list2):
    # Eliminate any direct matches
    list1 = [a for (a, b) in zip(list1, list2) if a != b]
    list2 = [b for (a, b) in zip(list1, list2) if a != b]

    out = 0
    for possible in list1:
        if possible in list2:
            index = list2.index(possible)
            del list2[index]
            out += 1
    return out

是否有更简洁,更有说服力的方法来做同样的事情?

3 个答案:

答案 0 :(得分:1)

由于重复项不计,您可以使用set来仅查找每个list中的元素。 set仅包含唯一元素。然后使用list.index

仅选择两者之间共享的元素
def compare(l1, l2):
    s1, s2 = set(l1), set(l2)
    shared = s1 & s2 # intersection, only the elements in both
    return len([e for e in shared if l1.index(e) != l2.index(e)])

如果你想要

,你实际上可以把它变成一个单行
def compare(l1, l2):
    return len([e for e in set(l1) & set(l2) if l1.index(e) != l2.index(e)])

<强>替代:

在功能上你可以使用reduce内置(在python3中,你必须先from functools import reduce)。这避免了构建列表,从而节省了过多的内存使用量。它使用lambda函数来完成工作。

def compare(l1, l2):
    return reduce(lambda acc, e: acc + int(l1.index(e) != l2.index(e)),
                  set(l1) & set(l2), 0)

简要说明:

reduce是一个函数式编程构造,它将传统的迭代减少到单个项目。在这里,我们使用reduceset交集减少为单个值。

lambda函数是匿名函数。说lambda x, y: x + 1就像说def func(x, y): return x + y,除了函数没有名字。 reduce将函数作为其第一个参数。 lambdareduce一起使用时收到的第一个参数是上一个函数accumulator的结果。

set(l1) & set(l2)是由l1l2中的唯一元素组成的集合。它被迭代,每个元素一次取出一个,并用作lambda函数的第二个参数。

0是累加器的初始值。我们使用它,因为我们假设有0个具有不同索引的共享元素开始。

答案 1 :(得分:1)

这个python函数适用于您提供的示例:

def compare(list1, list2):
    D = {e:i for i, e in enumerate(list1)}
    return len(set(e for i, e in enumerate(list2) if D.get(e) not in (None, i)))

答案 2 :(得分:1)

我不是说这是最简单的答案,但它只是一个单行。

import numpy as np
import itertools

l1 = [1, 2, 3, 4]
l2 = [1, 3, 2, 4]

print len(np.unique(list(itertools.chain.from_iterable([[a,b] for a,b in zip(l1,l2) if a!= b]))))

我解释说:

[[a,b] for a,b in zip(l1,l2) if a!= b]

是来自zip(l1,l2)的不同项目的情侣列表。此列表中的元素数是两个列表中相同位置的项目不同的位置数。

然后,list(itertools.chain.from_iterable()用于合并列表的组件列表。例如:

>>> list(itertools.chain.from_iterable([[3,2,5],[5,6],[7,5,3,1]]))
[3, 2, 5, 5, 6, 7, 5, 3, 1]

然后,使用np.unique()丢弃重复项,然后选择len()