【笔记】Java BigInteger - Addition/Subtraction

BigInteger to/from uint[]

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

/// <summary>
/// <see cref="uint"/>数组转为非负大整数
/// </summary>
private static BigInteger ValueOf(uint[] value)
{
    var result = BigInteger.Zero;
    var array = value.SkipWhile(num => num == 0u).ToArray();
    foreach (var num in array)
    {
        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;
}

Unsigned Add

private static readonly long LONG_MASK = 0xFFFF_FFFFL;

/// <summary>
/// 大数加法,数组第一个int存放最高32位,最后一个int存放最低32位。
/// </summary>
public static uint[] AddNonegative(uint[] left, uint[] right)
{
    // If left is shorter, swap the two arrays
    if (left.Length < right.Length)
    {
        var tmp = left;
        left = right;
        right = tmp;
    }

    var xIndex = left.Length;
    var yIndex = right.Length;
    var result = new uint[xIndex];
    var sum = 0L;
    if (yIndex == 1)
    {
        sum = (left[--xIndex] & LONG_MASK) + (right[0] & LONG_MASK);
        result[xIndex] = (uint)sum;
    }
    else
    {
        // Add common parts of both numbers
        while (yIndex > 0)
        {
            sum = (left[--xIndex] & LONG_MASK) +
                    (right[--yIndex] & LONG_MASK) + (long)((ulong)sum >> 32);
            result[xIndex] = (uint)sum;
        }
    }

    // Copy remainder of longer number while carry propagation is required
    var carry = ((ulong)sum >> 32) != 0;
    while (xIndex > 0 && carry)
        carry = ((result[--xIndex] = left[xIndex] + 1) == 0);

    // Copy remainder of longer number
    while (xIndex > 0)
        result[--xIndex] = left[xIndex];

    // Grow result if necessary
    if (carry)
    {
        Array.Resize(ref result, result.Length + 1);
        for (var i = result.Length - 1; i > 0; --i)
            result[i] = result[i - 1];

        result[0] = 0x01;
    }

    return result;
}

Unsigned Subtract

/// <summary>
/// 大数减法,<paramref name="left"/> 大于<paramref name="right"/>。数组第一个int存放最高32位,最后一个int存放最低32位。
/// </summary>
public static uint[] SubtractNonegative(uint[] left, uint[] right)
{
    var bigIndex = left.Length;
    var result = new uint[bigIndex];
    var littleIndex = right.Length;
    var difference = 0L;

    // Subtract common parts of both numbers
    while (littleIndex > 0)
    {
        difference = (left[--bigIndex] & LONG_MASK) -
                        (right[--littleIndex] & LONG_MASK) +
                        (difference >> 32);
        result[bigIndex] = (uint)difference;
    }

    // Subtract remainder of longer number while borrow propagates
    var borrow = (difference >> 32) != 0;
    while (bigIndex > 0 && borrow)
        borrow = ((result[--bigIndex] = left[bigIndex] - 1) == uint.MaxValue);

    // Copy remainder of longer number
    while (bigIndex > 0)
        result[--bigIndex] = left[bigIndex];

    return result;
}

BigInteger to/from (uint[], bool)

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

/// <summary>
/// (<see cref="uint"/>[], <see cref="bool"/>) to <see cref="BigInteger"/>
/// </summary>
private BigInteger ValueOf((uint[], bool) value)
{
    var result = BigInteger.Zero;
    var array = value.Item1.SkipWhile(num => num == 0u).ToArray();
    foreach (var num in array)
    {
        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);
}

Signed Add

private static readonly (uint[], bool) ZERO = (new uint[] { 0 }, true);

/// <summary>
/// 大数加法,数组第一个<see cref="uint"/>存放最高32位,最后一个<see cref="uint"/>存放最低32位。
/// </summary>
public static (uint[], bool) Add((uint[], bool) left, (uint[], bool) right)
{
    left = StripLeadingZeroInts(left);
    right = StripLeadingZeroInts(right);

    if (IsZero(left))
        return right;
    if (IsZero(right))
        return left;

    uint[] result;
    var isNonegative = left.Item2;
    if (left.Item2 == right.Item2)//正负相同
    {
        result = AddNonegative(left.Item1, right.Item1);
    }
    else
    {
        var cmp = Compare(left.Item1, right.Item1);

        if (cmp < 0)//abs(left) < abs(right)
        {
            result = SubtractNonegative(right.Item1, left.Item1);
            isNonegative = right.Item2;
        }
        else
        {
            result = SubtractNonegative(left.Item1, right.Item1);
        }
    }
            
    result = StripLeadingZeroInts(result);

    return (result, isNonegative);
}

private static int Compare(uint[] left, uint[] right)
{
    var len1 = left.Length;
    var len2 = right.Length;
    if (len1 < len2)
        return -1;
    if (len1 > len2)
        return 1;
    for (var i = 0; i < len1; i++)
    {
        var a = left[i];
        var b = right[i];
        if (a != b)
            return ((a & LONG_MASK) < (b & LONG_MASK)) ? -1 : 1;
    }

    return 0;
}

private static bool IsZero((uint[], bool) value)
    => value.Item1.Count() == 0 || value == ZERO;

private static uint[] StripLeadingZeroInts(uint[] value)
    => value.SkipWhile(num => num == 0u).ToArray();

private static (uint[], bool) StripLeadingZeroInts((uint[], bool) value)
    => (StripLeadingZeroInts(value.Item1), value.Item2);

Signed Subtract

/// <summary>
/// 大数减法,数组第一个<see cref="uint"/>存放最高32位,最后一个<see cref="uint"/>存放最低32位。
/// </summary>
public static (uint[], bool) Subtract((uint[], bool) left, (uint[], bool) right)
{
    left = StripLeadingZeroInts(left);
    right = StripLeadingZeroInts(right);

    if (IsZero(left))
        return Negate(right);
    if (IsZero(right))
        return left;

    uint[] result;
    var isNonegative = left.Item2;
    if (left.Item2 != right.Item2)//正负相异
    {
        result = AddNonegative(left.Item1, right.Item1);
    }
    else
    {
        var cmp = Compare(left.Item1, right.Item1);
        if (cmp == 0)//abs(left) == abs(right)
            return ZERO;
        else if (cmp < 0)//abs(left) < abs(right)
        {
            result = SubtractNonegative(right.Item1, left.Item1);
            isNonegative = !right.Item2;
        }
        else
        {
            result = SubtractNonegative(left.Item1, right.Item1);
        }
    }

    result = StripLeadingZeroInts(result);

    return (result, isNonegative);
}

private static (uint[], bool) Negate((uint[], bool) value)
    => (value.Item1, !value.Item2);

测试

[TestMethod]
public void AddNonegativeTest()
{
    for(var i = 0; i < 100; ++i)
    {
        var left = BigInteger.Abs(RandomBigInteger());
        var right = BigInteger.Abs(RandomBigInteger());
        var test = AddNonegative(ToIntArray(left), ToIntArray(right));
        var expected = left + right;
        Assert.AreEqual(expected, ValueOf(test));
    }
}

[TestMethod]
public void AddTest()
{
    for (var i = 0; i < 100; ++i)
    {
        var left = RandomBigInteger();
        var right = RandomBigInteger();
        var test = Add(ToTuple(left), ToTuple(right));
        var expected = left + right;
        Assert.AreEqual(expected, ValueOf(test));
    }
}

[TestMethod]
public void SubtractNonegativeTest()
{
    for (var i = 0; i < 100; ++i)
    {
        var left = BigInteger.Abs(RandomBigInteger());
        var right = BigInteger.Abs(RandomBigInteger());
        while (right > left) right >>= 1;
        var test = SubtractNonegative(ToIntArray(left), ToIntArray(right));
        var expected = left - right;
        Assert.AreEqual(expected, ValueOf(test));
    }
}

[TestMethod]
public void SubtractTest()
{
    for (var i = 0; i < 100; ++i)
    {
        var left = RandomBigInteger();
        var right = RandomBigInteger();
        var test = Subtract(ToTuple(left), ToTuple(right));
        var expected = left - right;
        Assert.AreEqual(expected, ValueOf(test));
    }
}

[TestMethod]
public void ConvertTest()
{
    for (var i = 0; i < 100; ++i)
    {
        var value = BigInteger.Abs(RandomBigInteger());
        var test = ToIntArray(value);
        Assert.AreEqual(value, ValueOf(test));

        var value2 = RandomBigInteger();
        var test2 = ToTuple(value2);
        Assert.AreEqual(value2, ValueOf(test2));
    }
}

private BigInteger RandomBigInteger()
{
    var ran = new Random(Guid.NewGuid().GetHashCode());
    var bytes = new byte[ran.Next(100, 3000)];
    ran.NextBytes(bytes);

    return new BigInteger(bytes);
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值