001 /* 002 * $Id: Roots.java 3296 2010-08-26 17:30:55Z kredel $ 003 */ 004 005 package edu.jas.arith; 006 007 008 // import java.util.Random; 009 import java.math.MathContext; 010 011 import edu.jas.structure.Power; 012 013 014 /** 015 * Root computation algorithms. Roots for BigInteger and BigDecimals. 016 * @author Heinz Kredel 017 */ 018 public class Roots { 019 020 021 /** 022 * Integer n-th root. Uses BigDecimal and newton iteration. R is the n-th 023 * root of A. 024 * @param A big integer. 025 * @param n long. 026 * @return the n-th root of A. 027 */ 028 public static BigInteger root(BigInteger A, int n) { 029 if (n == 1) { 030 return A; 031 } 032 if (n == 2) { 033 return sqrt(A); 034 } 035 if (n < 1) { 036 throw new IllegalArgumentException("negative root not defined"); 037 } 038 if (A == null || A.isZERO() || A.isONE()) { 039 return A; 040 } 041 // ensure enough precision 042 int s = A.val.bitLength() + 2; 043 MathContext mc = new MathContext(s); 044 //System.out.println("mc = " + mc); 045 BigDecimal Ap = new BigDecimal(A.val, mc); 046 //System.out.println("Ap = " + Ap); 047 BigDecimal Ar = root(Ap, n); 048 //System.out.println("Ar = " + Ar); 049 java.math.BigInteger RP = Ar.val.toBigInteger(); 050 BigInteger R = new BigInteger(RP); 051 while (true) { 052 BigInteger P = Power.positivePower(R, n); 053 //System.out.println("P = " + P); 054 if (A.compareTo(P) >= 0) { 055 break; 056 } else { 057 R = R.subtract(BigInteger.ONE); 058 } 059 } 060 return R; 061 } 062 063 064 /** 065 * Integer square root. Uses BigDecimal and newton iteration. R is the 066 * square root of A. 067 * @param A big integer. 068 * @return the square root of A. 069 */ 070 public static BigInteger sqrt(BigInteger A) { 071 if (A == null || A.isZERO() || A.isONE()) { 072 return A; 073 } 074 // ensure enough precision 075 int s = A.val.bitLength() + 2; 076 MathContext mc = new MathContext(s); 077 //System.out.println("mc = " + mc); 078 // newton iteration 079 BigDecimal Ap = new BigDecimal(A.val, mc); 080 //System.out.println("Ap = " + Ap); 081 BigDecimal Ar = sqrt(Ap); 082 //System.out.println("Ar = " + Ar); 083 java.math.BigInteger RP = Ar.val.toBigInteger(); 084 BigInteger R = new BigInteger(RP); 085 while (true) { 086 BigInteger P = R.multiply(R); 087 //System.out.println("P = " + P); 088 if (A.compareTo(P) >= 0) { 089 break; 090 } else { 091 R = R.subtract(BigInteger.ONE); 092 } 093 } 094 return R; 095 } 096 097 098 /** 099 * Integer square root. Uses BigInteger only. R is the square root of A. 100 * @param A big integer. 101 * @return the square root of A. 102 */ 103 public static BigInteger sqrtInt(BigInteger A) { 104 if (A == null || A.isZERO() || A.isONE()) { 105 return A; 106 } 107 int s = A.signum(); 108 if (s < 0) { 109 throw new ArithmeticException("root of negative not defined"); 110 } 111 if (s == 0) { 112 return A; 113 } 114 BigInteger R, R1, d; 115 int log2 = A.val.bitLength(); 116 //System.out.println("A = " + A + ", log2 = " + log2); 117 int rootlog2 = log2 - log2 / 2; 118 R = new BigInteger(A.val.shiftRight(rootlog2)); 119 //System.out.println("R = " + R + ", rootlog2 = " + rootlog2); 120 d = R; 121 while (!d.isZERO()) { 122 d = new BigInteger(d.val.shiftRight(1)); // div 2 123 R1 = R.sum(d); 124 s = A.compareTo(R1.multiply(R1)); 125 if (s == 0) { 126 return R1; 127 } 128 if (s > 0) { 129 R = R1; 130 } 131 //System.out.println("R1 = " + R1); 132 //System.out.println("d = " + d); 133 } 134 while (true) { 135 R1 = R.sum(BigInteger.ONE); 136 //System.out.println("R1 = " + R1); 137 s = A.compareTo(R1.multiply(R1)); 138 if (s == 0) { 139 return R1; 140 } 141 if (s > 0) { 142 R = R1; 143 } 144 if (s < 0) { 145 return R; 146 } 147 } 148 //return R; 149 } 150 151 152 /** 153 * Square root. R is the square root of A. 154 * @param A big decimal. 155 * @return the square root of A. 156 */ 157 public static BigDecimal sqrt(BigDecimal A) { 158 if (A == null || A.isZERO() || A.isONE()) { 159 return A; 160 } 161 // for small A use root of inverse 162 if (A.val.compareTo(BigDecimal.ONE.val) < 0) { 163 BigDecimal Ap = A.inverse(); 164 //System.out.println("A.inverse() = " + Ap); 165 Ap = sqrt(Ap); 166 return Ap.inverse(); 167 } 168 MathContext mc = A.context; 169 // newton iteration 170 BigDecimal Ap = new BigDecimal(A.val, mc); 171 BigDecimal ninv = new BigDecimal(0.5, mc); 172 BigDecimal R1, R = Ap.multiply(ninv); // initial guess 173 BigDecimal d; 174 while (true) { 175 R1 = R.sum(Ap.divide(R)); 176 R1 = R1.multiply(ninv); // div n 177 d = R.subtract(R1).abs(); 178 R = R1; 179 if (d.val.compareTo(BigDecimal.ONE.val) <= 0) { 180 //System.out.println("d = " + d); 181 break; 182 } 183 } 184 return R; 185 } 186 187 188 /** 189 * N-th root. R is the n-th root of A. 190 * @param A big decimal. 191 * @param n long. 192 * @return the n-th root of A. 193 */ 194 public static BigDecimal root(BigDecimal A, int n) { 195 if (n == 1) { 196 return A; 197 } 198 if (n == 2) { 199 return sqrt(A); 200 } 201 if (n < 1) { 202 throw new IllegalArgumentException("negative root not defined"); 203 } 204 if (A == null || A.isZERO() || A.isONE()) { 205 return A; 206 } 207 // for small A use root of inverse 208 if (A.val.compareTo(BigDecimal.ONE.val) < 0) { 209 BigDecimal Ap = A.inverse(); 210 //System.out.println("A.inverse() = " + Ap); 211 Ap = root(Ap, n); 212 return Ap.inverse(); 213 } 214 // ensure enough precision 215 MathContext mc = A.context; 216 // newton iteration 217 BigDecimal Ap = A; 218 BigDecimal N = new BigDecimal(n, mc); 219 BigDecimal ninv = new BigDecimal(1.0 / n, mc); 220 BigDecimal nsub = new BigDecimal(1.0, mc); // because of precision 221 nsub = nsub.subtract(ninv); 222 //BigDecimal half = BigDecimal.ONE.sum(BigDecimal.ONE).inverse(); 223 BigDecimal half = new BigDecimal(BigDecimal.ONE.val.divide(java.math.BigDecimal.TEN)); 224 BigDecimal P, R1, R = Ap.multiply(ninv); // initial guess 225 BigDecimal d; 226 while (true) { 227 P = Power.positivePower(R, n - 1); 228 R1 = Ap.divide(P.multiply(N)); 229 R1 = R.multiply(nsub).sum(R1); 230 d = R.subtract(R1).abs(); 231 R = R1; 232 //if ( d.compareTo( BigDecimal.ONE ) <= 0 ) { 233 // System.out.println("d = " + d); 234 if (d.val.compareTo(half.val) <= 0) { 235 //System.out.println("d.val = " + d.val); 236 break; 237 } 238 //} 239 } 240 return R; 241 } 242 243 }