查找总和为N的对的更好方法

时间:2018-09-29 08:50:14

标签: python

有没有更快的方法来编写此函数,该函数需要一个列表和一个值以在该列表中查找成对的N对数字值,这些对的总和为N,而没有重复项。我试图通过使用集合而不是使用列表来使其更快本身(但是我使用的是count(),但我知道这是线性时间),我知道有什么建议可能是一种方法

def pairsum_n(list1, value):

    set1 = set(list1)
    solution = {(min(i, value - i) , max(i, value - i)) for i in set1 if value - i in set1}
    solution.remove((value/2,value/2)) if list1.count(value/2) < 2 else None           
    return solution
"""
    Example: value = 10, list1 = [1,2,3,4,5,6,7,8,9]
    pairsum_n = { (1,9), (2,8), (3,7), (4,6) }
    Example: value = 10,  list2 = [5,6,7,5,7,5,3]
    pairsum_n = { (5,5), (3,7) }
"""

4 个答案:

答案 0 :(得分:3)

尝试一下,运行时间O(nlogn):

v = [1, 2, 3, 4, 5, 6, 7, 8, 9]
l = 0
r = len(v)-1

def myFunc(v, value):

    ans = []

    % this block search for the pair (value//2, value//2)
    if value % 2 == 0:
        c = [i for i in v if i == value // 2]
        if len(c) >= 2:
            ans.append((c[0], c[1]))

    v = list(set(v))
    l = 0
    r = len(v)-1
    v.sort()
    while l<len(v) and r >= 0 and l < r:
        if v[l] + v[r] == value:
            ans.append((v[l], v[r]))
            l += 1
            r -= 1
        elif v[l] + v[r] < value:
            l += 1
        else:
            r -= 1

    return list(set(ans))

它称为Two pointers technique,其工作原理如下。首先,对数组进行排序。这强加了最小运行时间O(nlogn)。然后设置两个指针,一个指针指向数组l的开始,另一个指针指向数组的最后一个元素r(指针名称分别用于左侧和右侧)。

现在,查看列表。如果在位置l和r返回的值之和小于我们要寻找的值,则需要递增l。如果更大,则需要递减r

如果v[l] + v[r] == value可以同时增加/减少lr,因为在任何情况下我们都希望跳过值(v[l], v[r])的组合,因为我们不想重复。

答案 1 :(得分:3)

您的方法非常好,只需进行一些调整即可使其更有效。 itertools很方便,但实际上并不适合此任务,因为它会产生很多不需要的对。输入列表较小可以,但是输入列表较大则太慢。

我们可以通过以下方法来避免产生重复项:依次使用数字集,在使用i >= value/2来消除重复项之后停止。

def pairsum_n(list1, value): 
    set1 = set(list1)
    list1 = sorted(set1)
    solution = []
    maxi = value / 2
    for i in list1:
        if i >= maxi:
            break
        j = value - i
        if j in set1:
            solution.append((i, j))
    return solution

请注意,原始list1未被修改。此函数中的分配创建一个新的本地list1。如果您确实希望在输出中使用(value/2, value/2),只需更改break条件。


这是一个稍微紧凑的版本。

def pairsum_n(list1, value): 
    set1 = set(list1)
    solution = []
    for i in sorted(set1):
        j = value - i
        if i >= j:
            break
        if j in set1:
            solution.append((i, j))
    return solution

可以进一步压缩,例如使用itertools.takewhile,但它很难阅读,效率也没有任何提高。

答案 2 :(得分:2)

时间:实际上,这比其他两种解决方案要慢。由于生成的组合数量很多,但实际上并不需要,因此列表越大,情况就越糟。


您可以使用itertools.combinations为您生成2元组组合。

如果它们与您的value相匹配,则将它们放入集合中,然后作为集合/列表返回:

from itertools import combinations 

def pairsum_n(list1, value): 
    """Returns the unique list of pairs of combinations of numbers from 
    list1 that sum up `value`. Reorders the values to (min_value,max_value)."""
    result = set()
    for n in combinations(list1, 2):
        if sum(n) == value:
            result.add( (min(n),max(n)) )
    return list(result)

    # more ugly one-liner:
    # return list(set(((min(n),max(n)) for n in combinations(list1,2) if sum(n)==value)))

data = [1,2,3,4,5,6,6,5,4,3,2,1]

print(pairsum_n(data,7))

输出:

[(1, 6), (2, 5), (3, 4)]

有趣的小事情,加上一些排序开销,您可以一次获得所有内容:

def pairsum_n2(data, count_nums=2):
    """Generate a dict with all count_nums-tuples from data. Key into the
    dict is the sum of all tuple-values."""
    d = {}
    for n in (tuple(sorted(p)) for p in combinations(data,count_nums)):
        d.setdefault(sum(n),set()).add(n)
    return d

get_all =  pairsum_n2(data,2) # 2 == number of numbers to combine
for k in get_all:
    print(k," -> ", get_all[k])

输出:

 3  ->  {(1, 2)}
 4  ->  {(1, 3), (2, 2)}
 5  ->  {(2, 3), (1, 4)}
 6  ->  {(1, 5), (2, 4), (3, 3)}
 7  ->  {(3, 4), (2, 5), (1, 6)}
 2  ->  {(1, 1)}
 8  ->  {(2, 6), (4, 4), (3, 5)}
 9  ->  {(4, 5), (3, 6)}
10  ->  {(5, 5), (4, 6)}
11  ->  {(5, 6)}
12  ->  {(6, 6)}

然后通过以下方式访问所需的那个:

print(get_all.get(7,"Not possible"))    #  {(3, 4), (2, 5), (1, 6)}
print(get_all.get(17,"Not possible"))   #  Not possible

答案 3 :(得分:1)

还有另一种解决方案,它比我刚才写的要快得多,不如@PM 2Ring的答案快

def pairsum_n(list1, value):
    set1 = set(list1)
    if list1.count(value/2) < 2:
        set1.remove(value/2)
    return set((min(x, value - x) , max(x, value - x)) for x in filterfalse(lambda x: (value - x) not in set1, set1))