Optimize a summing of array (subset problem)
In the hottest part of my program (90% of time according to gprof), I need to sum one array A into another B. Both arrays are 2^n (n is 18..24) sized and holds an integer (for simplicity, actually the element stored is mpz_t or small int array). The rule of summing: for each i in 0..2^n-1, set B[i] = sum (A[j])
, where j
is bit vector, and j & ~ i == 0
(in other words, k-th bit of any j
can't be set to 1 if k-th bit of i
is not 1).
My current code (this is a body of innermost loop) does this in the time of 2^(1.5 * n) sums, because I will iterate for each i on (in average) 2^(n/2) elements of 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;
}
}
My I mentioned, that almost any sum is recomputed from the values, summed earlier. I suggest, there can be code, doing the same task in the time of n* 2^n
sums.
My first idea is that B[i] = B[i_without_the_most_significant_bit] + A[j_new]
; where j_new is only j's having the most_significant bit from i in '1' state. This halves my time, but this is not enough (still hours and days on real problem size):
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
}
}
Can you suggest better algorithm?
Additional image, list of i and j summed for each i for n=4:
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
Note the similarity of figures
PS the msb magi开发者_如何学Pythonc is from here: Unset the most significant bit in a word (int32) [C]
Divide and conquer anyone? Now not in-place.
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];
}
}
精彩评论