比较2个列表以查找相等或近似匹配而不进行N ^ 2迭代

时间:2013-11-21 04:24:59

标签: python performance list

我有两个清单:

list1 = [101, 110, 136]
list2 = [101.04, 264.5, 379.9, 466.4, 629.6, 724.4, 799.8, 914.3]

迭代list1并将此列表中的每个元素与list2中的元素进行比较。如果第二个列表中遇到的数字与list1中的元素完全匹配或近似匹配,则输出该匹配。

注意:我想严格避免N ^ 2迭代,因为我想尽可能高效地进行迭代

3 个答案:

答案 0 :(得分:2)

您是否考虑过近似的含义?

>>> list1 = [101, 110, 136]
>>> list2 = [101.04, 264.5, 379.9, 466.4, 629.6, 724.4, 799.8, 914.3]
>>> set(int(x) for x in list1) & set(int(x) for x in list2)
set([101])

很简单,但如果list2[100.96, 264.5, 379.9, ...则您将无法获得匹配

当您定义“近似”时,您可以开始正确地思考解决方案。

如果列表被预先排序,那么提及

将是一件有用的事情

答案 1 :(得分:2)

事实证明这个问题有点棘手。下面的代码适用于任何数据集和边距值,但我没有对它进行过广泛的测试。

避免O(N ^ 2)性能的唯一方法是对数据进行排序,这样可以使用两个索引值,这样您就可以以不同于第一个列表的速率遍历第二个列表进行有效的比较。

下面的代码将为List1中的每个项目打印出List2中的每个匹配项,因此打印可能会有一些重复,因此性能会比O(n)略差,但是利用较小的边距会更好。 (这里选择较大的余量来夸大设置高或低的影响)。

list1 = [101, 110, 136, 380]
list2 = [101.04, 110.009, 264.5, 379.9, 466.4, 629.6, 724.4, 799.8, 914.3]
#guarantee that lists are sorted
list1.sort()
list2.sort()
#Set margin differently as needed
margin = 100
idx = 0;
for i in list1:
    while i > list2[idx] and not abs(i - list2[idx]) <= margin:
        idx+=1
    tempIdx = idx
    #Print out all the elements in list2 that are within the margin for list1
    while abs(i - list2[tempIdx]) <= margin:
        print list2[tempIdx]
        tempIdx+=1

答案 2 :(得分:1)

这应该给出O(nlogn)时间(由于两种类型),具有用户指定的容差epsilon。它松散地基于mergesort的合并步骤:

#!/usr/local/cpython-3.3/bin/python

import pprint

def approximate_matches(list1, list2, epsilon = 0.5):
    len_list1 = len(list1)
    len_list2 = len(list2)

    list1_index = 0
    list2_index = 0

    while list1_index < len_list1 and list2_index < len_list2:
        list1_element = list1[list1_index]
        list2_element = list2[list2_index]

        difference = abs(list1_element - list2_element)

        if difference < epsilon:
            yield (list1_element, list2_element)
            list1_index += 1
            list2_index += 1
        elif list1_element < list2_element:
            list1_index += 1
        elif list2_element < list1_element:
            list2_index += 1
        else:
            raise AssertionError('Unexpected else taken')


def main():
    list1 = [101.0, 110.0, 136.0, 379.6, 800.0, 900.0]
    list2 = [101.04, 264.5, 379.9, 466.4, 629.6, 724.4, 799.8, 914.3]

    list1.sort()
    list2.sort()

    pprint.pprint(list(approximate_matches(list1, list2)))

main()

HTH

PS:请注意,如果list1中的一个数字与list2中的两个数字匹配(反之亦然),则此代码仅报告一个匹配。