找到XOR的最低组合

时间:2012-01-23 12:19:39

标签: math optimization

请考虑以下代码:

#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数的解决方案。

如果有多个解决方案,请找到预先计算的存储数量最少的解决方案。

如果还有多种解决方案,那么选择哪个无关紧要。

其他一些信息:

  • 在真实程序中,函数的XOR可以是随机的,因为它们依赖于用户输入。
  • 总有16个步骤完成。
  • 每步的XOR数可以是0到7 XOR。
  • 预先计算的值所需的存储空间无关紧要

我对如何写这个有点迷茫。

3 个答案:

答案 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的那个)。

要优化加权函数的值,请将SXOR中的两个值合并,并将它们添加到S,然后将S1。选择那两个赋予加权函数最低新值的值(同样,这可以通过强力来确定)。 S1现在还有一个值(这将是一个临时值,如我的解决方案中的i值)。要创建此值,需要一个XOR(因此,加权函数会计算S中的值的数量。)

继续此步骤,直到找不到要添加到S的任何新值来减少加权函数的值。结果集包含初始值加上您必须计算的所有临时值。您采取的步骤将告诉您如何计算直接值。

这是一种贪婪的算法。它不一定能找到XOR的最小数量,但可以让您轻松获得良好的解决方案。可能算法实际上总是找到最佳解决方案,但这必须得到证实。如果您想绝对确定,可以从初始S值开始,完全遍历所有可能减少加权函数值的步骤。这将是一个树遍历,树将是有限的 - 因为值不能低于0 - 所以它肯定是可以解决的。

答案 2 :(得分:1)

你在这里手动完成的实际上是一个名为common subexpression elimination(CSE)的经典编译器优化。

在手动或使用工具在源代码上执行CSE之前,请检查生成的程序集以查看您的编译器是否已经为您执行CSE。有可能 - 并注意编译器确实是CSE应该完成的地方,因为需要做出权衡:你做CSE越积极,你减少的计算量就越多,但您需要的存储空间(即寄存器或RAM)越多。过于积极地执行CSE实际上会损害性能,如果它导致您泄漏寄存器或增加内存带宽 - 编译器通常会知道如何执行这种权衡。