两个子集的相加乘以

时间:2017-02-24 17:18:53

标签: c++ c algorithm math

我有一个元素{7,2,1}的数组,想法是做7 * 2 + 7 * 1 + 2 * 1,这基本上是这个算法:

a

其中n是我有数字的数组,<br> <br> <h1> table1 </h1><br> <br> <table id="example" class="display" width="100%" cellspacing="0"> <thead> <tr> <th>First name</th> <th>Place</th> <th>Order</th> </tr> </thead> </table> <br> <br> <h1> table 2 </h1><br> <br> <table id="example2" class="display" width="100%" cellspacing="0"> <thead> <tr> <th>First name</th> <th>Place</th> <th>checkbox</th> </tr> </thead> </table> 是元素的数量,我需要一个更有效的算法来做这个,我不知道怎么做,有人可以帮我一把吗?

谢谢!

4 个答案:

答案 0 :(得分:9)

在一般情况下你可以做得更好。是时候做一些数学了。让我们来看看3元素版本,我们有:

ab + ac + bc
= 1/2 * (2ab + 2ac + 2bc)
= 1/2 * (2ab + 2ac + 2bc + a^2 + b^2 + c^2 - (a^2 + b^2 + c^2))
= 1/2 * ((a+b+c)^2 - (a^2 + b^2 + c^2))

那是:

int sum = 0;
int sum_sq = 0;
for (int i : arr) {
    sum += i;
    sum_sq += i*i;
}
int result = (sum*sum - sum_sq) / 2;

这是O(n)次乘法,而不是O(n^2)。在某些时候,这肯定比天真的实施更好。对于3个元素来说,它是否更好是我没有时间的。

答案 1 :(得分:6)

@ chux的建议主要是重新分配操作:

a i * a i + 1 + a i * a i + 2 + ... + a i * a n

- &GT;

a i *(a i + 1 + ... + a n

结合避免不必要的重新计算(a i + 1 + ... + a n )项的部分和,通过利用每个不同于接下来是输入数组的一个元素的值。

这是一个带O(1)开销的一次通过实现:

int psum(size_t n, int array[n]) {
    int result = 0;
    int rsum = array[n - 1];

    for (int i = n - 2; i >= 0; i--) {
        result += array[i] * rsum;
        rsum += array[i];
    }

    return result;
}

索引i右侧的所有元素之和在变量rsum中从迭代到迭代保持不变。没有必要在数组中跟踪它的各种值,因为我们只需要循环的一次迭代的每个值。

这与输入数组中的元素数量呈线性关系。您会看到操作的数量和类型与@ Barry的答案非常相似,但是不需要类似于他的最后一步,这样可以节省一些操作。

正如@Barry在评论中观察到的那样,迭代也可以在另一个方向上运行,同时跟踪右手部分的左手部分和。这将与@ chux的描述略有不同,但它依赖于完全相同的原则。

答案 2 :(得分:4)

进行1次传球,从[a]的末尾走到前面,并在“右侧”形成所有元素的总和。

第二遍,多个a[i] * sum[i]

O(n)中。

long sum0(int a[], int n) {
  long sum = 0;
  for (int i = 0; i < n - 1; ++i)
    for (int k = i + 1; k < n; ++k)
      sum += a[i] * a[k];
  return sum;
}

long sum1(int a[], int n) {
  int long sums[n];
  sums[n - 1] = 0;
  for (int i = n - 2; i >= 0; i--) {
    sums[i] = a[i+1] + sums[i + 1];
  }

  long sum = 0;
  for (int i = 0; i < n - 1; ++i)
    sum += a[i] * sums[i];
  return sum;
}

void test(int a[], int n) {
  long s0 = sum0(a, n);
  long s1 = sum1(a, n);
  if (s0 != s1) printf("%9ld %9ld\n", s0, s1);
}

void tests(int k) {
  while (k--) {
    int n = rand() % 10 + 2;
    int a[n + 1];
    for (int m = 0; m < n; m++)
      a[m] = rand() % 256;
    test(a, n);
  }
}

int main() {
  int a[3] = { 7, 2, 1 };
  printf("%d\n", sum1(a, 3));
  tests(1000000);
  puts("Done");
}

事实证明,由于正在运行的sums[]只需要1个位置,因此不需要sums数组。这有效地使这个答案与其他答案相似

long sum1(int a[], int n) {
  int long sums = 0;
  long sum = 0;
  for (int i = n - 2; i >= 0; i--) {
    sums = a[i+1] + sums;
    sum += a[i] * sums;
  }
  return sum;
}

答案 3 :(得分:4)

我们有(a + b + c + ...) 2 =(a 2 + b 2 + c 2 + ...)+ 2(ab + bc + ca + ...)

你想要总和S = ab + bc + ca + ...,它有O(n 2 )对(使用2个嵌套循环)

你可以做2个独立的循环,一个在O中计算P = a 2 + b 2 + c 2 + ... n)时间,另一个在O(n)时间内计算Q =(a + b + c + ...) 2 。然后取S =(Q - P)/ 2。