我能想到的最好的办法是在循环逻辑上使用快速输出。结合使用 themis 描述的非零方法的可能性,您可以通过检查不到 2% 的 N^2 问题来回答您的问题。
下面是一些代码,给出了 80% 到 99% 零之间的数字的时间。当数字在 88% 左右为零时,使用 themis 的方法会变得更好(尽管在下面的示例中没有编码)。
这不是一个高度理论的解决方案,但它是实用的。
好的,这是问题空间的一些“理论”:
基本上,X 的每一位(输出)是由 A 的位沿顶部(MSB 到 LSB 从左到右)和 B 的位沿边( MSB 到 LSB 从上到下)。由于如果对角线上的任何一个为 1,则 X 的位为 1,因此您可以在单元遍历时执行提前退出。
下面的代码做到了这一点,并表明即使对于大约 87% 为零的数字,您也只需检查大约 2% 的单元格。对于更密集(更多 1)的数字,该百分比下降得更多。
换句话说,我不会担心棘手的算法,只需进行一些有效的逻辑检查。我认为诀窍是将输出的位视为网格的对角线,而不是 A shift-OR 的位与 B 的位。最棘手的是这种情况是跟踪您可以查看的位在 A 和 B 以及如何正确索引位。
希望这是有道理的。如果我需要进一步解释这一点(或者如果您发现这种方法有任何问题),请告诉我。
注意:如果我们更了解您的问题空间,我们可以相应地优化算法。如果您的数字大多非零,那么这种方法比 themis 更好,因为他的结果是需要更多的计算和存储空间(sizeof(int) * NNZ)。
注意 2:这假设数据基本上是位,并且我使用 .NET 的 BitArray 来存储和访问数据。我认为这在翻译成其他语言时不会引起任何重大的麻烦。基本思想仍然适用。
using System;
using System.Collections;
namespace BigIntegerOr
{
class Program
{
private static Random r = new Random();
private static BitArray WeightedToZeroes(int size, double pctZero, out int nnz)
{
nnz = 0;
BitArray ba = new BitArray(size);
for (int i = 0; i < size; i++)
{
ba[i] = (r.NextDouble() < pctZero) ? false : true;
if (ba[i]) nnz++;
}
return ba;
}
static void Main(string[] args)
{
// make sure there are enough bytes to hold the 6000 bits
int size = (6000 + 7) / 8;
int bits = size * 8;
Console.WriteLine("PCT ZERO\tSECONDS\t\tPCT CELLS\tTOTAL CELLS\tNNZ APPROACH");
for (double pctZero = 0.8; pctZero < 1.0; pctZero += 0.01)
{
// fill the "BigInts"
int nnzA, nnzB;
BitArray a = WeightedToZeroes(bits, pctZero, out nnzA);
BitArray b = WeightedToZeroes(bits, pctZero, out nnzB);
// this is the answer "BigInt" that is at most twice the size minus 1
int xSize = bits * 2 - 1;
BitArray x = new BitArray(xSize);
int LSB, MSB;
LSB = MSB = bits - 1;
// stats
long cells = 0;
DateTime start = DateTime.Now;
for (int i = 0; i < xSize; i++)
{
// compare using the diagonals
for (int bit = LSB; bit < MSB; bit++)
{
cells++;
x[i] |= (b[MSB - bit] && a[bit]);
if (x[i]) break;
}
// update the window over the bits
if (LSB == 0)
{
MSB--;
}
else
{
LSB--;
}
//Console.Write(".");
}
// stats
TimeSpan elapsed = DateTime.Now.Subtract(start);
double pctCells = (cells * 100.0) / (bits * bits);
Console.WriteLine(pctZero.ToString("p") + "\t\t" +elapsed.TotalSeconds.ToString("00.000") + "\t\t" +
pctCells.ToString("00.00") + "\t\t" + cells.ToString("00000000") + "\t" + (nnzA * nnzB).ToString("00000000"));
}
Console.ReadLine();
}
}
}