两个n位数A和B的乘法可以理解为一个移位之和:
(A << i1) + (A << i2) + ...
其中i1,i2,...是B中设置为1的位数。
现在让我们用OR替换PLUS以获得我真正需要的新操作:
(A << i1) | (A << i2) | ...
此操作非常类似于常规乘法,其中存在许多更快的算法(例如Schönhage-Strassen)。 我在这里提出了类似的操作算法吗?
数字的大小是6000位。
修改 出于某种原因,我没有链接/按钮发表评论(任何想法为什么?)所以我将编辑我的问题。 我确实为上面定义的操作搜索比O(n ^ 2)算法更快的算法。 是的,我知道这不是普通的乘法。
答案 0 :(得分:1)
是否有类似的算法?我想可能不是。
有没有办法加快速度超过O(n ^ 2)?有可能。如果你认为数A是A(x)=Σa n x n 的类比,其中 n 是A的二进制数字,那么你的按位OR运算(我们称之为A⊕B)可以表示如下,其中“⇔”表示“模拟”
A⇔A(x)=Σa n x n
B⇔B(x)=Σb n x n
C =A⊕B⇔C(x)= f(A(x)B(x))= f(V(x))其中f(V(x))= f(Σv n < / sub> x n )=Σu(v n )x n 其中u(v n )= 0如果v n = 0,则u(v n )= 1。
基本上你做的相当于取两个多项式并将它们相乘,然后识别所有非零项。从位串的角度来看,这意味着将bitstring视为零或1的样本数组,convolving两个数组,并折叠非零的结果样本。有快速卷积算法是O(n log n),例如使用FFT,这里的“折叠”步骤是O(n)...但不知怎的,我想知道快速卷积的O(n log n)评估将某些东西(比如大整数的乘法)视为O(1),这样你实际上就不会得到更快的算法。无论是那个,还是增长订单的常数都是如此之大,以至于在获得任何速度优势之前,你必须拥有数千个位。 ORing非常简单。
编辑:似乎有一种称为“二进制卷积”的内容(例如,请参阅this book),这听起来非常相关,但我找不到与理论有任何良好关联它背后是否有快速算法。
编辑2:也许这个术语是“逻辑卷积”或“按位卷积”...这里是page from CPAN(bleah!)与Walsh和Hadamard一起谈论它转换是一种类似于傅里叶变换的按位...嗯,不,这似乎是XOR的模拟而不是OR。
答案 1 :(得分:0)
我认为,你问的是你给出的添加剂技术的名称 当你写“我在这里介绍的操作的类似算法吗?”......
你看过Peasant multiplication技术了吗? 如果您未在本示例中获得第3列,请阅读维基百科说明。
B X A
27 X 15 : 1
13 30 : 1
6 60 : 0
3 120 : 1
1 240 : 1
B is 27 == binary form 11011b
27x15 = 15 + 30 + 120 + 240
= 15<<0 + 15<<1 + 15<<3 + 15<<4
= 405
听起来很熟悉?
这是你的算法。
lsb
为1
,请将 A 添加到C 更新如果您试图获得跨越6000位的移位和OR运算的快速算法,
实际上可能有一个。我会考虑更多。
看起来像'模糊'一个数字而不是另一个。有趣的。
这是一个相当粗略的例子,
110000011 X 1010101 would look like
110000011
110000011
110000011
110000011
---------------
111111111111111
两个数字中1
的数量将决定模糊的数量,并设置所有位数。
不知道你想用它做什么......
Update2 这是具有两个6000位数的shift + OR运算的性质。
我还没有得到一个干净的算法。已经更新了其他任何想要重新检查或远离这里的人。此外,描述这种操作的需要可能会激发人们的兴趣: - )
答案 2 :(得分:0)
您可以执行此操作O(B中A *#1位中的#1位)。
a-bitnums = set(x : ((1<<x) & A) != 0)
b-bitnums = set(x : ((1<<x) & B) != 0)
c-set = 0
for a-bit in a-bitnums:
for b-bit in b-bitnums:
c-set |= 1 << (a-bit + b-bit)
如果A和B的数量稀少,这可能是值得的 1位存在。
答案 3 :(得分:0)
我能做到的最好就是在循环逻辑上使用快速输出。结合使用非零方法的可能性,你可以通过检查不到2%的N ^ 2问题来回答你的问题。
下面是一些代码,它给出了80%到99%零之间的数字时序。 当数字大约为88%零时,使用它们的方法切换到更好(虽然未在下面的示例中编码)。
这不是一个高度理论化的解决方案,但它很实用。
好的,这是问题空间的一些“理论”:
基本上,X(输出)的每个位是通过沿顶部(从左到右的MSB到LSB)和沿着B的位构成的网格对角线上的位的OR求和。一侧(从上到下的MSB到LSB)。由于如果对角线上的X为1,则X为1,则可以在细胞遍历上提前执行。
下面的代码执行此操作并显示即使对于大约87%零的数字,您也只需要检查~2%的单元格。对于更密集(更多1)的数字,该百分比下降得更多。
换句话说,我不会担心棘手的算法,只是做一些有效的逻辑检查。我认为诀窍是将输出的位视为网格的对角线而不是A位移位的位与B的位。最棘手的是这种情况是跟踪你可以看到的位at A和B以及如何正确索引位。
希望这是有道理的。如果我需要进一步解释(或者如果您发现此方法有任何问题),请告诉我。
注意:如果我们更了解您的问题空间,我们可以相应地优化算法。如果你的数字大多数非零,那么这种方法比他们更好,因为他的结果是需要更多的计算和存储空间(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();
}
}
}
答案 4 :(得分:0)
只需使用任何FFT多项式乘法算法,并将所有大于或等于1的结果系数转换为1。
示例:
10011 * 10001
[1 x^4 + 0 x^3 + 0 x^2 + 1 x^1 + 1 x^0] * [1 x^4 + 0 x^3 + 0 x^2 + 0 x^1 + 1 x^0]
== [1 x^8 + 0 x^7 + 0 x^6 + 1 x^5 + 2 x^4 + 0 x^3 + 0 x^2 + 1 x^1 + 1 x^0]
-> [1 x^8 + 0 x^7 + 0 x^6 + 1 x^5 + 1 x^4 + 0 x^3 + 0 x^2 + 1 x^1 + 1 x^0]
-> 100110011
有关算法的示例,请检查:
http://www.cs.pitt.edu/~kirk/cs1501/animations/FFT.html
BTW,它具有线性复杂性,即O(n log(n))另见:
http://everything2.com/title/Multiplication%2520using%2520the%2520Fast%2520Fourier%2520Transform