So, I'm trying to improve some of the operations that .net 4's BigInteger
class provide since the operations appear to be quadratic. I've made a rough Karatsuba implementation but it's still slower than I'd expect.
The main problem seems to be that BigInteger provides no simple way to count the number of bits and, so, I have to use BigInteger.Log(..., 2). According to Visual Studio, about 80-90% of the time is spent calculating logarithms.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Numerics;
namespace Test
{
class Program
{
static BigInteger Karatsuba(BigInteger x, BigInteger y)
{
int n = (int)Math.Max(BigInteger.Log(x, 2), BigInteger.Log(y, 2));
if (n <= 10000) return x * y;
n = ((n+1) / 2);
BigInteger b = x >> n;
BigInteger a = x - (b << n);
BigInteger d = y >> n;
BigInteger c = y - (d << n);
BigInteger ac = Karatsuba(a, c);
BigInteger bd = Karatsuba(b, d);
BigInteger abcd = Karatsuba(a+b, c+d);
return ac + ((abcd - ac - bd) << n) + (bd << (2 * n));
}
static void Main(string[] args)
{
BigInteger x = BigInteger.One << 500000 - 1;
BigInteger y = BigInteger.One << 600000 + 1;
BigInteger z = 0, q;
Console.WriteLine("Working...");
DateTime t;
// Test standard multiplication
t = DateTime.Now;
z = x * y;
Console.WriteLine(DateTime.Now - t);
// Test Karatsuba multiplication
t = DateTime.Now;
q = Karatsuba(x, y);
Console.WriteLine(DateTime.Now - t);
// Check they're equal
Console.WriteLine(z == q);
Console.Read();
}
}
}
So, what can I do to speed it up?
Why count all of the bits?
In vb I do this:
In C# it would be:
Finally...
Calling the extension method may slow things down so perhaps this would be faster:
FYI: with the bit length method you can also calculate a good approximation of the log much faster than the BigInteger Method.
As far as accessing the internal UInt32 Array of the BigInteger structure, here is a hack for that.
import the reflection namespace
Then you can get the underlying UInteger() of the big integer as
or Alternately
Note that _bits fieldinfo can be used to directly access the underlying UInteger() _bits field of the BigInteger structure. This is faster than invoking the ToUInt32Array() method. However, when BigInteger B <= UInteger.MaxValue _bits is nothing. I suspect that as an optimization when a BigInteger fits the size of a 32 bit (machine size) word MS returns performs normal machine word arithmetic using the native data type.
I have also not been able to use the _bits.SetValue(B, Data()) as you normally would be able to using reflection. To work around this I use the BigInteger(bytes() b) constructor which has overhead. In c# you can use unsafe pointer operations to cast a UInteger() to Byte(). Since there are no pointer ops in VB, I use Buffer.BlockCopy. When access the data this way it is important to note that if the MSB of the bytes() array is set, MS interprets it as a Negative number. I would prefer they made a constructor with a separate sign field. The word array is to add an addition 0 byte to make uncheck the MSB
Also, when squaring you can improve even further
This eliminates 2 shifts, 2 additions and 3 subtractions from each recursion of your multiplication algorithm.