提交 36fe3287 编写于 作者: B bpb

8014319: Faster division of large integers

Summary: Implement Burnickel-Ziegler division algorithm in BigInteger
Reviewed-by: bpb, martin
Contributed-by: NTim Buktu <tbuktu@hotmail.com>
上级 e48f93eb
......@@ -33,7 +33,6 @@ import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamField;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;
import sun.misc.DoubleConsts;
......@@ -101,6 +100,7 @@ import sun.misc.FloatConsts;
* @author Josh Bloch
* @author Michael McCloskey
* @author Alan Eliasen
* @author Timothy Buktu
* @since JDK1.1
*/
......@@ -214,6 +214,14 @@ public class BigInteger extends Number implements Comparable<BigInteger> {
*/
private static final int TOOM_COOK_SQUARE_THRESHOLD = 140;
/**
* The threshold value for using Burnikel-Ziegler division. If the number
* of ints in the number are larger than this value,
* Burnikel-Ziegler division will be used. This value is found
* experimentally to work well.
*/
static final int BURNIKEL_ZIEGLER_THRESHOLD = 50;
/**
* The threshold value for using Schoenhage recursive base conversion. If
* the number of ints in the number are larger than this value,
......@@ -1781,7 +1789,7 @@ public class BigInteger extends Number implements Comparable<BigInteger> {
if (len < TOOM_COOK_SQUARE_THRESHOLD)
return squareKaratsuba();
else
return squareToomCook3();
return squareToomCook3();
}
/**
......@@ -1936,11 +1944,26 @@ public class BigInteger extends Number implements Comparable<BigInteger> {
* @throws ArithmeticException if {@code val} is zero.
*/
public BigInteger divide(BigInteger val) {
if (mag.length<BURNIKEL_ZIEGLER_THRESHOLD || val.mag.length<BURNIKEL_ZIEGLER_THRESHOLD)
return divideKnuth(val);
else
return divideBurnikelZiegler(val);
}
/**
* Returns a BigInteger whose value is {@code (this / val)} using an O(n^2) algorithm from Knuth.
*
* @param val value by which this BigInteger is to be divided.
* @return {@code this / val}
* @throws ArithmeticException if {@code val} is zero.
* @see MutableBigInteger#divideKnuth(MutableBigInteger, MutableBigInteger, boolean)
*/
private BigInteger divideKnuth(BigInteger val) {
MutableBigInteger q = new MutableBigInteger(),
a = new MutableBigInteger(this.mag),
b = new MutableBigInteger(val.mag);
a.divide(b, q, false);
a.divideKnuth(b, q, false);
return q.toBigInteger(this.signum * val.signum);
}
......@@ -1956,11 +1979,19 @@ public class BigInteger extends Number implements Comparable<BigInteger> {
* @throws ArithmeticException if {@code val} is zero.
*/
public BigInteger[] divideAndRemainder(BigInteger val) {
if (mag.length<BURNIKEL_ZIEGLER_THRESHOLD || val.mag.length<BURNIKEL_ZIEGLER_THRESHOLD)
return divideAndRemainderKnuth(val);
else
return divideAndRemainderBurnikelZiegler(val);
}
/** Long division */
private BigInteger[] divideAndRemainderKnuth(BigInteger val) {
BigInteger[] result = new BigInteger[2];
MutableBigInteger q = new MutableBigInteger(),
a = new MutableBigInteger(this.mag),
b = new MutableBigInteger(val.mag);
MutableBigInteger r = a.divide(b, q);
MutableBigInteger r = a.divideKnuth(b, q);
result[0] = q.toBigInteger(this.signum == val.signum ? 1 : -1);
result[1] = r.toBigInteger(this.signum);
return result;
......@@ -1975,11 +2006,51 @@ public class BigInteger extends Number implements Comparable<BigInteger> {
* @throws ArithmeticException if {@code val} is zero.
*/
public BigInteger remainder(BigInteger val) {
if (mag.length<BURNIKEL_ZIEGLER_THRESHOLD || val.mag.length<BURNIKEL_ZIEGLER_THRESHOLD)
return remainderKnuth(val);
else
return remainderBurnikelZiegler(val);
}
/** Long division */
private BigInteger remainderKnuth(BigInteger val) {
MutableBigInteger q = new MutableBigInteger(),
a = new MutableBigInteger(this.mag),
b = new MutableBigInteger(val.mag);
return a.divide(b, q).toBigInteger(this.signum);
return a.divideKnuth(b, q).toBigInteger(this.signum);
}
/**
* Calculates {@code this / val} using the Burnikel-Ziegler algorithm.
* @param val the divisor
* @return {@code this / val}
*/
private BigInteger divideBurnikelZiegler(BigInteger val) {
return divideAndRemainderBurnikelZiegler(val)[0];
}
/**
* Calculates {@code this % val} using the Burnikel-Ziegler algorithm.
* @param val the divisor
* @return {@code this % val}
*/
private BigInteger remainderBurnikelZiegler(BigInteger val) {
return divideAndRemainderBurnikelZiegler(val)[1];
}
/**
* Computes {@code this / val} and {@code this % val} using the
* Burnikel-Ziegler algorithm.
* @param val the divisor
* @return an array containing the quotient and remainder
*/
private BigInteger[] divideAndRemainderBurnikelZiegler(BigInteger val) {
MutableBigInteger q = new MutableBigInteger();
MutableBigInteger r = new MutableBigInteger(this).divideAndRemainderBurnikelZiegler(new MutableBigInteger(val), q);
BigInteger qBigInt = q.isZero() ? ZERO : q.toBigInteger(signum*val.signum);
BigInteger rBigInt = r.isZero() ? ZERO : r.toBigInteger(signum);
return new BigInteger[] {qBigInt, rBigInt};
}
/**
......@@ -3399,7 +3470,7 @@ public class BigInteger extends Number implements Comparable<BigInteger> {
/**
* Converts the specified BigInteger to a string and appends to
* <code>sb</code>. This implements the recursive Schoenhage algorithm
* {@code sb}. This implements the recursive Schoenhage algorithm
* for base conversions.
* <p/>
* See Knuth, Donald, _The Art of Computer Programming_, Vol. 2,
......@@ -3450,7 +3521,7 @@ public class BigInteger extends Number implements Comparable<BigInteger> {
* If this value doesn't already exist in the cache, it is added.
* <p/>
* This could be changed to a more complicated caching method using
* <code>Future</code>.
* {@code Future}.
*/
private static BigInteger getRadixConversionCache(int radix, int exponent) {
BigInteger[] cacheLine = powerCache[radix]; // volatile read
......
......@@ -43,12 +43,12 @@ import java.util.Random;
* this test is a strong assurance that the BigInteger operations
* are working correctly.
*
* Three arguments may be specified which give the number of
* decimal digits you desire in the three batches of test numbers.
* Four arguments may be specified which give the number of
* decimal digits you desire in the four batches of test numbers.
*
* The tests are performed on arrays of random numbers which are
* generated by a Random class as well as special cases which
* throw in boundary numbers such as 0, 1, maximum sized, etc.
* throw in boundary numbers such as 0, 1, maximum SIZEd, etc.
*
*/
public class BigIntegerTest {
......@@ -63,11 +63,14 @@ public class BigIntegerTest {
//
// SCHOENHAGE_BASE_CONVERSION_THRESHOLD = 8 ints = 256 bits
//
// BURNIKEL_ZIEGLER_THRESHOLD = 50 ints = 1600 bits
//
static final int BITS_KARATSUBA = 1600;
static final int BITS_TOOM_COOK = 2400;
static final int BITS_KARATSUBA_SQUARE = 2880;
static final int BITS_TOOM_COOK_SQUARE = 4480;
static final int BITS_SCHOENHAGE_BASE = 256;
static final int BITS_BURNIKEL_ZIEGLER = 1600;
static final int ORDER_SMALL = 60;
static final int ORDER_MEDIUM = 100;
......@@ -80,14 +83,15 @@ public class BigIntegerTest {
// #bits for testing Toom-Cook squaring
static final int ORDER_TOOM_COOK_SQUARE = 4600;
static final int SIZE = 1000; // numbers per batch
static Random rnd = new Random();
static int size = 1000; // numbers per batch
static boolean failure = false;
public static void pow(int order) {
int failCount1 = 0;
for (int i=0; i<size; i++) {
for (int i=0; i<SIZE; i++) {
// Test identity x^power == x*x*x ... *x
int power = rnd.nextInt(6) + 2;
BigInteger x = fetchNumber(order);
......@@ -106,7 +110,7 @@ public class BigIntegerTest {
public static void square(int order) {
int failCount1 = 0;
for (int i=0; i<size; i++) {
for (int i=0; i<SIZE; i++) {
// Test identity x^2 == x*x
BigInteger x = fetchNumber(order);
BigInteger xx = x.multiply(x);
......@@ -121,7 +125,7 @@ public class BigIntegerTest {
public static void arithmetic(int order) {
int failCount = 0;
for (int i=0; i<size; i++) {
for (int i=0; i<SIZE; i++) {
BigInteger x = fetchNumber(order);
while(x.compareTo(BigInteger.ZERO) != 1)
x = fetchNumber(order);
......@@ -187,7 +191,7 @@ public class BigIntegerTest {
int failCount = 0;
BigInteger base = BigInteger.ONE.shiftLeft(BITS_KARATSUBA - 32 - 1);
for (int i=0; i<size; i++) {
for (int i=0; i<SIZE; i++) {
BigInteger x = fetchNumber(BITS_KARATSUBA - 32 - 1);
BigInteger u = base.add(x);
int a = 1 + rnd.nextInt(31);
......@@ -210,7 +214,7 @@ public class BigIntegerTest {
failCount = 0;
base = base.shiftLeft(BITS_TOOM_COOK - BITS_KARATSUBA);
for (int i=0; i<size; i++) {
for (int i=0; i<SIZE; i++) {
BigInteger x = fetchNumber(BITS_TOOM_COOK - 32 - 1);
BigInteger u = base.add(x);
BigInteger u2 = u.shiftLeft(1);
......@@ -241,7 +245,7 @@ public class BigIntegerTest {
int failCount = 0;
BigInteger base = BigInteger.ONE.shiftLeft(BITS_KARATSUBA_SQUARE - 32 - 1);
for (int i=0; i<size; i++) {
for (int i=0; i<SIZE; i++) {
BigInteger x = fetchNumber(BITS_KARATSUBA_SQUARE - 32 - 1);
BigInteger u = base.add(x);
int a = 1 + rnd.nextInt(31);
......@@ -259,7 +263,7 @@ public class BigIntegerTest {
failCount = 0;
base = base.shiftLeft(BITS_TOOM_COOK_SQUARE - BITS_KARATSUBA_SQUARE);
for (int i=0; i<size; i++) {
for (int i=0; i<SIZE; i++) {
BigInteger x = fetchNumber(BITS_TOOM_COOK_SQUARE - 32 - 1);
BigInteger u = base.add(x);
int a = 1 + rnd.nextInt(31);
......@@ -276,10 +280,61 @@ public class BigIntegerTest {
report("squareLarge Toom-Cook", failCount);
}
/**
* Sanity test for Burnikel-Ziegler division. The Burnikel-Ziegler division
* algorithm is used when each of the dividend and the divisor has at least
* a specified number of ints in its representation. This test is based on
* the observation that if {@code w = u*pow(2,a)} and {@code z = v*pow(2,b)}
* where {@code abs(u) > abs(v)} and {@code a > b && b > 0}, then if
* {@code w/z = q1*z + r1} and {@code u/v = q2*v + r2}, then
* {@code q1 = q2*pow(2,a-b)} and {@code r1 = r2*pow(2,b)}. The test
* ensures that {@code v} is just under the B-Z threshold and that {@code w}
* and {@code z} are both over the threshold. This implies that {@code u/v}
* uses the standard division algorithm and {@code w/z} uses the B-Z
* algorithm. The results of the two algorithms are then compared using the
* observation described in the foregoing and if they are not equal a
* failure is logged.
*/
public static void divideLarge() {
int failCount = 0;
BigInteger base = BigInteger.ONE.shiftLeft(BITS_BURNIKEL_ZIEGLER - 33);
for (int i=0; i<SIZE; i++) {
BigInteger addend = new BigInteger(BITS_BURNIKEL_ZIEGLER - 34, rnd);
BigInteger v = base.add(addend);
BigInteger u = v.multiply(BigInteger.valueOf(2 + rnd.nextInt(Short.MAX_VALUE - 1)));
if(rnd.nextBoolean()) {
u = u.negate();
}
if(rnd.nextBoolean()) {
v = v.negate();
}
int a = 17 + rnd.nextInt(16);
int b = 1 + rnd.nextInt(16);
BigInteger w = u.multiply(BigInteger.valueOf(1L << a));
BigInteger z = v.multiply(BigInteger.valueOf(1L << b));
BigInteger[] divideResult = u.divideAndRemainder(v);
divideResult[0] = divideResult[0].multiply(BigInteger.valueOf(1L << (a - b)));
divideResult[1] = divideResult[1].multiply(BigInteger.valueOf(1L << b));
BigInteger[] bzResult = w.divideAndRemainder(z);
if (divideResult[0].compareTo(bzResult[0]) != 0 ||
divideResult[1].compareTo(bzResult[1]) != 0) {
failCount++;
}
}
report("divideLarge", failCount);
}
public static void bitCount() {
int failCount = 0;
for (int i=0; i<size*10; i++) {
for (int i=0; i<SIZE*10; i++) {
int x = rnd.nextInt();
BigInteger bigX = BigInteger.valueOf((long)x);
int bit = (x < 0 ? 0 : 1);
......@@ -300,7 +355,7 @@ public class BigIntegerTest {
public static void bitLength() {
int failCount = 0;
for (int i=0; i<size*10; i++) {
for (int i=0; i<SIZE*10; i++) {
int x = rnd.nextInt();
BigInteger bigX = BigInteger.valueOf((long)x);
int signBit = (x < 0 ? 0x80000000 : 0);
......@@ -321,7 +376,7 @@ public class BigIntegerTest {
public static void bitOps(int order) {
int failCount1 = 0, failCount2 = 0, failCount3 = 0;
for (int i=0; i<size*5; i++) {
for (int i=0; i<SIZE*5; i++) {
BigInteger x = fetchNumber(order);
BigInteger y;
......@@ -351,7 +406,7 @@ public class BigIntegerTest {
report("clearBit/testBit for " + order + " bits", failCount1);
report("flipBit/testBit for " + order + " bits", failCount2);
for (int i=0; i<size*5; i++) {
for (int i=0; i<SIZE*5; i++) {
BigInteger x = fetchNumber(order);
// Test getLowestSetBit()
......@@ -375,7 +430,7 @@ public class BigIntegerTest {
// Test identity x^y == x|y &~ x&y
int failCount = 0;
for (int i=0; i<size; i++) {
for (int i=0; i<SIZE; i++) {
BigInteger x = fetchNumber(order);
BigInteger y = fetchNumber(order);
BigInteger z = x.xor(y);
......@@ -387,7 +442,7 @@ public class BigIntegerTest {
// Test identity x &~ y == ~(~x | y)
failCount = 0;
for (int i=0; i<size; i++) {
for (int i=0; i<SIZE; i++) {
BigInteger x = fetchNumber(order);
BigInteger y = fetchNumber(order);
BigInteger z = x.andNot(y);
......@@ -440,7 +495,7 @@ public class BigIntegerTest {
public static void divideAndRemainder(int order) {
int failCount1 = 0;
for (int i=0; i<size; i++) {
for (int i=0; i<SIZE; i++) {
BigInteger x = fetchNumber(order).abs();
while(x.compareTo(BigInteger.valueOf(3L)) != 1)
x = fetchNumber(order).abs();
......@@ -519,7 +574,7 @@ public class BigIntegerTest {
public static void byteArrayConv(int order) {
int failCount = 0;
for (int i=0; i<size; i++) {
for (int i=0; i<SIZE; i++) {
BigInteger x = fetchNumber(order);
while (x.equals(BigInteger.ZERO))
x = fetchNumber(order);
......@@ -536,7 +591,7 @@ public class BigIntegerTest {
public static void modInv(int order) {
int failCount = 0, successCount = 0, nonInvCount = 0;
for (int i=0; i<size; i++) {
for (int i=0; i<SIZE; i++) {
BigInteger x = fetchNumber(order);
while(x.equals(BigInteger.ZERO))
x = fetchNumber(order);
......@@ -565,7 +620,7 @@ public class BigIntegerTest {
public static void modExp(int order1, int order2) {
int failCount = 0;
for (int i=0; i<size/10; i++) {
for (int i=0; i<SIZE/10; i++) {
BigInteger m = fetchNumber(order1).abs();
while(m.compareTo(BigInteger.ONE) != 1)
m = fetchNumber(order1).abs();
......@@ -883,8 +938,8 @@ public class BigIntegerTest {
/**
* Main to interpret arguments and run several tests.
*
* Up to three arguments may be given to specify the size of BigIntegers
* used for call parameters 1, 2, and 3. The size is interpreted as
* Up to three arguments may be given to specify the SIZE of BigIntegers
* used for call parameters 1, 2, and 3. The SIZE is interpreted as
* the maximum number of decimal digits that the parameters will have.
*
*/
......@@ -945,6 +1000,7 @@ public class BigIntegerTest {
multiplyLarge();
squareLarge();
divideLarge();
if (failure)
throw new RuntimeException("Failure in BigIntegerTest.");
......@@ -952,7 +1008,7 @@ public class BigIntegerTest {
/*
* Get a random or boundary-case number. This is designed to provide
* a lot of numbers that will find failure points, such as max sized
* a lot of numbers that will find failure points, such as max SIZEd
* numbers, empty BigIntegers, etc.
*
* If order is less than 2, order is changed to 2.
......@@ -987,13 +1043,13 @@ public class BigIntegerTest {
break;
case 4: // Random bit density
int iterations = rnd.nextInt(order-1);
result = BigInteger.ONE.shiftLeft(rnd.nextInt(order));
for(int i=0; i<iterations; i++) {
BigInteger temp = BigInteger.ONE.shiftLeft(
rnd.nextInt(order));
result = result.or(temp);
byte[] val = new byte[(order+7)/8];
int iterations = rnd.nextInt(order);
for (int i=0; i<iterations; i++) {
int bitIdx = rnd.nextInt(order);
val[bitIdx/8] |= 1 << (bitIdx%8);
}
result = new BigInteger(1, val);
break;
case 5: // Runs of consecutive ones and zeros
result = ZERO;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册