【问题标题】:Return tuple of any two items in list, which if summed equal given int返回列表中任意两项的元组,如果求和等于给定 int
【发布时间】:2014-05-30 22:53:28
【问题描述】:

例如,假设给定的整数列表:

int_list = list(range(-10,10))
[-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

如果int_list 中的任何给定两个值总和等于给定 int,例如2,最有效的方法是什么?

我在今天早上的一次技术电话采访中被问到如何有效地处理这种情况,int_list 说有 1 亿个项目(我漫无目的地回答:/)。

我的第一个想法是:

from itertools import combinations
int_list = list(range(-10,10))
combo_list = list(combinations(int_list, 2))
desired_int = 4
filtered_tuples = list(filter(lambda x: sum(x) == desired_int, combo_list))
filtered_tuples
[(-5, 9), (-4, 8), (-3, 7), (-2, 6), (-1, 5), (0, 4), (1, 3)]

这甚至不适用于只有 range(-10000, 10000) 的范围

另外,有没有人知道一个好的在线 Python 性能测试工具?

【问题讨论】:

  • 请参阅stackoverflow.com/a/12775802/1899640 以获取此问题的重复项列表
  • 顺便说一下,如果这是列表采用单个参数 n 的范围(-n,n)的狭窄情况,那么我相信我的第二个答案是最好的和最高效的一。如果这是任何可能的整数列表的更一般情况,那么亚当的答案是最好的。
  • 样本范围就是这样,一个样本。现实世界的情况是任何可能的整数列表。
  • @AaronHall 给你。也就是说,我从您的第二个答案中删除了“-1”,因为 OP 发现它很有用(尽管它仅涵盖特定情况并且不提供 real 答案)。

标签: python list tuples


【解决方案1】:

对于任何整数A,最多有一个整数B 相加等于整数N。遍历列表、进行算术运算并进行成员资格测试以查看 B 是否在集合中似乎更容易。

int_list = set(range(-500000, 500000))
TARGET_NUM = 2

def filter_tuples(int_list, target):
    for int_ in int_list:
        other_num = target - int_
        if other_num in int_list:
            yield (int_, other_num)

filtered_tuples = filter_tuples(int_list, TARGET_NUM)

请注意,这会重复结果。例如。 (-2, 4) 是来自 (4, -2) 的单独回复。您可以通过更改函数来解决此问题:

def filter_tuples(int_list, target):
    for int_ in int_list:
        other_num = target - int_
        if other_num in int_list:
            set.remove(int_)
            set.remove(other_num)
            yield (int_, other_num)

【讨论】:

    【解决方案2】:

    编辑:请参阅我的其他答案以获得更好的方法(带有警告)。

    查找 int_list 中任何给定两个值的总和是否等于给定 int(例如 2)的最有效方法是什么?

    我的第一个想法是使用itertools 模块的combinationsany 的捷径,但它可能比Adam 的方法慢很多:

    >>> import itertools
    >>> int_list = list(range(-10,10))
    >>> any(i + j == 2 for i, j in itertools.combinations(int_list, 2))
    True
    

    似乎对更大的范围反应相当灵敏:

    >>> any(i + j == 2 for i, j in itertools.combinations(xrange(-10000,10000), 2))
    True
    >>> any(i + j == 2 for i, j in itertools.combinations(xrange(-1000000,1000000), 2))
    True
    

    在我的机器上大约需要 10 秒:

    >>> any(i + j == 2 for i, j in itertools.combinations(xrange(-10000000,10000000), 2))
    True
    

    【讨论】:

    • 他的示例代码给我的印象是,他需要生成组合,而不是简单地断言它们存在。不过我可能是错的! itertools.combinations 是否返回生成器或列表?这可能是一个巨大的内存消耗(尽管无疑比 OP 代码中的 list(combinations)
    • 这就是所说的问题,称我为迟钝的逻辑学家,但我认为它是正确的。
    • 遍历所有组合不是解决这个问题的“最有效的方法”。
    • @alfasin ,请参阅我的其他答案。
    • @alfasin 值得注意的是,我的更快,因为它只是构建一个生成器,它不会创建任何值。 Aaron's 构建了整个值列表并断言存在组合。如果您进行了一个返回 False 并针对 Aaron 运行 any(My_function(false_num, range(small_number,big_number))) 的测试,他会更快。
    【解决方案3】:

    使用数学的更直接的方法:

    假设给定的整数列表:

    int_list = list(范围(-10,10)) ... [-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

    如果有任何给定的两个值,最有效的方法是什么 int_list 总和等于给定的 int,比如 2? ... 如何有效地 使用包含 1 亿个项目的 int_list 来处理这种情况。

    很明显,我们可以推断出我们可以对整数范围应用单个参数 n 的要求,其形式为 range(-n, n),表示从负 n 到但不包括的每个整数积极的 n.从那里可以清楚地看出,某个数字 x 是否是该范围内任意两个整数的和。

    任何这样的范围都可以简单地显示为包含一个对,该对的总和为该范围内的任意数字和超出该范围的 n-1,因此搜索它是浪费计算能力。

    def x_is_sum_of_2_diff_numbers_in_range(x, n):
        if isinstance(x, int) and isinstance(n, int):
            return -(n*2) < x < (n - 1)*2
        else:
            raise ValueError('args x and n must be ints')
    

    几乎立即计算:

    >>> x_is_sum_of_2_diff_numbers_in_range(2, 1000000000000000000000000000)
    True
    

    测试边缘情况:

    def main():
        print x_is_sum_of_2_diff_numbers_in_range(x=5, n=4) # True
        print x_is_sum_of_2_diff_numbers_in_range(x=6, n=4) # False
        print x_is_sum_of_2_diff_numbers_in_range(x=-7, n=4) # True
        print x_is_sum_of_2_diff_numbers_in_range(x=-8, n=4) # False
    

    编辑:

    由于我可以看到这个问题的更普遍的版本(列表可以包含任何给定的数字)是一个常见的版本,我可以理解为什么有些人对此有先入为主的方法,但我仍然坚持我的解释这个问题的要求,我认为这个答案是这个更具体案例的最佳方法。

    【讨论】:

    • 我看不到如何在以下整数列表中运行您的答案:[1,10,7,2,15,-12,-10]
    • @alfasin 但这不是提问者所问的。
    • @AaronHall 第一句怎么样:“例如,假设给定的整数列表...”?
    • @alfasin 不,您的假设不正确。提问者提供了一个以相同的 -start 和 stop 以及一个这样的参数递增的范围。他确实在这里感谢我。
    • Aaron:我喜欢看到这些以数学为中心的答案!我自己真的很讨厌他们。位玩弄和算法不是我的强项,建模和迭代才是。我很欣赏(imo 更好)的方法,即使它有点局限。
    【解决方案4】:

    我会认为任何依赖于列表上的双重嵌套迭代的解决方案(尽管内部循环被漂亮的 Python 函数隐藏)都是 O(n^2)。

    值得考虑对输入进行排序。对于任何合理的基于比较的排序,这将是 O(n.lg(n)),这已经优于 O(n^2)。根据输入列表的范围,使用基数排序或预排序(制作类似于桶排序)可能会做得更好。

    对输入进行排序后,要找到一对总和为任何给定数字的数字是一个 O(n) 操作,因此您的总体复杂度为 O(n.lg(n))。

    在实践中,对于规定的“大量”元素,具有良好缓存行为(按顺序压缩数组)的蛮力 O(n^2) 算法是否会优于渐近更好的算法,这是一个悬而未决的问题这会移动大量数据,但最终具有较低渐近复杂度的数据将获胜。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-10-10
      • 1970-01-01
      • 1970-01-01
      • 2021-09-04
      • 2019-03-27
      • 1970-01-01
      • 1970-01-01
      • 2015-01-10
      相关资源
      最近更新 更多