【问题标题】:What is optimal algorithm to check if a given integer is equal to sum of two elements of an int array?检查给定整数是否等于 int 数组的两个元素之和的最佳算法是什么?
【发布时间】:2018-09-30 04:47:43
【问题描述】:
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,因为没有两个元素之和为8。上面的代码像8 = 4 + 4一样工作,所以它返回True

我找不到一个算法来解决这个复杂度为O(n) 的问题。

有人可以帮助我吗?

【问题讨论】:

  • 我试过运行它,它真的跑得很快。你可以看到this image

标签: python algorithm


【解决方案1】:

您必须考虑同一项目的多个实例,因此在这里设置不是很好的选择。

相反,您可以使用 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 时返回 True(我又加了一个 3),k=8,11 时返回 False

【讨论】:

  • 非常感谢,我终于明白了。这就是我想要找到的。再次感谢您
【解决方案2】:

添加到 MBo 的答案中。

“最优”在算法方面可能是一个模棱两可的术语,因为在算法运行速度和内存效率之间经常存在折衷。有时我们也可能对最坏情况下的资源消耗或平均资源消耗感兴趣。我们将在这里循环最坏的情况,因为它更简单并且大致相当于我们场景中的平均值。

让我们将数组的长度称为n,并考虑3个示例。

示例 1

我们从一个非常简单的算法开始解决我们的问题,两个嵌套循环遍历数组,并检查每两个不同索引的项目是否总和为目标数。

  • 时间复杂度:最坏的情况(答案是FalseTrue,但我们在检查的最后一对项目中找到它)有n^2 循环迭代。如果你熟悉大 O 表示法,我们会说算法的时间复杂度是O(n^2),这基本上意味着就我们的输入大小n 而言,求解算法所需的时间会增长更多或不像n^2 带有乘法因子(好吧,从技术上讲,符号的意思是“最多像 n^2 带有一个乘法因子,但将其用作“或多或少像" 代替)。

  • 空间复杂度(内存消耗):我们只存储一个数组,加上一组固定的对象,其大小不依赖于n(Python 需要运行的所有东西,调用堆栈,可能还有两个迭代器和/或一些临时变量)。因此,随着n 增长的内存消耗部分只是数组的大小,即n 乘以在数组中存储整数所需的内存量(我们称之为sizeof(int))。

结论:时间是O(n^2),内存是n*sizeof(int)+O(1),也就是一个额外的常数因子,这对我们来说无关紧要,从现在开始我们将忽略它)。

示例 2

让我们考虑一下 MBo 答案中的算法。

  • 时间复杂度:比示例 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),内存是3*n*sizeof(int)。当n 增长时,该算法要快得多,但使用的内存是示例 1 的三倍。在一些几乎没有可用内存的奇怪场景中(可能是嵌入式系统),这个3*n*sizeof(int) 可能太多了,你可能无法使用此算法(诚然,它可能永远不会成为真正的问题)。

示例 3

我们可以在示例 1 和示例 2 之间找到一个权衡吗?

一种方法是复制与示例 1 中相同类型的嵌套循环结构,但需要进行一些预处理以用更快的方式替换内部循环。为此,我们对初始数组进行排序。使用well-chosen algorithms 完成,时间复杂度为O(n*log(n)),内存使用量可以忽略不计。

一旦我们对数组进行了排序,我们就编写外循环(这是整个数组的常规循环),然后在外循环内,使用二分法搜索我们缺少的数字以达到我们的目标@ 987654355@。这种二分法的内存消耗为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)))。总计:n*sizeof(int) + O(log(n))

结论:时间是O(n*log(n)),内存是n*sizeof(int) + O(log(n))。内存几乎和示例 1 一样小。时间复杂度略高于示例 2。在示例 2 因内存不足而无法使用的场景中,在速度方面下一个最好的事情实际上是示例 3,即几乎与示例 2 一样快,并且如果非常慢的示例 1 运行,可能有足够的空间运行。

总体结论

这个答案只是为了表明“最佳”在算法中是依赖于上下文的。在这个特定示例中,不太可能选择实现示例 3。通常,如果 n 太小以至于人们会选择最简单的设计和最快的编码方式,您会看到示例 1,或者示例2 如果n 有点大,我们想要速度。但是,如果您查看我为排序算法链接的维基百科页面,您会发现它们都不是最擅长的。他们都有可以用更好的东西代替的场景。

【讨论】:

  • 在示例 3 中将 O(log(n)) 更改为 O(n*log(n))
猜你喜欢
  • 1970-01-01
  • 2015-04-16
  • 1970-01-01
  • 2023-04-02
  • 1970-01-01
  • 1970-01-01
  • 2016-07-02
  • 2021-11-03
  • 2020-11-30
相关资源
最近更新 更多