优化数组的求和(子集问题)

时间:2011-05-15 21:43:18

标签: c algorithm optimization subset

在我程序最热门的部分(90%的时间根据gprof),我需要将一个数组A加到另一个B中。两个数组都是2 ^ n(n是18..24)大小并保持一个整数(为简单起见,实际存储的元素是mpz_t或小int数组)。求和规则:对于0..2 ^ n-1中的每个i,设置B[i] = sum (A[j]),其中j是位向量,j & ~ i == 0(换句话说,第k位)如果j的第k位不是1,则任何i都不能设置为1。

我当前的代码(这是最内层循环的主体)在2 ^(1.5 * n)和的时间内执行此操作,因为我将迭代每个i on(平均)2 ^(n / 2)个元素A。

  int A[1<<n]; // have some data
  int B[1<<n]; // empty
  for (int i = 0; i < (1<<n); i++ ) {
    /* Iterate over subsets */
    for (int j = i; ; j=(j-1) & i ) {
      B[i] += A[j];  /* it is an `sum`, actually it can be a mpz_add here */
      if(j==0) break;
    }
  }

我的提到,几乎所有的金额都是从价值中重新计算出来的,这是前面总结的。我建议,可以有代码,在n* 2^n总和时执行相同的任务。

我的第一个想法是B[i] = B[i_without_the_most_significant_bit] + A[j_new];其中j_new只是j在'1'状态下具有来自i的最重要位。这减少了我的时间,但这还不够(实际问题大小仍然是几小时和几天):

  int A[1<<n];
  int B[1<<n];
  B[0] = A[0]; // the i==0 will not work with my idea and clz()
  for (int i = 1; i < (1<<n); i++ ) {
    int msb_of_i = 1<< ((sizeof(int)*8)-__builtin_clz(i)-1);
    int i_wo_msb = i & ~ msb;
    B[i] = B[i_wo_msb];
    /* Iterate over subsets */
    for (int j_new = i; ; j_new=(j_new-1) & i ) {
      B[i] += A[j_new];  
      if(j_new==msb) break; // stop, when we will try to unset msb
    }
  }

你能建议更好的算法吗?

附加图像,对于n = 4,每个i的i和j的总和:

i  j`s summed
0  0
1  0 1
2  0 2
3  0 1 2 3
4  0 4
5  0 1 4 5
6  0 2 4 6
7  0 1 2 3 4 5 6 7
8  0                8
9  0 1              8 9
a  0 2              8 a
b  0 1 2 3          8 9 a b
c  0 4              8 c
d  0 1 4 5          8 9 c d
e  0 2 4 6          8 a c e
f  0 1 2 3 4 5 6 7  8 9 a b c d e f

注意数字的相似性

PS msb魔法来自这里:Unset the most significant bit in a word (int32) [C]

1 个答案:

答案 0 :(得分:4)

划分和征服任何人?现在不到位。

void sums(int *a, int n, int *b) {
  if (n <= 0) {
    *b = *a;
    return;
  }
  int m = 1 << (n - 1);
  sums(a, n - 1, b);
  sums(a + m, n - 1, b + m);
  for (int i = 0; i < m; i++) {
    b[m + i] += b[i];
  }
}