私のプログラムの最もホットな部分(gprofによると90%の時間)では、1つの配列Aを別のBに合計する必要があります。両方の配列は2 ^ n(nは18..24)サイズで、整数を保持します(簡単にするため) 、実際に格納される要素はmpz_tまたはsmall int配列です)。合計のルール:0..2 ^ n-1の各iについて、を設定しますB[i] = sum (A[j])
。ここで、j
はビットベクトルであり、j & ~ i == 0
(つまり、kj
番目のビットがi
1)ではありません。
私の現在のコード(これは最も内側のループの本体です)は、2 ^(1.5 * n)の合計の時間にこれを行います。これは、Aの(平均)2 ^(n / 2)要素で各iを反復するためです。
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は、「1」状態のiからの最上位ビットを持つjのみです。これは私の時間を半分にしますが、これでは十分ではありません(実際の問題のサイズではまだ数時間と数日です):
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の魔法はここからです:単語の最上位ビットの設定を解除します(int32)[C]