¿Por qué contar todos los bits?
en VB hago esto:
<Runtime.CompilerServices.Extension()> _
Function BitLength(ByVal n As BigInteger) As Integer
Dim Data() As Byte = n.ToByteArray
Dim result As Integer = (Data.Length - 1) * 8
Dim Msb As Byte = Data(Data.Length - 1)
While Msb
result += 1
Msb >>= 1
End While
Return result
End Function
En C# sería:
public static int BitLength(this BigInteger n)
{
byte[] Data = n.ToByteArray();
int result = (Data.Length - 1) * 8;
byte Msb = Data[Data.Length - 1];
while (Msb != 0) {
result += 1;
Msb >>= 1;
}
return result;
}
Finalmente ...
static BigInteger Karatsuba(BigInteger x, BigInteger y)
{
int n = (int)Math.Max(x.BitLength(), y.BitLength());
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));
}
Al llamar al método de extensión puede ralentizar las cosas por lo quizás esto sería más rápido:
int n = (int)Math.Max(BitLength(x), BitLength(y));
FYI: con el método de longitud de bits también se puede calcular una buena aproximación del registro mucho más rápido que el Método BigInteger.
bits = BitLength(a) - 1;
log_a = (double)i * log(2.0);
Por lo que el acceso a la matriz de UInt32 interna de la estructura BigInteger, aquí es un corte para eso.
importe el espacio de reflexión
Private Shared ArrM As MethodInfo
Private Shard Bits As FieldInfo
Shared Sub New()
ArrM = GetType(System.Numerics.BigInteger).GetMethod("ToUInt32Array", BindingFlags.NonPublic Or BindingFlags.Instance)
Bits = GetType(System.Numerics.BigInteger).GetMember("_bits", BindingFlags.NonPublic Or BindingFlags.Instance)(0)
End Sub
<Extension()> _
Public Function ToUInt32Array(ByVal Value As System.Numerics.BigInteger) As UInteger()
Dim Result() As UInteger = ArrM.Invoke(Value, Nothing)
If Result(Result.Length - 1) = 0 Then
ReDim Preserve Result(Result.Length - 2)
End If
Return Result
End Function
entonces se puede obtener la UInteger() subyacente del entero grande como
Dim Data() As UInteger = ToUInt32Array(Value)
Length = Data.Length
o alternativamente
Dim Data() As UInteger = Value.ToUInt32Array()
Tenga en cuenta que _bits FieldInfo puede ser utilizado para acceder directamente al campo subyacente UInteger() _bits de la estructura de BigInteger re. Esto es más rápido que invocar el método ToUInt32Array(). Sin embargo, cuando BigInteger B < = UInteger.MaxValue _bits no es nada. Sospecho que, como optimización, cuando un BigInteger se ajusta al tamaño de una palabra de 32 bits (tamaño de la máquina), MS devuelve la aritmética de la palabra máquina normal utilizando el tipo de datos nativo.
Tampoco he podido utilizar _bits.SetValue (B, Data()) como normalmente lo haría con la reflexión. Para solucionar este problema, utilizo el constructor BigInteger (bytes() b) que tiene una sobrecarga. En C# puede usar operaciones de puntero inseguras para convertir un UInteger() en Byte(). Como no hay operaciones de puntero en VB, utilizo Buffer.BlockCopy. Cuando se accede a los datos de esta manera, es importante tener en cuenta que si se establece el MSB del conjunto de bytes(), MS lo interpreta como un número negativo. Preferiría que hicieran un constructor con un campo de signo separado.La palabra matriz es añadir una adición 0 bytes para hacer desmarques el MSB
Además, al elevar al cuadrado se puede mejorar aún más
Function KaratsubaSquare(ByVal x As BigInteger)
Dim n As Integer = BitLength(x) 'Math.Max(BitLength(x), BitLength(y))
If (n <= KaraCutoff) Then Return x * x
n = ((n + 1) >> 1)
Dim b As BigInteger = x >> n
Dim a As BigInteger = x - (b << n)
Dim ac As BigInteger = KaratsubaSquare(a)
Dim bd As BigInteger = KaratsubaSquare(b)
Dim c As BigInteger = Karatsuba(a, b)
Return ac + (c << (n + 1)) + (bd << (2 * n))
End Function
Esto elimina 2 turnos, 2 adiciones y 3 sustracciones a cada recursión de su algoritmo de multiplicación
¿Podría darnos un contexto sobre lo que es Karatsuba? –
No estoy seguro de si esto ayudará, pero tal vez de alguna manera puede convertirlo en un BitArray para que pueda contar los bits. – AaronLS
@aaronls: Eso es mucho más rápido, gracias. –