请考虑以下代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main (int argc, char *argv[])
{
time_t seed;
time (&seed);
srand (seed);
int i, j, k, l;
// init random values s1 .. s8
int s[8];
for (l = 0; l < 8; l++) s[l] = rand ();
// zero result
int r[16];
for (j = 0; j < 16; j++) r[j] = 0;
// do 100 random xor functions
for (i = 0; i < 100; i++)
{
// generates random function to show why CSE must be computed in runtime
int steps[16];
for (j = 0; j < 16; j++) steps[j] = rand ();
// _here_ is optimization possible
// run function MANY times to show that optimization makes sense
for (l = 0; l < 1000000; l++)
{
for (j = 0; j < 16; j++)
{
int tmp = 0;
for (k = 0; k < 8; k++) tmp ^= ((steps[j] >> k) & 1) ? s[k] : 0;
r[j] += tmp;
}
}
for (j = 0; j < 16; j++) printf ("%08x\n", r[j]);
puts ("");
}
return 0;
}
在代码中,以下展开的函数在循环中执行多次:
r[ 0] += s01 ^ s03;
r[ 1] += s02 ^ s04;
r[ 2] += s03 ^ s05;
r[ 3] += s02;
r[ 4] += s03;
r[ 5] += s04 ^ s06;
r[ 6] += s03;
r[ 7] += s04;
r[ 8] += s02 ^ s04 ^ s05 ^ s07;
r[ 9] += s03 ^ s04 ^ s05 ^ s07;
r[10] += s04 ^ s05 ^ s06;
r[11] += s05 ^ s06 ^ s08;
r[12] += s03 ^ s06;
r[13] += s06;
r[14] += s02 ^ s03 ^ s04 ^ s05 ^ s06 ^ s07;
r[15] += s03 ^ s04 ^ s05 ^ s06;
总计 23 XOR 。
但实施很糟糕。优化版本是:
int s04___s05 = s04 ^ s05;
int s03___s06 = s03 ^ s06;
int s04___s05___s07 = s04___s05 ^ s07;
int s03___s04___s05___s06 = s03___s06 ^ s04___s05;
r[ 0] += s01 ^ s03;
r[ 1] += s02 ^ s04;
r[ 2] += s03 ^ s05;
r[ 3] += s02;
r[ 4] += s03;
r[ 5] += s04 ^ s06;
r[ 6] += s03;
r[ 7] += s04;
r[ 8] += s02 ^ s04___s05___s07;
r[ 9] += s03 ^ s04___s05___s07;
r[10] += s04___s05 ^ s06;
r[11] += s05 ^ s06 ^ s08;
r[12] += s03___s06;
r[13] += s06;
r[14] += s02 ^ s03___s04___s05___s06 ^ s07;
r[15] += s03___s04___s05___s06;
总计 15 XOR 。
我正在搜索自动执行此步骤的算法,并找到使用最低XOR数的解决方案。
如果有多个解决方案,请找到预先计算的存储数量最少的解决方案。
如果还有多种解决方案,那么选择哪个无关紧要。
其他一些信息:
我对如何写这个有点迷茫。
答案 0 :(得分:2)
我们要计算r[i]
。它等于它们之间最多8个输入异或
现在,想一想:s8 ^ s6 ^ s5 ^ s4 ^ s3 ^ s2 ^ s1,就像一个数字10111111.
如果我们在XORing中使用相应的s
,则为1,否则为0
我们可以预先计算所有可能的2 ^ 8变体:
t[0] = 0 (00000000, nothing)
t[1] = s1 (00000001)
t[2] = s2 (00000010)
t[3] = s2 ^ s1 (00000011)
t[4] = s3 (00000100)
t[5] = s3 ^ s1 (00000101)
...
t[255] = s8 ^ s7 ^ s6 ^ s5 ^ s4 ^ s3 ^ s2 ^ s1 (11111111)
然后在循环中,如果你想要例如计算:
r[0] = s1 ^ s3
在我们的表示中,s1 ^ s3是00000101 = 5,它为我们提供了预先计算的查找表的索引:
r[0] = t[5]
在没有任何XOR循环的情况下解决了您的问题。
答案 1 :(得分:2)
让我们首先搜索一个抽象的问题定义:你有一个长度为8位的位向量类型,它代表了你的8个输入信号的组合。对于每个信号,您有一个位向量值,如10000000
(第一个信号)或00100000
(第三个信号)。给出了这些值。您想要生成以下值(我遗漏了一些简单的值):
r[0] = 10100000
r[1] = 01010000
r[2] = 00101000
r[5] = 00010100
r[8] = 01011010
r[9] = 00111010
r[10] = 00011100
r[11] = 00001101
r[12] = 00100100
r[14] = 01111110
r[15] = 00111100
我们现在想要搜索最少的组合(执行XOR
)来生成这些值。这是一个优化问题。我不会在这里完成最低XOR
次执行的证据,但这就是我得到的:
int i1 = s02 ^ s04; // 01010000
int i2 = s03 ^ s05; // 00101000
int i3 = s04 ^ s06; // 00010100
int i4 = s05 ^ s07; // 00001010
int i5 = s03 ^ s06; // 00100100
int i6 = i1 ^ i4; // 01011010
int i7 = i2 ^ i3; // 00111100
int i8 = s06 ^ s07; // 00000110
r[0] = s01 ^ s03;
r[1] = i1;
r[2] = i2;
r[5] = i3;
r[8] = i6;
r[9] = i7 ^ i8;
r[10] = i3 ^ s05;
r[11] = i4 ^ i8 ^ s08;
r[12] = i5;
r[14] = i6 ^ i5;
r[15] = i7;
14 XOR
s。
制定一般算法:首先使用Set S={10000000, 01000000, ... , 00000001}
。您需要一个加权函数来告诉您集合的值。将其定义为:XOR
中的值计算所有目标值所需的S
个数,而不存储其他临时值加上 S
中的值数量减去 8(初始值)。加权函数的第一部分可以用强力实现(找到目标值的所有可能组合,最多使用S
中的每个值一次,选择执行次数最少XOR
的那个)。
要优化加权函数的值,请将S
和XOR
中的两个值合并,并将它们添加到S
,然后将S1
。选择那两个赋予加权函数最低新值的值(同样,这可以通过强力来确定)。 S1现在还有一个值(这将是一个临时值,如我的解决方案中的i
值)。要创建此值,需要一个XOR
(因此,加权函数会计算S
中的值的数量。)
继续此步骤,直到找不到要添加到S
的任何新值来减少加权函数的值。结果集包含初始值加上您必须计算的所有临时值。您采取的步骤将告诉您如何计算直接值。
这是一种贪婪的算法。它不一定能找到XOR
的最小数量,但可以让您轻松获得良好的解决方案。可能算法实际上总是找到最佳解决方案,但这必须得到证实。如果您想绝对确定,可以从初始S
值开始,完全遍历所有可能减少加权函数值的步骤。这将是一个树遍历,树将是有限的 - 因为值不能低于0 - 所以它肯定是可以解决的。
答案 2 :(得分:1)
你在这里手动完成的实际上是一个名为common subexpression elimination(CSE)的经典编译器优化。
在手动或使用工具在源代码上执行CSE之前,请检查生成的程序集以查看您的编译器是否已经为您执行CSE。有可能 - 并注意编译器确实是CSE应该完成的地方,因为需要做出权衡:你做CSE越积极,你减少的计算量就越多,但您需要的存储空间(即寄存器或RAM)越多。过于积极地执行CSE实际上会损害性能,如果它导致您泄漏寄存器或增加内存带宽 - 编译器通常会知道如何执行这种权衡。