【笔记】大数乘法之古典算法 (Java BigInteger源码)

BigInteger与uint[]

用uint[]来表示非负大数,其中数组开头是大数的最高32位,数组结尾是大数最低32位。其与BigInteger的转换方法

/// <summary>
/// <see cref="uint"/>数组转为非负大整数
/// </summary>
private static BigInteger ValueOf(uint[] value)
{
    var result = BigInteger.Zero;
    foreach (var num in value)
    {
        result <<= 32;
        result |= (num & 0xFFFF_FFFF);
    }

    return result;
}

/// <summary>
/// 非负大整数转为<see cref="uint"/>数组
/// </summary>
private static uint[] ToIntArray(BigInteger value)
{
    var byteCount = value.GetByteCount();
    var len = (int)Math.Ceiling(byteCount / 4d);
    var result = new uint[len];
    for (var i = len - 1; i >= 0; --i)
    {
        result[i] = (uint)(value & 0xFFFF_FFFF);
        value >>= 32;
    }

    return result;
}

测试

[TestMethod]
public void ConvertTest()
{
    var bytes = new byte[32];
    Random ran = new Random();
    for (var i = 0; i < 100; ++i)
    {
        ran.NextBytes(bytes);
        var value = BigInteger.Abs(new BigInteger(bytes));
        var test = ToIntArray(value);
        Assert.AreEqual(value, ValueOf(test));
    }
            
}

Unsigned乘法(Java BigInteger源码)

private static readonly long LONG_MASK = 0xFFFF_FFFFL;

/// <summary>
/// 非负大数乘法,数组第一个<see cref="uint"/>存放最高32位,最后一个<see cref="uint"/>存放最低32位。
/// </summary>
public static uint[] MultiplyNonegative(uint[] left, uint[] right)
{
    var xstart = left.Length - 1;
    var ystart = right.Length - 1;
    var result = new uint[left.Length + right.Length];

    var carry = 0L;
    for (int j = ystart, k = ystart + 1 + xstart; j >= 0; j--, k--)
    {
        var product = (right[j] & LONG_MASK) *
                        (left[xstart] & LONG_MASK) + carry;
        result[k] = (uint)product;
        carry = (long)((ulong)product >> 32);
    }
    result[xstart] = (uint)carry;

    for (var i = xstart - 1; i >= 0; i--)
    {
        carry = 0;
        for (int j = ystart, k = ystart + 1 + i; j >= 0; j--, k--)
        {
            var product = (right[j] & LONG_MASK) *
                            (left[i] & LONG_MASK) +
                            (result[k] & LONG_MASK) + carry;
            result[k] = (uint)product;
            carry = (long)((ulong)product >> 32);
        }
        result[i] = (uint)carry;
    }

    return result;
}

测试

[TestMethod]
public void MultiplyNonegativeTest()
{
    var bytes = new byte[32];
    Random ran = new Random();
    for (var i = 0; i < 100; ++i)
    {
        ran.NextBytes(bytes);
        var left = BigInteger.Abs(new BigInteger(bytes));
        ran.NextBytes(bytes);
        var right = BigInteger.Abs(new BigInteger(bytes));
        var test = MultiplyNonegative(ToIntArray(left), ToIntArray(right));
        var expected = left * right;
        Assert.AreEqual(expected, ValueOf(test));
    }
}

BigInteger与(uint[], bool)

上面的减法有太多限制,加法也不能计算负数,接下来推广到通用加减法。

用(uint[], bool)来表示有符号大数,其中uint[]是大数的绝对值,bool为false时是负数。

其与BigInteger之间转换方法

/// <summary>
/// (<see cref="uint"/>[], <see cref="bool"/>) to <see cref="BigInteger"/>
/// </summary>
private BigInteger ValueOf((uint[], bool) value)
{
    var result = BigInteger.Zero;
    foreach (var num in value.Item1)
    {
        result <<= 32;
        result |= (num & 0xFFFF_FFFF);
    }

    return value.Item2 ? result : -result;
}


/// <summary>
/// <see cref="BigInteger"/> to (<see cref="uint"/>[], <see cref="bool"/>)
/// </summary>
private (uint[], bool) ToTuple(BigInteger value)
{
    var positive = BigInteger.Abs(value);

    var byteCount = positive.GetByteCount();
    var len = (int)Math.Ceiling(byteCount / 4d);
    var result = new uint[len];
    for (var i = len - 1; i >= 0; --i)
    {
        result[i] = (uint)(positive & 0xFFFF_FFFF);
        positive >>= 32;
    }

    return (result, value >= 0);
}

测试

[TestMethod]
public void ConvertTest()
{
    var bytes = new byte[32];
    Random ran = new Random();
    for (var i = 0; i < 100; ++i)
    {
        ran.NextBytes(bytes);
        var value = new BigInteger(bytes);
        var test = ToTuple(value);
        Assert.AreEqual(value, ValueOf(test));
    }
}

Signed乘法

/// <summary>
/// 大数乘法,数组第一个<see cref="uint"/>存放最高32位,最后一个<see cref="uint"/>存放最低32位。
/// </summary>
public static (uint[], bool) Multiply((uint[], bool) left, (uint[], bool) right)
{
    if (IsZero(left))
        return left;
    if (IsZero(right))
        return right;
    if (IsAbsOne(left))
        return (right.Item1, right.Item2 == left.Item2);
    if (IsAbsOne(right))
        return (left.Item1, left.Item2 == right.Item2);

    return (MultiplyNonegative(left.Item1, right.Item1), left.Item2 == right.Item2);
}


private static bool IsZero((uint[], bool) value)
    => value.Item1.Length == 1 && value.Item1[0] == 0;

private static bool IsAbsOne((uint[], bool) value)
    => value.Item1.Length == 1 && value.Item1[0] == 1;

测试

[TestMethod]
public void MultiplyTest()
{
    var bytes = new byte[32];
    Random ran = new Random();
    for (var i = 0; i < 100; ++i)
    {
        ran.NextBytes(bytes);
        var left = new BigInteger(bytes);
        ran.NextBytes(bytes);
        var right = new BigInteger(bytes);
        var test = Multiply(ToTuple(left), ToTuple(right));
        var expected = left * right;
        Assert.AreEqual(expected, ValueOf(test));
    }
}

猜你喜欢

转载自blog.csdn.net/xueyan0096/article/details/83898636