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

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

目录

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

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

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

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

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

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

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

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

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

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


4、Karatsuba乘法(二分展开式乘法)
/**
     * 使用Karatsuba乘法算法将两个大整数相乘。
     * 这是一种递归的分治算法,与多聚类算法中通常使用的“小学”算法相比,它在处理大数问题时效率更高。
     * 如果进行乘法的数组长度为n,“小学”算法的渐近O(n ^ 2)的复杂性。
     * 相比之下,Karatsuba算法的复杂性O(n ^(log2(3))),或O(n ^ 1.585)。
     * 在对过程进行评估时,它通过执行3次乘法而不是4次来实现这种性能提升。
     * 由于它有一些开销,当两个数字都大于某个阈值时(通过实验发现),应该使用它。
     *
     * See:  http://en.wikipedia.org/wiki/Karatsuba_algorithm
     */
    private static BigInteger multiplyKaratsuba(BigInteger x, BigInteger y) {
        int xlen = x.mag.length;
        int ylen = y.mag.length;
        // 每个数组的一半的长度。
        int half = (Math.max(xlen, ylen)+1) / 2;
        // xl和yl分别是x和y的下半部,xh和yh是上半部。
        BigInteger xl = x.getLower(half);
        BigInteger xh = x.getUpper(half);
        BigInteger yl = y.getLower(half);
        BigInteger yh = y.getUpper(half);
        BigInteger p1 = xh.multiply(yh);  // p1 = xh*yh
        BigInteger p2 = xl.multiply(yl);  // p2 = xl*yl
        // p3=(xh+xl)*(yh+yl)
        BigInteger p3 = xh.add(xl).multiply(yh.add(yl));
        // result = p1 * 2^(32*2*half) + (p3 - p1 - p2) * 2^(32*half) + p2
        BigInteger result = p1.shiftLeft(32*half).add(p3.subtract(p1).subtract(p2)).shiftLeft(32*half).add(p2);
        if (x.signum != y.signum) {
            return result.negate();
        } else {
            return result;
        }
    }

5、ToomCook3乘法(三分展开式乘法)
/**
     * 使用3路Toom-Cook乘法算法将两个大整数相乘。
     * 这是一种递归的分治算法,与多聚类算法中通常使用的“小学”算法相比,它在处理大数问题时效率更高。
     * 如果进行乘法的数组长度为n,“小学”算法的渐近O(n ^ 2)的复杂性。
     * 相比之下,3路Toom-Cook的复杂性为O(n ^ 1.465)。
     * 它通过将每个数字分成三部分,并在计算乘积时进行5次而不是9次的乘法,从而实现这种渐近性能的提高。
     * 由于图姆-库克算法的开销(加法、移位和除法),它只能在两个数字都大于某个阈值时使用(实验发现)。
     * 这个阈值通常比卡拉图巴乘法的阈值大,所以这种算法通常只在数字明显变大时才使用。
     *
     * 使用的算法是Marco Bodrato概述的“最优”3路Toom-Cook算法。
     *  See: http://bodrato.it/toom-cook/
     *       http://bodrato.it/papers/#WAIFI2007
     */
    private static BigInteger multiplyToomCook3(BigInteger a, BigInteger b) {
        int alen = a.mag.length;
        int blen = b.mag.length;
        int largest = Math.max(alen, blen);
        // k是低阶切片的大小(以int表示)。
        int k = (largest+2)/3;   // Equal to ceil(largest/3)
        // r是最高阶片的大小(以int为单位)。
        int r = largest - 2*k;
        // 获取这些数字的切片。a2和b2是数字中最重要的位(高位),a0和b0是最不重要的位(低位)。
        BigInteger a0, a1, a2, b0, b1, b2;
        a2 = a.getToomSlice(k, r, 0, largest);
        a1 = a.getToomSlice(k, r, 1, largest);
        a0 = a.getToomSlice(k, r, 2, largest);
        b2 = b.getToomSlice(k, r, 0, largest);
        b1 = b.getToomSlice(k, r, 1, largest);
        b0 = b.getToomSlice(k, r, 2, largest);
        BigInteger v0, v1, v2, vm1, vinf, t1, t2, tm1, da1, db1;
        v0 = a0.multiply(b0);
        da1 = a2.add(a0);
        db1 = b2.add(b0);
        vm1 = da1.subtract(a1).multiply(db1.subtract(b1));
        da1 = da1.add(a1);
        db1 = db1.add(b1);
        v1 = da1.multiply(db1);
        v2 = da1.add(a2).shiftLeft(1).subtract(a0).multiply(
             db1.add(b2).shiftLeft(1).subtract(b0));
        vinf = a2.multiply(b2);
        // 这个算法需要两个2的除法和一个3的除法。
        // 所有的除法都是精确的,也就是说,它们不产生余数,所有的结果都是肯定的。
        // 以2为单位的除法是按右移执行的,这样比较有效,只剩下以3为单位的除法。
        // 3的除法是由这种情况下的优化算法完成的。
        t2 = v2.subtract(vm1).exactDivideBy3();
        tm1 = v1.subtract(vm1).shiftRight(1);
        t1 = v1.subtract(v0);
        t2 = t2.subtract(t1).shiftRight(1);
        t1 = t1.subtract(tm1).subtract(vinf);
        t2 = t2.subtract(vinf.shiftLeft(1));
        tm1 = tm1.subtract(t2);
        // 向左移位的位数。
        int ss = k*32;
        BigInteger result = vinf.shiftLeft(ss).add(t2).shiftLeft(ss).add(t1).shiftLeft(ss).add(tm1).shiftLeft(ss).add(v0);
        if (a.signum != b.signum) {
            return result.negate();
        } else {
            return result;
        }
    }
   

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值