我目前正在针对某些物理问题执行傅里叶变换,我的算法的一个巨大瓶颈来自对标量积模2的评估。
对于给定的整数N,我必须用二进制表示所有数字,最多为2 ^ N-1。
对于这些数字的每个,表示为二进制向量(例如15 = 2 ^ 3 + 2 ^ 2 + 2 + 2 ^ 0 =(1,1,1,1,0, ...,0))我必须用二进制形式模2中的0到2 ^ N-1的所有数字来评估它的标量积。
(例如,标量乘积1.15 =(1,0,0,...,0)。(1,1,1,1,0,...,0)= 1 * 1 + 1 * 0 + ... = 1 mod 2)
请注意,在缩减模2期间,组件以二进制形式保存。
(1,1)。(1,1)= 1 * 1 + 1 * 1而不是1 * 1 + 2 * 2
这基本上是我必须执行的2 ^(2N)个标量积,并减少模2。
我很难超过N = 18。
我想知道是否可以使用一些聪明的数学技巧来大大减少花时间。
我在考虑某种递归(即在文件中保存N的结果并推断N + 1的结果)但我不确定这会有所帮助。实际上,通过这种递归,知道N的结果,我可以切割对应于N部分的N + 1的向量加上一个额外的数字,但是然后在每个标量积,而不是评估标量积,我将不得不告诉我的计算机去读一个大文件(因为我可能无法将其全部保存在动态内存中),这可能非常耗时,可能超过我必须为每个产品执行的~20次乘法
是否有任何已知的优化数理论算法可以非常快速地评估这样的标量积模2?是否有任何我不知道可以利用的规则或想法?
抱歉可怕的格式化,我无法让LateX在这里工作。
答案 0 :(得分:4)
相应位的乘积之和(模2)将等于两个数的AND中的1位数,模2。
由于您可以轻松获得数字的二进制表示,因此可能没有必要为它们实际创建位数组,而只需使用编程语言中的整数数据类型,该类型允许至少32位。许多语言都提供位运算符,例如AND(&
)和XOR(^
)。
使用variable-precision SWAR algorithm可以对数字中的1位进行计数。
以下是Python中的程序,它为2个数字计算此产品模2:
def numberOfSetBits(i):
i = i - ((i >> 1) & 0x55555555);
i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
def product(a, b):
return numberOfSetBits(a & b) % 2
不是用 numberOfSetBits 对这些位进行计数,而是可以将这些位与XOR一起折叠,首先将16个最高有效位与16个最低有效位进行折叠,然后得到8个最重要的位。 8个最低有效位,直到你剩下一位。再次在Python中:
def bitParity(i):
i = (i >> 16) ^ i
i = (i >> 8) ^ i
i = (i >> 4) ^ i
i = (i >> 2) ^ i
i = (i >> 1) ^ i
return i % 2
def product(a, b):
return bitParity(a & b)
答案 1 :(得分:1)
如果您更改评估这些对的顺序(大小为2 n x 2 n 的矩阵),那么您可以有效地找出哪些产品-mod -2评估的每一行都有变化。
使用Gray code,您可以按特殊顺序迭代0 ... 2 n -1中的每个值,其中每次只有1位外循环值发生变化。您可以为0 ... 2 n -1中的每个值存储1位,表示前一行的product-mod-2值,然后根据更改位是否有更改它任何效果,只有当另一个(内部循环)数字中的相应位为1时才会产生(如果它是0,那么无论另一个位的值是什么,二进制AND都将为0)。
在C:
int N = 5;
int max = (1 << N) - 1;
unsigned char* prev = calloc((1 << N) / 8, 1);
// for the first row all the products will be zero, so start at row 1
for(int a = 1; a <= max; a++)
{
int grey = a ^ (a >> 1); // compute the grey code
int prev_grey = (a - 1) ^ ((a - 1) >> 1);
int changed_bit = grey ^ prev_grey;
for(int b = 0; b <= max; b++)
{
// the product will be changed only if b has a 1 at the same place
// (otherwise it will be 0 regardless)
if(b & changed_bit)
{
prev[b >> 3] ^= (1 << (b & 7));
}
int mod = (prev[b >> 3] & (1 << (b & 7))) != 0;
printf("mod value of %d and %d is %d\n", grey, b, mod);
}
}
内循环可以进一步优化,因为你可以很容易地找出b在变化位的位置中具有非零值的值:例如,如果它位于位置10那么将会有运行1024连续0然后1等等所以你知道你有1024个值,其中product-mod-2与上一行相同等。我不清楚这是否有助于你虽然因为我不知道你在用这些产品做什么。
内部循环也可以展开(例如32或64次),这样您每次都不会读取和写入prev
数组,而是处理32或64位的块。时间。