私が思いついた最善の方法は、ループ ロジックで高速アウトを使用することです。themis で説明されている非ゼロ アプローチを使用する可能性と組み合わせると、N^2 問題の 2% 未満を検査することで質問に答えることができます。
以下は、80% から 99% の間の数値のタイミングをゼロにするコードです。数値が約 88% ゼロになると、themis のアプローチを使用すると、より良い結果が得られます (ただし、以下のサンプルではコード化されていません)。
これは高度に理論的な解決策ではありませんが、実用的です。
OK、ここに問題空間の「理論」があります。
基本的に、X (出力) の各ビットは、A のビットを上部 (MSB から LSB へ、左から右へ) に沿って配置し、B のビットを側面 (上から下にMSBからLSBへ)。対角線のいずれかが 1 の場合、X のビットは 1 であるため、セル トラバーサルでアーリー アウトを実行できます。
以下のコードはこれを行っており、~87% ゼロの数値であっても、~2% のセルをチェックするだけでよいことを示しています。密度の高い (1 の数が多い) 数の場合、その割合はさらに低下します。
言い換えれば、トリッキーなアルゴリズムを気にせず、効率的なロジック チェックを行うだけです。秘訣は、出力のビットをグリッドの対角線として見ることだと思います。A のビットと B のビットのシフト OR を比較するのではありません。 A と B の at と、ビットに適切にインデックスを付ける方法。
うまくいけば、これは理にかなっています。これについてもう少し説明する必要がある場合はお知らせください (または、このアプローチに問題がある場合)。
注: 問題空間をもう少しよく知っていれば、それに応じてアルゴリズムを最適化できます。数値がほとんどゼロでない場合、このアプローチはより多くの計算と必要なストレージ スペース (sizeof(int) * NNZ) になるため、themis よりも優れています。
注 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();
}
}
}