645 lines
18 KiB
Java
645 lines
18 KiB
Java
package org.nevec.rjm;
|
|
|
|
import java.math.BigInteger;
|
|
import java.util.Vector;
|
|
|
|
import it.cavallium.warppi.util.Error;
|
|
|
|
/**
|
|
* BigInteger special functions and Number theory.
|
|
*
|
|
* @since 2009-08-06
|
|
* @author Richard J. Mathar
|
|
*/
|
|
public class BigIntegerMath {
|
|
|
|
/**
|
|
* Evaluate binomial(n,k).
|
|
*
|
|
* @param n
|
|
* The upper index
|
|
* @param k
|
|
* The lower index
|
|
* @return The binomial coefficient
|
|
*/
|
|
static public BigInteger binomial(final int n, final int k) {
|
|
if (k == 0) {
|
|
return BigInteger.ONE;
|
|
}
|
|
BigInteger bin = new BigInteger("" + n);
|
|
final BigInteger n2 = bin;
|
|
for (BigInteger i = new BigInteger("" + (k - 1)); i.compareTo(BigInteger.ONE) >= 0; i = i.subtract(BigInteger.ONE)) {
|
|
bin = bin.multiply(n2.subtract(i));
|
|
}
|
|
for (BigInteger i = new BigInteger("" + k); i.compareTo(BigInteger.ONE) == 1; i = i.subtract(BigInteger.ONE)) {
|
|
bin = bin.divide(i);
|
|
}
|
|
return bin;
|
|
} /* binomial */
|
|
|
|
/**
|
|
* Evaluate binomial(n,k).
|
|
*
|
|
* @param n
|
|
* The upper index
|
|
* @param k
|
|
* The lower index
|
|
* @return The binomial coefficient
|
|
* @since 2008-10-15
|
|
*/
|
|
static public BigInteger binomial(final BigInteger n, final BigInteger k) {
|
|
/*
|
|
* binomial(n,0) =1
|
|
*/
|
|
if (k.compareTo(BigInteger.ZERO) == 0) {
|
|
return BigInteger.ONE;
|
|
}
|
|
|
|
BigInteger bin = new BigInteger("" + n);
|
|
|
|
/*
|
|
* the following version first calculates n(n-1)(n-2)..(n-k+1)
|
|
* in the first loop, and divides this product through k(k-1)(k-2)....2
|
|
* in the second loop. This is rather slow and replaced by a faster
|
|
* version
|
|
* below
|
|
* BigInteger n2 = bin ;
|
|
* BigInteger i= k.subtract(BigInteger.ONE) ;
|
|
* for( ; i.compareTo(BigInteger.ONE) >= 0 ; i =
|
|
* i.subtract(BigInteger.ONE) )
|
|
* bin = bin.multiply(n2.subtract(i)) ;
|
|
* i= new BigInteger(""+k) ;
|
|
* for( ; i.compareTo(BigInteger.ONE) == 1 ; i =
|
|
* i.subtract(BigInteger.ONE) )
|
|
* bin = bin.divide(i) ;
|
|
*/
|
|
|
|
/*
|
|
* calculate n then n(n-1)/2 then n(n-1)(n-2)(2*3) etc up to
|
|
* n(n-1)..(n-k+1)/(2*3*..k)
|
|
* This is roughly the best way to keep the individual intermediate
|
|
* products small
|
|
* and in the integer domain. First replace C(n,k) by C(n,n-k) if n-k<k.
|
|
*/
|
|
BigInteger truek = new BigInteger(k.toString());
|
|
if (n.subtract(k).compareTo(k) < 0) {
|
|
truek = n.subtract(k);
|
|
}
|
|
|
|
/*
|
|
* Calculate C(num,truek) where num=n and truek is the smaller of n-k
|
|
* and k.
|
|
* Have already initialized bin=n=C(n,1) above. Start definining the
|
|
* factorial
|
|
* in the denominator, named fden
|
|
*/
|
|
BigInteger i = new BigInteger("2");
|
|
BigInteger num = new BigInteger(n.toString());
|
|
/*
|
|
* a for-loop (i=2;i<= truek;i++)
|
|
*/
|
|
for (; i.compareTo(truek) <= 0; i = i.add(BigInteger.ONE)) {
|
|
/*
|
|
* num = n-i+1 after this operation
|
|
*/
|
|
num = num.subtract(BigInteger.ONE);
|
|
/*
|
|
* multiply by (n-i+1)/i
|
|
*/
|
|
bin = bin.multiply(num).divide(i);
|
|
}
|
|
return bin;
|
|
} /* binomial */
|
|
|
|
/**
|
|
* Evaluate sigma_k(n).
|
|
*
|
|
* @param n
|
|
* the main argument which defines the divisors
|
|
* @param k
|
|
* the lower index, which defines the power
|
|
* @return The sum of the k-th powers of the positive divisors
|
|
*/
|
|
static public BigInteger sigmak(final BigInteger n, final int k) {
|
|
return new Ifactor(n.abs()).sigma(k).n;
|
|
} /* sigmak */
|
|
|
|
/**
|
|
* Evaluate sigma(n).
|
|
*
|
|
* @param n
|
|
* the argument for which divisors will be searched.
|
|
* @return the sigma function. Sum of the positive divisors of the argument.
|
|
* @since 2006-08-14
|
|
* @author Richard J. Mathar
|
|
*/
|
|
static public BigInteger sigma(final int n) {
|
|
return new Ifactor(Math.abs(n)).sigma().n;
|
|
}
|
|
|
|
/**
|
|
* Compute the list of positive divisors.<br>
|
|
* <b>Deprecated: This function is extremely slow and inefficient!</b>
|
|
*
|
|
* @param n
|
|
* The integer of which the divisors are to be found.
|
|
* @return The sorted list of positive divisors.
|
|
* @since 2010-08-27
|
|
* @author Richard J. Mathar
|
|
*/
|
|
@Deprecated
|
|
static public Vector<BigInteger> divisors(final BigInteger n) {
|
|
return new Ifactor(n.abs()).divisors();
|
|
}
|
|
|
|
/**
|
|
* Evaluate sigma(n).
|
|
*
|
|
* @param n
|
|
* the argument for which divisors will be searched.
|
|
* @return the sigma function. Sum of the divisors of the argument.
|
|
* @since 2006-08-14
|
|
* @author Richard J. Mathar
|
|
*/
|
|
static public BigInteger sigma(final BigInteger n) {
|
|
return new Ifactor(n.abs()).sigma().n;
|
|
}
|
|
|
|
/**
|
|
* Evaluate floor(sqrt(n)).
|
|
*
|
|
* @param n
|
|
* The non-negative argument.
|
|
* @return The integer square root. The square root rounded down.
|
|
* @since 2010-08-27
|
|
* @author Richard J. Mathar
|
|
*/
|
|
static public int isqrt(final int n) {
|
|
if (n < 0) {
|
|
throw new ArithmeticException("Negative argument " + n);
|
|
}
|
|
final double resul = Math.sqrt(n);
|
|
return (int) Math.round(resul);
|
|
}
|
|
|
|
/**
|
|
* Evaluate floor(sqrt(n)).
|
|
*
|
|
* @param n
|
|
* The non-negative argument.
|
|
* Arguments less than zero throw an ArithmeticException.
|
|
* @return The integer square root, the square root rounded down.
|
|
* @since 2010-08-27
|
|
* @author Richard J. Mathar
|
|
*/
|
|
static public long isqrt(final long n) {
|
|
if (n < 0) {
|
|
throw new ArithmeticException("Negative argument " + n);
|
|
}
|
|
final double resul = Math.sqrt(n);
|
|
return Math.round(resul);
|
|
}
|
|
|
|
/**
|
|
* Evaluate floor(sqrt(n)).
|
|
*
|
|
* @param n
|
|
* The non-negative argument.
|
|
* Arguments less than zero throw an ArithmeticException.
|
|
* @return The integer square root, the square root rounded down.
|
|
* @since 2011-02-12
|
|
* @author Richard J. Mathar
|
|
*/
|
|
static public BigInteger isqrt(final BigInteger n) {
|
|
if (n.compareTo(BigInteger.ZERO) < 0) {
|
|
throw new ArithmeticException("Negative argument " + n.toString());
|
|
}
|
|
/*
|
|
* Start with an estimate from a floating point reduction.
|
|
*/
|
|
BigInteger x;
|
|
final int bl = n.bitLength();
|
|
if (bl > 120) {
|
|
x = n.shiftRight(bl / 2 - 1);
|
|
} else {
|
|
final double resul = Math.sqrt(n.doubleValue());
|
|
x = new BigInteger("" + Math.round(resul));
|
|
}
|
|
|
|
final BigInteger two = new BigInteger("2");
|
|
while (true) {
|
|
/*
|
|
* check whether the result is accurate, x^2 =n
|
|
*/
|
|
final BigInteger x2 = x.pow(2);
|
|
BigInteger xplus2 = x.add(BigInteger.ONE).pow(2);
|
|
if (x2.compareTo(n) <= 0 && xplus2.compareTo(n) > 0) {
|
|
return x;
|
|
}
|
|
xplus2 = xplus2.subtract(x.shiftLeft(2));
|
|
if (xplus2.compareTo(n) <= 0 && x2.compareTo(n) > 0) {
|
|
return x.subtract(BigInteger.ONE);
|
|
}
|
|
/*
|
|
* Newton algorithm. This correction is on the
|
|
* low side caused by the integer divisions. So the value required
|
|
* may end up by one unit too large by the bare algorithm, and this
|
|
* is caught above by comparing x^2, (x+-1)^2 with n.
|
|
*/
|
|
xplus2 = x2.subtract(n).divide(x).divide(two);
|
|
x = x.subtract(xplus2);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Evaluate core(n).
|
|
* Returns the smallest positive integer m such that n/m is a perfect
|
|
* square.
|
|
*
|
|
* @param n
|
|
* The non-negative argument.
|
|
* @return The square-free part of n.
|
|
* @since 2011-02-12
|
|
* @author Richard J. Mathar
|
|
*/
|
|
static public BigInteger core(final BigInteger n) {
|
|
if (n.compareTo(BigInteger.ZERO) < 0) {
|
|
throw new ArithmeticException("Negative argument " + n);
|
|
}
|
|
final Ifactor i = new Ifactor(n);
|
|
return i.core();
|
|
}
|
|
|
|
/**
|
|
* Minor of an integer matrix.
|
|
*
|
|
* @param A
|
|
* The matrix.
|
|
* @param r
|
|
* The row index of the row to be removed (0-based).
|
|
* An exception is thrown if this is outside the range 0 to the
|
|
* upper row index of A.
|
|
* @param c
|
|
* The column index of the column to be removed (0-based).
|
|
* An exception is thrown if this is outside the range 0 to the
|
|
* upper column index of A.
|
|
* @return The depleted matrix. This is not a deep copy but contains
|
|
* references to the original.
|
|
* @since 2010-08-27
|
|
* @author Richard J. Mathar
|
|
*/
|
|
static public BigInteger[][] minor(final BigInteger[][] A, final int r, final int c) throws ArithmeticException {
|
|
/* original row count */
|
|
final int rL = A.length;
|
|
if (rL == 0) {
|
|
throw new ArithmeticException("zero row count in matrix");
|
|
}
|
|
if (r < 0 || r >= rL) {
|
|
throw new ArithmeticException("row number " + r + " out of range 0.." + (rL - 1));
|
|
}
|
|
/* original column count */
|
|
final int cL = A[0].length;
|
|
if (cL == 0) {
|
|
throw new ArithmeticException("zero column count in matrix");
|
|
}
|
|
if (c < 0 || c >= cL) {
|
|
throw new ArithmeticException("column number " + c + " out of range 0.." + (cL - 1));
|
|
}
|
|
final BigInteger M[][] = new BigInteger[rL - 1][cL - 1];
|
|
int imrow = 0;
|
|
for (int row = 0; row < rL; row++) {
|
|
if (row != r) {
|
|
int imcol = 0;
|
|
for (int col = 0; col < cL; col++) {
|
|
if (col != c) {
|
|
M[imrow][imcol] = A[row][col];
|
|
imcol++;
|
|
}
|
|
}
|
|
imrow++;
|
|
}
|
|
}
|
|
return M;
|
|
}
|
|
|
|
/**
|
|
* Replace column of a matrix with a column vector.
|
|
*
|
|
* @param A
|
|
* The matrix.
|
|
* @param c
|
|
* The column index of the column to be substituted (0-based).
|
|
* @param v
|
|
* The column vector to be inserted.
|
|
* With the current implementation, it must be at least as long
|
|
* as the row count, and
|
|
* its elements that exceed that count are ignored.
|
|
* @return The modified matrix. This is not a deep copy but contains
|
|
* references to the original.
|
|
* @since 2010-08-27
|
|
* @author Richard J. Mathar
|
|
*/
|
|
@SuppressWarnings("unused")
|
|
static private BigInteger[][] colSubs(final BigInteger[][] A, final int c, final BigInteger[] v)
|
|
throws ArithmeticException {
|
|
/* original row count */
|
|
final int rL = A.length;
|
|
if (rL == 0) {
|
|
throw new ArithmeticException("zero row count in matrix");
|
|
}
|
|
/* original column count */
|
|
final int cL = A[0].length;
|
|
if (cL == 0) {
|
|
throw new ArithmeticException("zero column count in matrix");
|
|
}
|
|
if (c < 0 || c >= cL) {
|
|
throw new ArithmeticException("column number " + c + " out of range 0.." + (cL - 1));
|
|
}
|
|
final BigInteger M[][] = new BigInteger[rL][cL];
|
|
for (int row = 0; row < rL; row++) {
|
|
for (int col = 0; col < cL; col++) {
|
|
/*
|
|
* currently, v may just be longer than the row count, and
|
|
* surplus
|
|
* elements will be ignored. Shorter v lead to an exception.
|
|
*/
|
|
if (col != c) {
|
|
M[row][col] = A[row][col];
|
|
} else {
|
|
M[row][col] = v[row];
|
|
}
|
|
}
|
|
}
|
|
return M;
|
|
}
|
|
|
|
/**
|
|
* Determinant of an integer square matrix.
|
|
*
|
|
* @param A
|
|
* The square matrix.
|
|
* If column and row dimensions are unequal, an
|
|
* ArithmeticException is thrown.
|
|
* @return The determinant.
|
|
* @since 2010-08-27
|
|
* @author Richard J. Mathar
|
|
*/
|
|
static public BigInteger det(final BigInteger[][] A) throws ArithmeticException {
|
|
BigInteger d = BigInteger.ZERO;
|
|
/* row size */
|
|
final int rL = A.length;
|
|
if (rL == 0) {
|
|
throw new ArithmeticException("zero row count in matrix");
|
|
}
|
|
/* column size */
|
|
final int cL = A[0].length;
|
|
if (cL != rL) {
|
|
throw new ArithmeticException("Non-square matrix dim " + rL + " by " + cL);
|
|
}
|
|
|
|
/*
|
|
* Compute the low-order cases directly.
|
|
*/
|
|
if (rL == 1) {
|
|
return A[0][0];
|
|
} else if (rL == 2) {
|
|
d = A[0][0].multiply(A[1][1]);
|
|
return d.subtract(A[0][1].multiply(A[1][0]));
|
|
} else {
|
|
/* Work arbitrarily along the first column of the matrix */
|
|
for (int r = 0; r < rL; r++) {
|
|
/*
|
|
* Do not consider minors that do no contribute anyway
|
|
*/
|
|
if (A[r][0].compareTo(BigInteger.ZERO) != 0) {
|
|
final BigInteger M[][] = BigIntegerMath.minor(A, r, 0);
|
|
final BigInteger m = A[r][0].multiply(BigIntegerMath.det(M));
|
|
/* recursive call */
|
|
if (r % 2 == 0) {
|
|
d = d.add(m);
|
|
} else {
|
|
d = d.subtract(m);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return d;
|
|
}
|
|
|
|
/**
|
|
* Solve a linear system of equations.
|
|
*
|
|
* @param A
|
|
* The square matrix.
|
|
* If it is not of full rank, an ArithmeticException is thrown.
|
|
* @param rhs
|
|
* The right hand side. The length of this vector must match the
|
|
* matrix size;
|
|
* else an ArithmeticException is thrown.
|
|
* @return The vector of x in A*x=rhs.
|
|
* @since 2010-08-28
|
|
* @author Richard J. Mathar
|
|
* @throws Error
|
|
*/
|
|
static public Rational[] solve(final BigInteger[][] A, final BigInteger[] rhs) throws ArithmeticException, Error {
|
|
|
|
final int rL = A.length;
|
|
if (rL == 0) {
|
|
throw new ArithmeticException("zero row count in matrix");
|
|
}
|
|
|
|
/* column size */
|
|
final int cL = A[0].length;
|
|
if (cL != rL) {
|
|
throw new ArithmeticException("Non-square matrix dim " + rL + " by " + cL);
|
|
}
|
|
if (rhs.length != rL) {
|
|
throw new ArithmeticException("Right hand side dim " + rhs.length + " unequal matrix dim " + rL);
|
|
}
|
|
|
|
/*
|
|
* Gauss elimination
|
|
*/
|
|
final Rational x[] = new Rational[rL];
|
|
|
|
/*
|
|
* copy of r.h.s ito a mutable Rationalright hand side
|
|
*/
|
|
for (int c = 0; c < cL; c++) {
|
|
x[c] = new Rational(rhs[c]);
|
|
}
|
|
|
|
/*
|
|
* Create zeros downwards column c by linear combination of row c and
|
|
* row r.
|
|
*/
|
|
for (int c = 0; c < cL - 1; c++) {
|
|
/*
|
|
* zero on the diagonal? swap with a non-zero row, searched with
|
|
* index r
|
|
*/
|
|
if (A[c][c].compareTo(BigInteger.ZERO) == 0) {
|
|
boolean swpd = false;
|
|
for (int r = c + 1; r < rL; r++) {
|
|
if (A[r][c].compareTo(BigInteger.ZERO) != 0) {
|
|
for (int cpr = c; cpr < cL; cpr++) {
|
|
final BigInteger tmp = A[c][cpr];
|
|
A[c][cpr] = A[r][cpr];
|
|
A[r][cpr] = tmp;
|
|
}
|
|
final Rational tmp = x[c];
|
|
x[c] = x[r];
|
|
x[r] = tmp;
|
|
swpd = true;
|
|
break;
|
|
}
|
|
}
|
|
/*
|
|
* not swapped with a non-zero row: determinant zero and no
|
|
* solution
|
|
*/
|
|
if (!swpd) {
|
|
throw new ArithmeticException("Zero determinant of main matrix");
|
|
}
|
|
}
|
|
/* create zero at A[c+1..cL-1][c] */
|
|
for (int r = c + 1; r < rL; r++) {
|
|
/*
|
|
* skip the cpr=c which actually sets the zero: this element is
|
|
* not visited again
|
|
*/
|
|
for (int cpr = c + 1; cpr < cL; cpr++) {
|
|
final BigInteger tmp = A[c][c].multiply(A[r][cpr]).subtract(A[c][cpr].multiply(A[r][c]));
|
|
A[r][cpr] = tmp;
|
|
}
|
|
final Rational tmp = x[r].multiply(A[c][c]).subtract(x[c].multiply(A[r][c]));
|
|
x[r] = tmp;
|
|
}
|
|
}
|
|
if (A[cL - 1][cL - 1].compareTo(BigInteger.ZERO) == 0) {
|
|
throw new ArithmeticException("Zero determinant of main matrix");
|
|
}
|
|
/* backward elimination */
|
|
for (int r = cL - 1; r >= 0; r--) {
|
|
x[r] = x[r].divide(A[r][r]);
|
|
for (int rpr = r - 1; rpr >= 0; rpr--) {
|
|
x[rpr] = x[rpr].subtract(x[r].multiply(A[rpr][r]));
|
|
}
|
|
}
|
|
|
|
return x;
|
|
}
|
|
|
|
/**
|
|
* The lowest common multiple
|
|
*
|
|
* @param a
|
|
* The first argument
|
|
* @param b
|
|
* The second argument
|
|
* @return lcm(|a|,|b|)
|
|
* @since 2010-08-27
|
|
* @author Richard J. Mathar
|
|
*/
|
|
static public BigInteger lcm(final BigInteger a, final BigInteger b) {
|
|
final BigInteger g = a.gcd(b);
|
|
return a.multiply(b).abs().divide(g);
|
|
}
|
|
|
|
/**
|
|
* Evaluate the value of an integer polynomial at some integer argument.
|
|
*
|
|
* @param c
|
|
* Represents the coefficients c[0]+c[1]*x+c[2]*x^2+.. of the
|
|
* polynomial
|
|
* @param x
|
|
* The abscissa point of the evaluation
|
|
* @return The polynomial value.
|
|
* @since 2010-08-27
|
|
* @author Richard J. Mathar
|
|
*/
|
|
static public BigInteger valueOf(final Vector<BigInteger> c, final BigInteger x) {
|
|
if (c.size() == 0) {
|
|
return BigInteger.ZERO;
|
|
}
|
|
BigInteger res = c.lastElement();
|
|
for (int i = c.size() - 2; i >= 0; i--) {
|
|
res = res.multiply(x).add(c.elementAt(i));
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* The central factorial number t(n,k) number at the indices provided.
|
|
*
|
|
* @param n
|
|
* the first parameter, non-negative.
|
|
* @param k
|
|
* the second index, non-negative.
|
|
* @return t(n,k)
|
|
* @since 2009-08-06
|
|
* @author Richard J. Mathar
|
|
* @throws Error
|
|
* @see <a href="https://doi.org/10.1080/01630568908816313">P. L. Butzer
|
|
* et al, Num. Funct. Anal. Opt. 10 (5)( 1989) 419-488</a>
|
|
*/
|
|
static public Rational centrlFactNumt(final int n, final int k) throws Error {
|
|
if (k > n || k < 0 || k % 2 != n % 2) {
|
|
return Rational.ZERO;
|
|
} else if (k == n) {
|
|
return Rational.ONE;
|
|
} else {
|
|
/* Proposition 6.2.6 */
|
|
final Factorial f = new Factorial();
|
|
Rational jsum = new Rational(0, 1);
|
|
final int kprime = n - k;
|
|
for (int j = 0; j <= kprime; j++) {
|
|
Rational nusum = new Rational(0, 1);
|
|
for (int nu = 0; nu <= j; nu++) {
|
|
Rational t = new Rational(j - 2 * nu, 2);
|
|
t = t.pow(kprime + j);
|
|
t = t.multiply(BigIntegerMath.binomial(j, nu));
|
|
if (nu % 2 != 0) {
|
|
nusum = nusum.subtract(t);
|
|
} else {
|
|
nusum = nusum.add(t);
|
|
}
|
|
}
|
|
nusum = nusum.divide(f.at(j)).divide(n + j);
|
|
nusum = nusum.multiply(BigIntegerMath.binomial(2 * kprime, kprime - j));
|
|
if (j % 2 != 0) {
|
|
jsum = jsum.subtract(nusum);
|
|
} else {
|
|
jsum = jsum.add(nusum);
|
|
}
|
|
}
|
|
return jsum.multiply(k).multiply(BigIntegerMath.binomial(n + kprime, k));
|
|
}
|
|
} /* CentralFactNumt */
|
|
|
|
/**
|
|
* The central factorial number T(n,k) number at the indices provided.
|
|
*
|
|
* @param n
|
|
* the first parameter, non-negative.
|
|
* @param k
|
|
* the second index, non-negative.
|
|
* @return T(n,k)
|
|
* @since 2009-08-06
|
|
* @author Richard J. Mathar
|
|
* @see <a href="https://doi.org/10.1080/01630568908816313">P. L. Butzer
|
|
* et al, Num. Funct. Anal. Opt. 10 (5)( 1989) 419-488</a>
|
|
*/
|
|
static public Rational centrlFactNumT(final int n, final int k) {
|
|
if (k > n || k < 0 || k % 2 != n % 2) {
|
|
return Rational.ZERO;
|
|
} else if (k == n) {
|
|
return Rational.ONE;
|
|
} else {
|
|
/* Proposition 2.1 */
|
|
return BigIntegerMath.centrlFactNumT(n - 2, k - 2).add(BigIntegerMath.centrlFactNumT(n - 2, k).multiply(new Rational(k * k, 4)));
|
|
}
|
|
} /* CentralFactNumT */
|
|
|
|
} /* BigIntegerMath */
|