java大数运算详解【其一】大数加减法

14 篇文章 0 订阅
10 篇文章 0 订阅

目录

java大数运算详解【其一】大数加减法

java大数运算详解【其二】大数乘法

java大数运算详解【其三】大数乘法之平方算法之按位二次展开式算法

java大数运算详解【其四】大数乘法之平方算法之Karatsuba平方算法

java大数运算详解【其五】大数乘法之平方算法之ToomCook3平方算法

java大数运算详解【其六】大数乘法之单位乘法和经典乘法

java大数运算详解【其七】大数乘法之Karatsuba乘法和ToomCook3乘法

java大数运算详解【其八】大数除法

java大数运算详解【其九】大数除法之试商法(Knuth除法)核心算法

java大数运算详解【其十】大数除法之Burnikel-Ziegler除法算法

 

一、大数加法
/**
     * 返回值为{@code (this + val)}的大型整数。
     *
     * @param  加数。
     * @return {@code this + val}.
     */
    public BigInteger add(BigInteger val) {
        if (val.signum == 0)
            return this;
        if (signum == 0)
            return val;
        if (val.signum == signum)//同号相加
            return new BigInteger(add(mag, val.mag), signum);
        //异号相减
        int cmp = compareMagnitude(val);
        if (cmp == 0)
            return ZERO;//ZERO为值零的大型整数
        int[] resultMag = (cmp > 0 ? subtract(mag, val.mag)
                           : subtract(val.mag, mag));//用大的数据减小的数据
        resultMag = trustedStripLeadingZeroInts(resultMag);//规范化数据

        return new BigInteger(resultMag, cmp == signum ? 1 : -1);//根据数据生成并返回
    }
    
我们再来看其相关的各部分的实现:
/**
     *将int数组x和y的内容相加。
     *一个新的int数组来保存答案并返回其引用。
     */
    private static int[] add(int[] x, int[] y) {
        // 若x数组长度短于y数组, 则交换两个数组
        if (x.length < y.length) {
            int[] tmp = x;
            x = y;
            y = tmp;
        }

        int xIndex = x.length;
        int yIndex = y.length;
        int result[] = new int[xIndex];
        long sum = 0;
        /**
         *这里采用位与将int转为long再相加并处理进位,进位保存在sum的高32位。
         */
        if (yIndex == 1) {
            sum = (x[--xIndex] & LONG_MASK) + (y[0] & LONG_MASK) ;//LONG_MASK为值0xffffffff的长整型
            result[xIndex] = (int)sum;
        } else {
            //将公共部分相加
            while (yIndex > 0) {
                sum = (x[--xIndex] & LONG_MASK) +
                      (y[--yIndex] & LONG_MASK) + (sum >>> 32);
                result[xIndex] = (int)sum;
            }
        }
        // 进位传播
        boolean carry = (sum >>> 32 != 0);
        while (xIndex > 0 && carry)
            carry = ((result[--xIndex] = x[xIndex] + 1) == 0);

        //需要在进位传播后复制较长的数
        while (xIndex > 0)
            result[--xIndex] = x[xIndex];

        //必要时扩充数组
        if (carry) {
            int bigger[] = new int[result.length + 1];
            System.arraycopy(result, 0, bigger, 1, result.length);
            bigger[0] = 0x01;
            return bigger;
        }
        return result;
    }
可以看见,add的实现与我们笔算的方式极其类似,我们称这种算法为循环带进位加法算法,简称加法。

/**
     *比较该BigInteger的mag数组与指定的BigInteger的mag数组。这是compareTo忽略符号的版本。
     * @param val BigInteger,它的mag数组需要比较。
     * @return - 1,0或1,当该BigInteger的mag数组小于,等于或大于指定的BigInteger的mag数组。
     */
    final int compareMagnitude(BigInteger val) {
        int[] m1 = mag;
        int len1 = m1.length;
        int[] m2 = val.mag;
        int len2 = m2.length;
        //先比较数组长度
        if (len1 < len2)
            return -1;
        if (len1 > len2)
            return 1;
        for (int i = 0; i < len1; i++) {//从高位依次比较
            int a = m1[i];
            int b = m2[i];
            if (a != b)
                return ((a & LONG_MASK) < (b & LONG_MASK)) ? -1 : 1;
        }
        return 0;
    }
    
/**
     * 从第一个int数组(大)减去第二个int数组(小)的内容。
     * 第一个int数组(大)必须表示比第二个大的数。
     * 这个方法分配了保存答案所需的空间。
     */
    private static int[] subtract(int[] big, int[] little) {
        int bigIndex = big.length;
        int result[] = new int[bigIndex];
        int littleIndex = little.length;
        long difference = 0;

        // 公共部分相减
        while (littleIndex > 0) {
            difference = (big[--bigIndex] & LONG_MASK) -
                         (little[--littleIndex] & LONG_MASK) +
                         (difference >> 32);
            result[bigIndex] = (int)difference;
        }

        // 借位传播
        boolean borrow = (difference >> 32 != 0);
        while (bigIndex > 0 && borrow)
            borrow = ((result[--bigIndex] = big[bigIndex] - 1) == -1);

        // 需要在借位传播后复制较长的数
        while (bigIndex > 0)
            result[--bigIndex] = big[bigIndex];

        return result;
    }
可以看见,subtract的实现也与我们笔算的方式极其类似,我们称这种算法为循环带借位减法算法,简称减法。

/**
     *返回去掉任何前置零的输入数组。
     *由于来源是可信的,复制可能会被跳过。
     */
    private static int[] trustedStripLeadingZeroInts(int val[]) {
        int vlen = val.length;
        int keep;
        // 寻找非零索引
        for (keep = 0; keep < vlen && val[keep] == 0; keep++)
            ;
        return keep == 0 ? val : java.util.Arrays.copyOfRange(val, keep, vlen);
    }
    
二、大数减法
/**
     * 返回值为{@code (this - val)}的大型整数。
     *
     * @param  减数。
     * @return {@code this - val}.
     */
    public BigInteger subtract(BigInteger val) {
        if (val.signum == 0)
            return this;
        if (signum == 0)
            return val.negate();
        if (val.signum != signum)//异号则加
            return new BigInteger(add(mag, val.mag), signum);
        //同号则减
        int cmp = compareMagnitude(val);
        if (cmp == 0)
            return ZERO;
        int[] resultMag = (cmp > 0 ? subtract(mag, val.mag)
                           : subtract(val.mag, mag));
        resultMag = trustedStripLeadingZeroInts(resultMag);
        return new BigInteger(resultMag, cmp == signum ? 1 : -1);
    }
大数减法的处理同大数加法,只是对参数的符号处理不一样,因此其流程类似。

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值