失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > java大数运算详解【其三】大数乘法之平方算法之按位二次展开式算法

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

时间:2024-07-26 05:35:40

相关推荐

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

目录

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

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

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

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

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

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

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

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

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

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

所有解释都是最基本的,没有过多赘述,如若不懂静心思考。

1.1、按位二次展开式算法

/**

* 将整型数组x的内容平方,结果放入整型数组z中,x的内容不变。

*/

private static final int[] squareToLen(int[] x, int len, int[] z) {

int zlen = len << 1;

if (z == null || z.length < zlen)

z = new int[zlen];

// 在调用intrinsified(增强)方法之前执行检查。

implSquareToLenChecks(x, len, z, zlen);

return implSquareToLen(x, len, z, zlen);

}

/**

* 参数验证。

*/

private static void implSquareToLenChecks(int[] x, int len, int[] z, int zlen) throws RuntimeException {

if (len < 1) {

throw new IllegalArgumentException("invalid input length: " + len);

}

if (len > x.length) {

throw new IllegalArgumentException("input length out of bound: " +

len + " > " + x.length);

}

if (len * 2 > z.length) {

throw new IllegalArgumentException("input length out of bound: " +

(len * 2) + " > " + z.length);

}

if (zlen < 1) {

throw new IllegalArgumentException("invalid input length: " + zlen);

}

if (zlen > z.length) {

throw new IllegalArgumentException("input length out of bound: " +

len + " > " + z.length);

}

}

/**

* Java运行时可以为该方法使用intrinsic(固有的,增强的)。

*/

@HotSpotIntrinsicCandidate

private static final int[] implSquareToLen(int[] x, int len, int[] z, int zlen) {

/*

* 这里使用的算法来自Colin Plumb的C库。

* 技巧:考虑乘法中的部分乘积

* “abcde”本身:

*

* a b c d e

* * a b c d e

* ==================

* ae be ce de ee

* ad bd cd dd de

* ac bc cc cd ce

* ab bb bc bd be

* aa ab ac ad ae

*

* 注意主对角线以上的所有元素:

* ae be ce de = (abcd) * e

* ad bd cd = (abc) * d

* ac bc = (ab) * c

* ab = (a) * b

*

* 是主对角线以下所有内容的副本:

* de

* cd ce

* bc bd be

* ab ac ad ae

*

* 因此,总和是2 *(对角线上方或下方)+对角线。

*

* 这是从对角线开始累积的(对角线由输入数字的平方组成),

* 然后对角线除以2,非对角线相加,再乘以2。

* 低位只是输入低位的一个副本,因此不需要特别注意。

*/

// 储存平方值,右移一位(即,除以2)。

int lastProductLowWord = 0;

for (int j=0, i=0; j < len; j++) {//计算每一位的平方值并存储在z中且右移一位

long piece = (x[j] & LONG_MASK);

long product = piece * piece;

z[i++] = (lastProductLowWord << 31) | (int)(product >>> 33);

z[i++] = (int)(product >>> 1);

lastProductLowWord = (int)product;

}

// 计算对角线上方或下方的总和

for (int i=len, offset=1; i > 0; i--, offset+=2) {//注意:offset-1=2*(len-i),即:2*len-offset=2*i-1.

int t = x[i-1];

t = mulAdd(z, x, offset, i-1, t);

addOne(z, offset-1, i, t);//t的添加位置为z[z.length-1-(offset-1)-i],即:z[2*len-offset-i].

//故addOne函数最多需进位传播2*len-offset-i=i-1次,为判断运算是否溢出,需传参mlen为i,而不是i-1.

//其实运算一定不会产生溢出(zlen=2*len时),所以传参mlen为i-1也可行(同时传参offset为offset)。

}

// 回移并设置低位

primitiveLeftShift(z, zlen, 1);

z[zlen-1] |= x[len-1] & 1;

return z;

}

/**

* 将数组乘以一个整数k,然后添加到结果中,返回进位。

*/

static int mulAdd(int[] out, int[] in, int offset, int len, int k) {

implMulAddCheck(out, in, offset, len, k);

return implMulAdd(out, in, offset, len, k);

}

/**

* 参数验证。

*/

private static void implMulAddCheck(int[] out, int[] in, int offset, int len, int k) {

if (len > in.length) {

throw new IllegalArgumentException("input length is out of bound: " + len + " > " + in.length);

}

if (offset < 0) {

throw new IllegalArgumentException("input offset is invalid: " + offset);

}

if (offset > (out.length - 1)) {

throw new IllegalArgumentException("input offset is out of bound: " + offset + " > " + (out.length - 1));

}

if (len > (out.length - offset)) {

throw new IllegalArgumentException("input len is out of bound: " + len + " > " + (out.length - offset));

}

}

/**

* Java运行时可以为该方法使用intrinsic(固有的,增强的)。

* 将in数组(长度为len,数据倒序存储)乘以k,再添加到out数组(偏移量为offset,数据倒序存储),

* 添加位置为第offset位,即out[out.length-offset - 1].

* 返回进位。

*/

@HotSpotIntrinsicCandidate

private static int implMulAdd(int[] out, int[] in, int offset, int len, int k) {

long kLong = k & LONG_MASK;

long carry = 0;

offset = out.length-offset - 1;//重计算偏移量

for (int j=len-1; j >= 0; j--) {

long product = (in[j] & LONG_MASK) * kLong +

(out[offset] & LONG_MASK) + carry;

out[offset--] = (int)product;

carry = product >>> 32;

}

return (int)carry;

}

/**

* 在a中加上一个整数,把carry这个整数加到a中。

* 把carry添加到a中,添加位置为第offset+mlen位,即a[a.length-1-mlen-offset].

* 进位最多传播mlen位。

* 返回结果进位。

*/

static int addOne(int[] a, int offset, int mlen, int carry) {

offset = a.length-1-mlen-offset;

long t = (a[offset] & LONG_MASK) + (carry & LONG_MASK);

a[offset] = (int)t;

if ((t >>> 32) == 0)

return 0;

while (--mlen >= 0) {

if (--offset < 0) { // 数据溢出

return 1;

} else {

a[offset]++;

if (a[offset] != 0)

return 0;//进位传播处理完成,返回0

}

}

return 1;//进位传播了mlen次,仍未处理完毕,返回进位1

}

// 将a数组(长度len,数据倒序存储)左移n位,假设没有前导零,0<=n<32.

static void primitiveLeftShift(int[] a, int len, int n) {

if (len == 0 || n == 0)

return;

int n2 = 32 - n;

for (int i=0, c=a[i], m=i+len-1; i < m; i++) {//循环移位

int b = c;

c = a[i+1];

a[i] = (b << n) | (c >>> n2);

}

a[len-1] <<= n;

}

按位二次展开式算法,即将mag数组看作len个项的和,并使用二次展开式进行计算其平方值。

如果觉得《java大数运算详解【其三】大数乘法之平方算法之按位二次展开式算法》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。