def check_set(S, k):
S2 = k - S
set_from_S2=set(S2.flatten())
for x in S:
if(x in set_from_S2):
return True
return False
我有一个给定的整数k。我想检查k是否等于数组S的两个元素的和。
S = np.array([1,2,3,4])
k = 8
在这种情况下,它应该返回False
,因为S中没有两个元素的总和为8。上面的代码像8 = 4 + 4
一样工作,因此它返回了True
我找不到复杂度为O(n)
的算法来解决这个问题。
有人可以帮助我吗?
答案 0 :(得分:6)
您必须考虑同一项目的多个实例,因此在此处设置不是一个好的选择。
相反,您可以使用value_field = number_of_keys
(作为变体-from collections import Counter
)来利用字典
A = [3,1,2,3,4]
Cntr = {}
for x in A:
if x in Cntr:
Cntr[x] += 1
else:
Cntr[x] = 1
#k = 11
k = 8
ans = False
for x in A:
if (k-x) in Cntr:
if k == 2 * x:
if Cntr[k-x] > 1:
ans = True
break
else:
ans = True
break
print(ans)
对于k = 5,6(我又添加3)返回True,对于k = 8,11返回False
答案 1 :(得分:4)
添加到MBo的答案中。
就算法学而言,“最优”可能是一个模棱两可的术语,因为在算法运行速度与内存效率之间经常存在折衷。有时我们也可能对最坏情况下的资源消耗或平均资源消耗感兴趣。我们将在最坏的情况下循环,因为它更简单,并且在我们的方案中大致等于平均值。
我们调用n
数组的长度,让我们考虑3个示例。
示例1
我们从一个非常幼稚的算法开始,针对这个问题,有两个嵌套循环遍历数组,并检查每两个具有不同索引的项的总和是否等于目标数。
时间复杂度:最坏的情况(答案为False
或答案为True
,但我们在检查的最后一对项目中找到了答案)具有{{1} }循环迭代。如果您熟悉big-O表示法,我们会说该算法的时间复杂度为n^2
,这基本上意味着,根据我们的输入大小O(n^2)
而言,求解该问题所需的时间算法的增长或多或少像n
一样具有乘数因子(嗯,从技术上讲,该符号的意思是“ 最多像 n^2
一样具有乘数因子,但这是对语言的普遍滥用改为使用“或多或少”。
空间复杂度(内存消耗):我们只存储一个数组,以及一组固定的对象,这些对象的大小不依赖于n^2
(Python需要运行的所有东西,调用堆栈,也许是两个迭代器)和/或一些临时变量)。因此,n
所增加的一部分内存消耗仅是数组的大小,它是n
乘以在数组中存储整数所需的内存量(我们称{{1 }}。
结论:时间是n
,内存是sizeof(int)
(O(n^2)
,也就是说,取决于一个附加的常数,这对我们来说无关紧要,而我们会从现在开始忽略。)
示例2
让我们在MBo的答案中考虑算法。
时间复杂度:比示例1好得多。我们首先创建一个字典。这是在n*sizeof(int)
上循环进行的。在适当的条件下,在字典中设置键是恒定时间的操作,因此该第一循环的每个步骤所花费的时间不取决于+O(1)
。因此,就时间复杂度而言,目前我们使用n
。现在,我们在n
上只剩下一个循环。访问字典中元素所花费的时间与O(n)
无关,因此,总复杂度再次为n
。将两个循环组合在一起,因为它们都像n
一样增长到一个乘法因子,所以它们的总和(直到一个不同的乘法因子)也一样。总计:O(n)
。
内存:与以前基本相同,外加n
个元素的字典。为了简单起见,让我们考虑这些元素是整数(我们本可以使用布尔值),而忽略了字典的某些方面,只计算用于存储键和值的大小。有O(n)
个整数键和n
个整数值要存储,它们在内存上使用n
。加上以前的内容,我们总共有n
。
结论:时间为2*n*sizeof(int)
,内存为3*n*sizeof(int)
。当O(n)
增长时,该算法的速度要快得多,但使用的内存却比示例1多三倍。在一些几乎没有可用内存的怪异场景中(可能是嵌入式系统),这个3*n*sizeof(int)
可能太多了,而且您可能无法使用此算法(不可否认,它可能永远不会成为一个真正的问题)。
示例3
我们可以在示例1和示例2之间找到权衡吗?
一种实现方法是复制与示例1中相同的嵌套循环结构,但需要进行一些预处理以更快地替换内部循环。为此,我们对初始数组进行排序。用well-chosen algorithms完成,它的时间复杂度为n
,并且内存使用率可以忽略不计。
对数组进行排序后,我们将编写外循环(这是整个数组的常规循环),然后在该外循环内,使用二分法搜索缺少的数字以达到目标{ {1}}。这种二分法的内存消耗为3*n*sizeof(int)
,时间复杂度也为O(n*log(n))
。
时间复杂度:预处理排序为k
。然后,在算法的主要部分中,我们对O(log(n))
二分搜索进行了O(log(n))
调用,总计为O(n*log(n))
。因此,总体而言,n
。
内存:忽略常数部分,我们拥有数组的内存(O(log(n))
)和二分法搜索(O(n*log(n))
)中的调用堆栈内存。总计:O(n*log(n))
。
结论:时间为n*sizeof(int)
,内存为O(log(n))
。内存几乎与示例1中的内存一样小。时间复杂度比示例2中的稍大。在由于缺少内存而无法使用示例2的情况下,就速度而言,次优的选择实际上是示例3,即几乎与示例2一样快,并且如果示例1的速度很慢,则可能有足够的运行空间。
总体结论
这个答案只是为了表明“最优”在算法上是上下文相关的。在这个特定示例中,人们不太可能选择实现示例3。通常,如果n*sizeof(int) + O(log(n))
太小,您会看到示例1,那么人们会选择最简单的设计和最快的编码,或示例2,如果O(n*log(n))
大一点,我们想要速度。但是,如果您查看我链接用于排序算法的Wikipedia页面,您会发现它们都不是所有方面的最佳选择。他们都有可以用更好的东西代替的方案。