两个数字相加到第三个

时间:2012-11-02 16:08:55

标签: algorithm language-agnostic

给定一个整数数组[a1 a2 ... an],不一定是不同的,如果有不同的索引i,j,k,那么给出一个返回“是”的算法ai + aj = ak ,否则“不”。

有没有办法比蛮力更快地做到这一点,这需要O(n ^ 3)?

4 个答案:

答案 0 :(得分:2)

是的。

第一步:对数组进行排序

然后以智能方式浏览您的索引。聪明的方法可能是选择

  • a0 + a1
  • a0 + a2
  • a0 + a3
  • ...
  • a0 + a(n-1)
  • a1 + a(n-1)
  • a1 + a(n-2)

智能这里意味着连续两对测试指数不能相距太远。

对于第一个aO + a1,您会发现k是否a0 + a1 = ak O(logn)k'中进行了二分搜索。

对于以下内容,假设测试对接近前一个,这意味着如果ai + aj = ak' k',那么k必须接近k {1}}。您可以从k'开始线性搜索,直到ai + aj匹配,或者对O(1)对变得太大/太小。这在一般情况下花费n^2

由于您必须最多测试O(n^2)对,因此整个算法为{{1}}。

答案 1 :(得分:1)

  • 建立所有可能总和的列表ai + aj:O(n ^ 2)。
    该列表的大小为= n ^ 2

  • 然后将该列表与数组进行比较,以查看是否存在任何相似之处:

    • 首先对每个列表进行排序:O((n ^ 2)log(n ^ 2))+ O(nlogn)
    • 浏览它们以找到任何匹配项:O(n ^ 2)

总计:来自alestanis的每条评论的O((n ^ 2)log(n ^ 2))(= O((n ^ 2)log(n))

编辑:我忘记了明确的要求,但这不应该改变结果 首先,为了确保i!= j,在构建步骤1中所有总和的列表时,只需排除i == j 第二,确保i!= k和j!= k,用索引i,j标记每个和,并在排序前用索引k标记每个原始值。
然后在最后一步找到任何匹配项,检查标记的索引是否不同。

答案 2 :(得分:0)

以下python代码实现了类似于alestanis所述的方法,类似于Daniel Le提到的wikipedia article中给出的二次算法。对于大的均匀随机正整数,所述的O(n ^ 2)复杂度似乎成立。 (内部搜索循环,平均增加k,大约(n ^ 2)/ 4次。)首先出现在旧的AMD Athlon 5000处理器上运行的定时样本的输出,然后是代码。

0.002519    N 50    NN 2500     ops 607     NN/ops 4.1186   NN/t 992405.8   Matches 0
0.00752     N 100   NN 10000    ops 1902    NN/ops 5.2576   NN/t 1329794.2  Matches 0
0.035443    N 200   NN 40000    ops 10648   NN/ops 3.7566   NN/t 1128570.5  Matches 2
0.063056    N 400   NN 160000   ops 37403   NN/ops 4.2777   NN/t 2537427.4  Matches 33
0.176328    N 800   NN 640000   ops 163247  NN/ops 3.9204   NN/t 3629595.6  Matches 244
0.729919    N 1600  NN 2560000  ops 658122  NN/ops 3.8899   NN/t 3507238.7  Matches 2062
2.720713    N 3200  NN 10240000     ops 2535751     NN/ops 4.0383   NN/t 3763719.4  Matches 16178
11.07818    N 6400  NN 40960000     ops 10160769    NN/ops 4.0312   NN/t 3697358.2  Matches 128793
import random, bisect, time
V, W, N, Nlim = 500, 500000, 50, 6400
while N <= Nlim:
    t0 = time.time()
    U=sorted([random.randint(V,V+W) for i in range(N)])
    bigsum = U[-1]
    U.append(N*W)
    matches = ops = 0
    for i, ai in enumerate(U[:-2]):
        k = bisect.bisect_right(U, ai+U[i+1])
        for j, aj in enumerate(U[i+1:-1]):
            while ai+aj > U[k]:
                k += 1
                ops += 1
            if U[k] == ai+aj:
                #print 'Match', i, j, k, ai, aj, ai+aj, U[k]
                matches += 1
            if ai+aj > bigsum:
                break
    et = time.time() - t0
    print round(et,6), '\tN',N, '\tNN', N*N, '\tops', ops, '\tNN/ops',
    print round(float(N*N)/ops,4), '\tNN/t', round(N*N/et,1), '\tMatches', matches
    N *= 2

代码可能应该按照维基百科大纲重写;重写可能会清除一些笨拙的数组切片数和无关的完成检查。

答案 3 :(得分:0)

#include <cstdio>
#include <algorithm>
#include <vector>

using namespace std;

bool SUM3(vector<int> &v)
{
  sort(v.begin(), v.end());

  for (int i = 0; i < v.size(); ++i) {
    int j = 0, k = v.size() - 1;

    while (j < k) {
      int result = v[j] + v[k] - v[i];

      if (result < 0 || i == j) ++j;
      else if (result > 0) --k;
      else return true;
    }
  }

  return false;
}

int main()
{
  int a1[] = {25, 7, 9, 2, 4, 8, 10};
  vector<int> v1(a1, a1 + sizeof a1 / sizeof a1[0]);

  printf("%s\n", SUM3(v1) ? "true" : "false");

  int a2[] = {1, 2, 4};
  vector<int> v2(a2, a2 + sizeof a2 / sizeof a2[0]);

  printf("%s\n", SUM3(v2) ? "true" : "false");
  return 0;
}

该算法的复杂性为O(n ^ 2)。