检查数字不是列表中 2 个整数的总和
Posted
技术标签:
【中文标题】检查数字不是列表中 2 个整数的总和【英文标题】:Check number not a sum of 2 ints on a list 【发布时间】:2018-01-21 13:29:11 【问题描述】:给定一个整数列表,我想检查第二个列表,并从第一个列表中删除那些不能由第二个中的两个数字之和组成的列表。所以给定a = [3,19,20]
和b = [1,2,17]
,我想要[3,19]
。
看起来很简单,有两个嵌套循环 - 除了我被 break
和 continue
命令卡住了。
这是我所拥有的:
def myFunction(list_a, list_b):
for i in list_a:
for a in list_b:
for b in list_b:
if a + b == i:
break
else:
continue
break
else:
continue
list_a.remove(i)
return list_a
我知道我需要做什么,只是语法似乎不必要地令人困惑。有人可以告诉我一个更简单的方法吗?蒂亚!
【问题讨论】:
如果a
包含a = [3, 4, 19, 20]
怎么办?那么有两个可能的和:3+20
和4+19
,那么在这种情况下要删除什么?
对b
进行排序并使用二分搜索...?
19也可以用2+17组成。另外,如何从列表 B 的 2 个元素的总和中得出 3?
抱歉,我不小心使用了不正确的示例 - 现在已修复。
@JohnColeman:是的,只是因为我正在通过删除那些无法制作的东西来接近它。早些时候,我试图列出所有 可以 制作的内容,然后删除重复项,但这似乎效率较低。
【参考方案1】:
你可以这样做,
In [13]: from itertools import combinations
In [15]: [item for item in a if item in [sum(i) for i in combinations(b,2)]]
Out[15]: [3, 19]
combinations
将给出b
中所有可能的组合并得到总和列表。只需检查a
中存在的值
编辑
如果你不想使用itertools
为它写了一个函数。像这样,
def comb(s):
for i, v1 in enumerate(s):
for j in range(i+1, len(s)):
yield [v1, s[j]]
result = [item for item in a if item in [sum(i) for i in comb(b)]]
【讨论】:
谢谢!这很好,但我试图在没有 import 语句的情况下解决它 - 这是(更长、更复杂的)家庭作业的一部分。 @JoshFriedlander 更新了答案。【参考方案2】:代码注释:
在迭代列表时从列表中删除元素是非常危险的。也许您可以将要保留的项目附加到新列表中,然后将其返回。 您当前的算法是O(nm^2)
,其中n
是list_a
的大小,m
是list_b
的大小。这是非常低效的,但问题的一个良好开端。
还有很多不必要的continue
和break
语句,这会导致复杂的代码难以调试。
您还可以将所有内容都放在一个函数中。如果您将每个任务拆分为不同的功能,例如将一个功能用于查找配对,一个用于检查list_a
中的每个项目与list_b
。这是一种将问题分解成更小的问题,并用它们来解决更大问题的方法。
总的来说,我认为您的函数做得太多,通过分解问题可以将逻辑压缩为更简单的代码。
另一种方法:
因为我觉得这个任务很有趣,所以我决定自己尝试一下。我概述的方法如下所示。
1.您可以首先使用散列法检查列表是否在O(n)
时间内有一对给定的总和:
def check_pairs(lst, sums):
lookup = set()
for x in lst:
current = sums - x
if current in lookup:
return True
lookup.add(x)
return False
2.然后你可以使用这个函数来检查list_b
中的任何一对是否等于list_a
中迭代的数字之和:
def remove_first_sum(list_a, list_b):
new_list_a = []
for x in list_a:
check = check_pairs(list_b, x)
if check:
new_list_a.append(x)
return new_list_a
它保留了list_a
中的数字,这些数字有助于list_b
中的两个数字之和。
3.上面也可以写成列表推导式:
def remove_first_sum(list_a, list_b):
return [x for x in list_a if check_pairs(list_b, x)]
两者的工作原理如下:
>>> remove_first_sum([3,19,20], [1,2,17])
[3, 19]
>>> remove_first_sum([3,19,20,18], [1,2,17])
[3, 19, 18]
>>> remove_first_sum([1,2,5,6],[2,3,4])
[5, 6]
注意: 总体而言,上述算法的时间复杂度为O(n)
,不需要太复杂。但是,这也导致了O(n)
额外的辅助空间,因为保存了一个集合来记录所看到的项目。
【讨论】:
如果我在这里忽略了什么,请告诉我。 非常有帮助,谢谢!时间复杂度在这里非常重要,因此我最终使用了您答案的修改版本。【参考方案3】:您可以先创建所有可能的总和组合,然后过滤掉不属于该组合列表的元素
定义输入列表
>>> a = [3,19,20]
>>> b = [1,2,17]
接下来我们将定义两个元素之和的所有可能组合
>>> y = [i+j for k,j in enumerate(b) for i in b[k+1:]]
接下来,我们将对列表a
的每个元素应用一个函数,并检查它是否存在于上面计算的列表中。 map
函数可以与 if/else
子句一起使用。 map
将在 else 子句成功的情况下产生 None
。为此,我们可以过滤列表以删除 None
值
>>> list(filter(None, map(lambda x: x if x in y else None,a)))
以上操作会输出:
>>> [3,19]
你也可以将所有这些行合并为一行,但我不推荐这样做。
【讨论】:
【参考方案4】:你可以试试这样的:
a = [3,19,20]
b= [1,2,17,5]
n_m_s=[]
data=[n_m_s.append(i+j) for i in b for j in b if i+j in a]
print(set(n_m_s))
print("after remove")
final_data=[]
for j,i in enumerate(a):
if i not in n_m_s:
final_data.append(i)
print(final_data)
输出:
19, 3
after remove
[20]
【讨论】:
以上是关于检查数字不是列表中 2 个整数的总和的主要内容,如果未能解决你的问题,请参考以下文章