I am currently trying to solve this problem as described here:
http://uva.onlinejudge.org/external/1/113.pdf
The plan was to implement a recursive function to derive the solution. Some of the code here comes from Rosetta code for determining the nth root.
// Power of Cryptography 113
import java.util.Scanner;
import java.math.BigDecimal;
import java.math.RoundingMode;
// k can be 10^9
// n <= 200
// p <= 10^101
class crypto {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
while(in.hasNext()) {
// Given two integers (n,p)
// Find k such k^n = p
int n = in.nextInt();
BigDecimal p = in.nextBigDecimal();
System.out.println(nthroot(n,p));
}
}
public static BigDecimal nthroot(int n, BigDecimal A) {
return nthroot(n, A, .001);
}
public static BigDecimal nthroot(int n, BigDecimal A, double p) {
if(A.compareTo(BigDecimal.ZERO) < 0) return new BigDecimal(-1);
// we handle only real positive numbers
else if(A.equals(BigDecimal.ZERO)) {
return BigDecimal.ZERO;
}
BigDecimal x_prev = A;
BigDecimal x = A.divide(new BigDecimal(n)); // starting "guessed" value...
BigDecimal y = x.subtract(x_prev);
while(y.abs().compareTo(new BigDecimal(p)) > 0) {
x_prev = x;
BigDecimal temp = new BigDecimal(n-1.0);
x = (x.multiply(temp).add(A).divide(x.pow(temp.intValue())).divide(new BigDecimal(n)));
}
return x;
}
}
And here is the resulting error code:
Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
at java.math.BigDecimal.divide(BigDecimal.java:1616)
at crypto.nthroot(crypto.java:38)
at crypto.nthroot(crypto.java:24)
at crypto.main(crypto.java:19)
Anybody here for a working code snippet? Here we go:
public final class RootCalculus {
private static final int SCALE = 10;
private static final int ROUNDING_MODE = BigDecimal.ROUND_HALF_DOWN;
public static BigDecimal nthRoot(final int n, final BigDecimal a) {
return nthRoot(n, a, BigDecimal.valueOf(.1).movePointLeft(SCALE));
}
private static BigDecimal nthRoot(final int n, final BigDecimal a, final BigDecimal p) {
if (a.compareTo(BigDecimal.ZERO) < 0) {
throw new IllegalArgumentException("nth root can only be calculated for positive numbers");
}
if (a.equals(BigDecimal.ZERO)) {
return BigDecimal.ZERO;
}
BigDecimal xPrev = a;
BigDecimal x = a.divide(new BigDecimal(n), SCALE, ROUNDING_MODE); // starting "guessed" value...
while (x.subtract(xPrev).abs().compareTo(p) > 0) {
xPrev = x;
x = BigDecimal.valueOf(n - 1.0)
.multiply(x)
.add(a.divide(x.pow(n - 1), SCALE, ROUNDING_MODE))
.divide(new BigDecimal(n), SCALE, ROUNDING_MODE);
}
return x;
}
private RootCalculus() {
}
}
Just set SCALE to however precise you need the calculation to be.
That is expected if the resulting mathematical decimal number is non-terminating. The Javadocs for the 1-arg overload of divide state:
Throws:
ArithmeticException - if the exact quotient does not have a terminating decimal expansion
Use another overload of the divide method to specify a scale (a cutoff) (and a RoundingMode).
Related
I have two classes: Fraction and Test. I already do well with class Fraction, but class Test has some issues.
I want to allow the user enter the fractions and store in ArrayList, the user can compare two fraction from the array by choosing the index of the array. But when I compare two fraction, it doesn't work well!
class Fraction:
class Fraction {
private int numerator;
private int denominator;
Fraction(int n, int d) {
numerator = n;
denominator = d;
}
public Fraction(int n) {
this(n, 1);
}
public Fraction() {
numerator = 0;
denominator = 1;
}
public int getNumerator() {
return numerator;
}
public void setNumerator(int numerator) {
this.numerator = numerator;
}
public int getDenominator() {
return denominator;
}
public void setDenominator(int denominator) {
this.denominator = denominator;
}
public void display() {
String s = this.getNumerator() + "/" + this.getDenominator();
System.out.println(s);
}
public double evaluate() {
double n = numerator;
double d = denominator;
return (n / d);
}
public boolean isEquals(Fraction f){
int gcd1 = gcd(f.getNumerator(), f.getDenominator());
double fractionFloatValue = (f.getNumerator()/gcd1) / (f.getDenominator()/gcd1);
int gcd2 = gcd(this.getNumerator(), this.getDenominator());
double fractionFloatValue2 = (this.getNumerator()/gcd2) / (this.getDenominator()/gcd2);
return (fractionFloatValue == fractionFloatValue2) ? true : false;
}
public Fraction add(Fraction f2) {
Fraction r = new Fraction((numerator * f2.denominator)
+ (f2.numerator * denominator), (denominator * f2.denominator));
return r;
}
static private int gcd(int x, int y) {
return y == 0 ? x : gcd(y, x % y);
}
public static String asFraction(int x, int y) {
int gcd = gcd(x, y);
return (x / gcd) + "/" + (y / gcd);
}
/*public static void main(String[] argv) {
Fraction f0 = new Fraction();
Fraction f1 = new Fraction(3);
Fraction f2 = new Fraction(20, 60);
Fraction f3 = new Fraction(1, 3);
System.out.println("--------------Testing constructors--------------");
f0.display();
f1.display();
f2.display();
System.out.println("--------------Test if two fractions is equal--------------");
System.out.println(f2.isEquals(f1));
}*/
}
and class Test:
import java.util.ArrayList;
import java.util.Scanner;
public class Test {
public static void enterFraction(){
ArrayList<Fraction> arr = new ArrayList<Fraction>();
Scanner scanner = new Scanner(System.in);
boolean check = false;
int i = 1;
while(!check){
System.out.println("Enter fraction"+i+":");
Fraction f = new Fraction();
System.out.println("Enter Numerator: ");
int numerator = scanner.nextInt();
scanner.nextLine();
f.setNumerator(numerator);
System.out.println("Enter Denominator: ");
int denominator = scanner.nextInt();
scanner.nextLine();
f.setDenominator(denominator);
System.out.println("Your fraction"+i+" is: "+f.getNumerator()+"/"+f.getDenominator());
arr.add(f);
System.out.println("Want to compare fractions? (Y/Yes or N/No)");
String compareRequest = scanner.nextLine();
if(compareRequest.equalsIgnoreCase("y")){
System.out.println("Choose your target fraction!!! (enter the index of the array)");
int position = scanner.nextInt();
scanner.nextLine();
Fraction targetFraction = arr.get(position);
targetFraction.display();
System.out.println("Choose your second fraction to compare!!! (enter the index of the array)");
int position2 = scanner.nextInt();
scanner.nextLine();
Fraction secondFraction = arr.get(position2);
secondFraction.display();
boolean compareTwoFractions = secondFraction.isEquals(targetFraction);
if(compareTwoFractions == true){
System.out.println("Two fractions are equal");
}
else if(compareTwoFractions == false){
System.out.println("Two fractions are not equal");
}
}
i++;
System.out.println("Do you want to enter more fraction? (Y/Yes or N/No)");
String checkRequest = scanner.nextLine();
if(checkRequest.equalsIgnoreCase("n")){
check = true;
}
}
}
public static void main(String[] args){
enterFraction();
}
}
I input like this:
Enter fraction1:
Enter Numerator:
2
Enter Denominator:
4
Your fraction1 is: 2/4
Want to compare fractions? (Y/Yes or N/No)
n
Do you want to enter more fraction? (Y/Yes or N/No)
y
Enter fraction2:
Enter Numerator:
1
Enter Denominator:
3
Your fraction2 is: 1/3
Want to compare fractions? (Y/Yes or N/No)
y
Choose your target fraction!!! (enter the index of the array)
0
2/4
Choose your second fraction to compare!!! (enter the index of the array)
1
1/3
Two fractions are equal
Do you want to enter more fraction? (Y/Yes or N/No)
You see it not work, 2/4 == 1/3. Please point me somethings with this.
The problem is that getNumerator(), getDenominator(), and gcd return an int. Therefore, the division inside your equals method is done in integers:
double fractionFloatValue = (f.getNumerator()/gcd1) / (f.getDenominator()/gcd1);
...
double fractionFloatValue2 = (this.getNumerator()/gcd2) / (this.getDenominator()/gcd2);
The value of fractionFloatValue and fractionFloatValue2 are, in fact, integers, even though they are assigned to variables of type double. Both 1/3 and 1/2 are proper fractions, so integer division evaluates to zero in both cases. That's why your equals returns true in both cases.
There are two ways to fix this:
Declare gcd1 and gcd2 as double. This would force the division into double; unfortunately, your code would suffer from double comparison for equality, which is inherently imprecise, or
Use identity n1/d1 == n2/d2 when n1*d2 == n2*d1. This eliminates division, so you get perfect precision in your comparisons until you overflow (and you would not overflow with the constraints that you are using if you use long for the results of your multiplication).
I change two line that #dasblinkenlight has mentioned by:
double fractionFloatValue = ((f.getNumerator()/gcd1)*1.0) / (f.getDenominator()/gcd1);
double fractionFloatValue2 = ((this.getNumerator()/gcd2)*1.0) / (this.getDenominator()/gcd2);
and It worked now.
I was trying to get a cubic root in java using Math.pow(n, 1.0/3) but because it divides doubles, it doesn't return the exact answer. For example, with 125, this gives 4.9999999999. Is there a work-around for this? I know there is a cubic root function but I'd like to fix this so I can calculate higher roots.
I would not like to round because I want to know whether a number has an integer root by doing something like this: Math.pow(n, 1.0 / 3) % ((int) Math.pow(n, 1.0 / 3)).
Since it is not possible to have arbitrary-precision calculus with double, you have three choices:
Define a precision for which you decide whether a double value is an integer or not.
Test whether the rounded value of the double you have is a correct result.
Do calculus on a BigDecimal object, which supports arbitrary-precision double values.
Option 1
private static boolean isNthRoot(int value, int n, double precision) {
double a = Math.pow(value, 1.0 / n);
return Math.abs(a - Math.round(a)) < precision; // if a and round(a) are "close enough" then we're good
}
The problem with this approach is how to define "close enough". This is a subjective question and it depends on your requirements.
Option 2
private static boolean isNthRoot(int value, int n) {
double a = Math.pow(value, 1.0 / n);
return Math.pow(Math.round(a), n) == value;
}
The advantage of this method is that there is no need to define a precision. However, we need to perform another pow operation so this will affect performance.
Option 3
There is no built-in method to calculate a double power of a BigDecimal. This question will give you insight on how to do it.
The Math.round function will round to the nearest long value that can be stored to a double. You could compare the 2 results to see if the number has an integer cubic root.
double dres = Math.pow(125, 1.0 / 3.0);
double ires = Math.round(dres);
double diff = Math.abs(dres - ires);
if (diff < Math.ulp(10.0)) {
// has cubic root
}
If that's inadequate you can try implementing this algorithm and stop early if the result doesn't seem to be an integer.
I wrote this method to compute floor(x^(1/n)) where x is a non-negative BigInteger and n is a positive integer. It was a while ago now so I can't explain why it works, but I'm reasonably confident that when I wrote it I was happy that it's guaranteed to give the correct answer reasonably quickly.
To see if x is an exact n-th power you can check if the result raised to the power n gives you exactly x back again.
public static BigInteger floorOfNthRoot(BigInteger x, int n) {
int sign = x.signum();
if (n <= 0 || (sign < 0))
throw new IllegalArgumentException();
if (sign == 0)
return BigInteger.ZERO;
if (n == 1)
return x;
BigInteger a;
BigInteger bigN = BigInteger.valueOf(n);
BigInteger bigNMinusOne = BigInteger.valueOf(n - 1);
BigInteger b = BigInteger.ZERO.setBit(1 + x.bitLength() / n);
do {
a = b;
b = a.multiply(bigNMinusOne).add(x.divide(a.pow(n - 1))).divide(bigN);
} while (b.compareTo(a) == -1);
return a;
}
To use it:
System.out.println(floorOfNthRoot(new BigInteger("125"), 3));
Edit
Having read the comments above I now remember that this is the Newton-Raphson method for n-th roots. The Newton-Raphson method has quadratic convergence (which in everyday language means it's fast). You can try it on numbers which have dozens of digits and you should get the answer in a fraction of a second.
You can adapt the method to work with other number types, but double and BigDecimal are in my view not suited for this kind of thing.
You can use some tricks come from mathematics field, to havemore accuracy.
Like this one x^(1/n) = e^(lnx/n).
Check the implementation here:
https://www.baeldung.com/java-nth-root
Here is the solution without using Java's Math.pow function.
It will give you nearly nth root
public class NthRoot {
public static void main(String[] args) {
try (Scanner scanner = new Scanner(System.in)) {
int testcases = scanner.nextInt();
while (testcases-- > 0) {
int root = scanner.nextInt();
int number = scanner.nextInt();
double rootValue = compute(number, root) * 1000.0 / 1000.0;
System.out.println((int) rootValue);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static double compute(int number, int root) {
double xPre = Math.random() % 10;
double error = 0.0000001;
double delX = 2147483647;
double current = 0.0;
while (delX > error) {
current = ((root - 1.0) * xPre + (double) number / Math.pow(xPre, root - 1)) / (double) root;
delX = Math.abs(current - xPre);
xPre = current;
}
return current;
}
I'd go for implementing my own function to do this, possibly based on this method.
Well this is a good option to choose in this situation.
You can rely on this-
System.out.println(" ");
System.out.println(" Enter a base and then nth root");
while(true)
{
a=Double.parseDouble(br.readLine());
b=Double.parseDouble(br.readLine());
double negodd=-(Math.pow((Math.abs(a)),(1.0/b)));
double poseve=Math.pow(a,(1.0/b));
double posodd=Math.pow(a,(1.0/b));
if(a<0 && b%2==0)
{
String io="\u03AF";
double negeve=Math.pow((Math.abs(a)),(1.0/b));
System.out.println(" Root is imaginary and value= "+negeve+" "+io);
}
else if(a<0 && b%2==1)
System.out.println(" Value= "+negodd);
else if(a>0 && b%2==0)
System.out.println(" Value= "+poseve);
else if(a>0 && b%2==1)
System.out.println(" Value= "+posodd);
System.out.println(" ");
System.out.print(" Enter '0' to come back or press any number to continue- ");
con=Integer.parseInt(br.readLine());
if(con==0)
break;
else
{
System.out.println(" Enter a base and then nth root");
continue;
}
}
It's a pretty ugly hack, but you could reach a few of them through indenting.
System.out.println(Math.sqrt(Math.sqrt(256)));
System.out.println(Math.pow(4, 4));
System.out.println(Math.pow(4, 9));
System.out.println(Math.cbrt(Math.cbrt(262144)));
Result:
4.0
256.0
262144.0
4.0
Which will give you every n^3th cube and every n^2th root.
Find nth root Using binary search method.
Here is the way to find nth root with any precision according to your requirements.
import java.util.Scanner;
public class FindRoot {
public static void main(String[] args) {
try (Scanner scanner = new Scanner(System.in)) {
int testCase = scanner.nextInt();
while (testCase-- > 0) {
double number = scanner.nextDouble();
int root = scanner.nextInt();
double precision = scanner.nextDouble();
double result = findRoot(number, root, precision);
System.out.println(result);
}
}
}
private static double findRoot(double number, int root, double precision) {
double start = 0;
double end = number / 2;
double mid = end;
while (true) {
if (precision >= diff(number, mid, root)) {
return mid;
}
if (pow(mid, root) > number) {
end = mid;
} else {
start = mid;
}
mid = (start + end) / 2;
}
}
private static double diff(double number, double mid, int n) {
double power = pow(mid, n);
return number > power ? number - power : power - number;
}
private static double pow(double number, int pow) {
double result = number;
while (pow-- > 1) {
result *= number;
}
return result;
}
}
I'm using this nth_root algorithm, which also provide the remainder :
public static BigInteger[] sqrt(final BigInteger n) {
final BigInteger[] res = {ZERO, n,};
BigInteger a, b;
assert (n.signum() > 0);
a = ONE.shiftLeft(n.bitLength() & ~1);
while (!a.equals(ZERO)) {
b = res[0].add(a);
res[0] = res[0].shiftRight(1);
if (res[1].compareTo(b) >= 0) {
res[1] = res[1].subtract(b);
res[0] = res[0].add(a);
}
a = a.shiftRight(2);
}
return res;
}
public static BigInteger[] nth_root(BigInteger n, final int nth) {
final BigInteger[] res;
switch(nth){
case 0 : res = new BigInteger[]{n.equals(ONE) ? ONE : ZERO, ZERO} ; break;
case 1 : res = new BigInteger[]{n, ZERO}; break;
case 2 : res = sqrt(n); break;
default:
int sign = n.signum() ;
n = n.abs();
res = new BigInteger[]{n.shiftLeft((n.bitLength() + nth - 1) / nth), n};
while(res[1].compareTo(res[0])<0) {
res[0] = res[1];
res[1] = BigInteger.valueOf(nth-1).multiply(res[1]).add(n.divide(res[1].pow(nth - 1))).divide(BigInteger.valueOf(nth));
}
res[1] = res[0].pow(nth);
res[1] = n.subtract(res[1]);
if (sign < 0 && (nth & 1) == 1) {
res[0] = res[0].negate();
res[1] = res[1].negate();
} else assert (sign > 0);
}
return res ;
}
}
What I have to do is take 2 random variables for a fraction, 1 to 1000, and check to see if they are in reduced terms already or not. I do this 1,000 times and keep track of whether it was or wasn't in reduced terms.
Here is the main class
import java.util.*;
public class ratio1 {
/**
* #param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
int nonReducedCount = 0; //counts how many non reduced ratios there are
for(int i =1; i<=1000; i++){
Random rand = new Random();
int n = rand.nextInt(1000)+1; //random int creation
int m = rand.nextInt(1000)+1;
Ratio ratio = new Ratio(n,m);
if (ratio.getReduceCount() != 0 ){ // if the ratio was not already fully reduced
nonReducedCount++; // increase the count of non reduced ratios
}
}
int reducedCount = 1000 - nonReducedCount; //number of times the ratio was reduced already
double reducedRatio = reducedCount / nonReducedCount; //the ratio for reduced and not reduced
reducedRatio *= 6;
reducedRatio = Math.sqrt(reducedRatio);
System.out.println("pi is " + reducedRatio);
}
}
And here is the class I am not sure about. All I want from it is to determine whether or not the fraction is already in simplest form. When I currently try to run it, it is giving me an error; "Exception in thread "main" java.lang.StackOverflowError
at Ratio.gcd (Ratio.java:67)
at Ratio.gcd (Ratio.java:66)"
public class Ratio{
protected int numerator; // numerator of ratio
protected int denominator; //denominator of ratio
public int reduceCount = 0; //counts how many times the reducer goes
public Ratio(int top, int bottom)
//pre: bottom !=0
//post: constructs a ratio equivalent to top::bottom
{
numerator = top;
denominator = bottom;
reduce();
}
public int getNumerator()
//post: return the numerator of the fraction
{
return numerator;
}
public int getDenominator()
//post: return the denominator of the fraction
{
return denominator;
}
public double getValue()
//post: return the double equivalent of the ratio
{
return (double)numerator/(double)denominator;
}
public int getReduceCount()
//post: returns the reduceCount
{
return reduceCount;
}
public Ratio add(Ratio other)
//pre: other is nonnull
//post: return new fraction--the sum of this and other
{
return new Ratio(this.numerator*other.denominator+this.denominator*other.numerator,this.denominator*other.denominator);
}
protected void reduce()
//post: numerator and denominator are set so that the greatest common divisor of the numerator and demoninator is 1
{
int divisor = gcd(numerator, denominator);
if(denominator < 0) divisor = -divisor;
numerator /= divisor;
denominator /= divisor;
reduceCount++;
}
protected static int gcd(int a, int b)
//post: computes the greatest integer value that divides a and b
{
if (a<0) return gcd(-a,b);
if (a==0){
if(b==0) return 1;
else return b;
}
if (b>a) return gcd(b,a);
return gcd(b%a,a);
}
public String toString()
//post:returns a string that represents this fraction.
{
return getNumerator()+"/"+getDenominator();
}
}
Here are the lines of the error in the Ratio class;
if (b>a) return gcd(b,a);
return gcd(b%a,a);
A fraction is reducible if its GCD is greater than 1. You can compute the GCD with the static method given in Ratio, so you could instead use:
...
int n = rand.nextInt(1000)+1;
int m = rand.nextInt(1000)+1;
if(Ratio.gcd(n,m) == 1) {
nonReducedCount++;
}
This saves you from instantiating a new Ratio instance.
If that method doesn't work for you, you can always use your own GCD calculator. This one is recursive too and similar to the one in Ratio:
public static int gcd(int a, int b) { return b==0 ? a : gcd(b,a%b); }
You could Google it for non-recursive methods if the StackOverflowError is still a problem.
I am writing a program to calculate Feigenbaum's constant using the Logistics equation by finding superstable values and then using the ratio of these superstable values to calculate the constant.
I use BigDecimals for almost all of my values so that I can maintain the necessary level of precision during the calculation of the constant.
I am adapting my code from the C++ code on pages 30-35 of the following file: http://webcache.googleusercontent.com/search?q=cache:xabTioRiF0IJ:home.simula.no/~logg/pub/reports/chaos_hw1.ps.gz+&cd=21&hl=en&ct=clnk&gl=us
I doubt what the program does even matters to my question. I run the program, and it seems to be working. The output i get for the first 4 superstable values and the first 2 d's is what is expected, but then after displaying these 4 rows, the program seems to just halt. I don't get an exception, but even after waiting for 30 minutes no more calculations are outputted. I can't figure out what exactly is causing it, because the calculation time should be about the same for each row, yet it obviously is not. Here is my output:
Feigenbaum constant calculation (using superstable points):
j a d
-----------------------------------------------------
1 2.0 N/A
2 3.23606797749979 N/A
4 3.4985616993277016 4.708943013540503
8 3.554640862768825 4.680770998010695
And here is my code:
import java.math.*;
// If there is a stable cycle, the iterates of 1/2 converge to the cycle.
// This was proved by Fatou and Julia.
// (What's special about x = 1/2 is that it is the critical point, the point at which the logistic map's derivative is 0.)
// Source: http://classes.yale.edu/fractals/chaos/Cycles/LogisticCycles/CycleGeneology.html
public class Feigenbaum4
{
public static BigDecimal r[] = new BigDecimal[19];
public static int iter = 0;
public static int iter1 = 20; // Iterations for tolerance level 1
public static int iter2 = 10; // Iterations for tolerance level 2
public static BigDecimal tol1 = new BigDecimal("2E-31"); // Tolerance for convergence level 1
public static BigDecimal tol2 = new BigDecimal("2E-27"); // Tolerance for convergence level 2
public static BigDecimal step = new BigDecimal("0.01"); // step when looking for second superstable a
public static BigDecimal x0 = new BigDecimal(".5");
public static BigDecimal aZero = new BigDecimal("2.0");
public static void main(String [] args)
{
System.out.println("Feigenbaum constant calculation (using superstable points):");
System.out.println("j\t\ta\t\t\td");
System.out.println("-----------------------------------------------------");
int n = 20;
if (FindFirstTwo())
{
FindRoots(n);
}
}
public static BigDecimal F(BigDecimal a, BigDecimal x)
{
BigDecimal temp = new BigDecimal("1");
temp = temp.subtract(x);
BigDecimal ans = (a.multiply(x.multiply(temp)));
return ans;
}
public static BigDecimal Dfdx(BigDecimal a, BigDecimal x)
{
BigDecimal ans = (a.subtract(x.multiply(a.multiply(new BigDecimal("2")))));
return ans;
}
public static BigDecimal Dfda(BigDecimal x)
{
BigDecimal temp = new BigDecimal("1");
temp = temp.subtract(x);
BigDecimal ans = (x.multiply(temp));
return ans;
}
public static BigDecimal NewtonStep(BigDecimal a, BigDecimal x, int n)
{
// This function returns the Newton step for finding the root, a,
// of fn(x,a) - x = 0 for a fixed x = X
BigDecimal fval = F(a, x);
BigDecimal dval = Dfda(x);
for (int i = 1; i < n; i++)
{
dval = Dfda(fval).add(Dfdx(a, fval).multiply(dval));
fval = F(a, fval);
}
BigDecimal ans = fval.subtract(x);
ans = ans.divide(dval, MathContext.DECIMAL64);
ans = ans.negate();
return ans;
}
public static BigDecimal Root(BigDecimal a0, int n)
{
// Find the root a of fn(x,a) - x = 0 for fixed x = X
// with Newton’s method. The initial guess is a0.
//
// On return iter is the number of iterations if
// the root was found. If not, iter is -1.
BigDecimal a = a0;
BigDecimal a_old = a0;
BigDecimal ans;
// First iter1 iterations with a stricter criterion,
// tol1 < tol2
for (iter = 0; iter < iter1; iter++)
{
a = a.add(NewtonStep(a, x0, n));
// check for convergence
BigDecimal temp = a.subtract(a_old);
temp = temp.divide(a_old, MathContext.DECIMAL64);
ans = temp.abs();
if (ans.compareTo(tol1) < 0)
{
return a;
}
a_old = a;
}
// If this doesn't work, do another iter2 iterations
// with the larger tolerance tol2
for (; iter < (iter1 + iter2); iter++)
{
a = a.add(NewtonStep(a, x0, n));
// check for convergence
BigDecimal temp = a.subtract(a_old);
temp = temp.divide(a_old, MathContext.DECIMAL64);
ans = temp.abs();
if (ans.compareTo(tol2) < 0)
{
return a;
}
a_old = a;
}
BigDecimal temp2 = a.subtract(a_old);
temp2 = temp2.divide(a_old, MathContext.DECIMAL64);
ans = temp2.abs();
// If not out at this point, iterations did not converge
System.out.println("Error: Iterations did not converge,");
System.out.println("residual = " + ans.toString());
iter = -1;
return a;
}
public static boolean FindFirstTwo()
{
BigDecimal guess = aZero;
BigDecimal r0;
BigDecimal r1;
while (true)
{
r0 = Root(guess, 1);
r1 = Root(guess, 2);
if (iter == -1)
{
System.out.println("Error: Unable to find first two superstable orbits");
return false;
}
BigDecimal temp = r0.add(tol1.multiply(new BigDecimal ("2")));
if (temp.compareTo(r1) < 0)
{
System.out.println("1\t\t" + r0.doubleValue() + "\t\t\tN/A");
System.out.println("2\t" + r1.doubleValue() + "\t\tN/A");
r[0] = r0;
r[1] = r1;
return true;
}
guess = guess.add(step);
}
}
public static void FindRoots(int n)
{
int n1 = 4;
BigDecimal delta = new BigDecimal(4.0);
BigDecimal guess;
for (int i = 2; i < n; i++)
{
// Computation
BigDecimal temp = (r[i-1].subtract(r[i-2])).divide(delta, MathContext.DECIMAL64);
guess = r[i-1].add(temp);
r[i] = Root(guess, n1);
BigDecimal temp2 = r[i-1].subtract(r[i-2]);
BigDecimal temp3 = r[i].subtract(r[i-1]);
delta = temp2.divide(temp3, MathContext.DECIMAL64);
// Output
System.out.println(n1 + "\t" + r[i].doubleValue() + "\t" + delta.doubleValue());
// Step to next superstable orbit
n1 = n1 * 2;
}
}
}
EDIT:
Phil Steitz's Answer essentially solved my problem. I looked at some thread dumps, and after doing a bit of research to try and understand them, and compiling my program with debugging info, I was able to find that the main thread was stalling at the line:
dval = Dfda(fval).add(Dfdx(a, fval).multiply(dval));
as Phil Steit's said, by using
MathContext.DECIMAL128
in not only this line:
dval = Dfda(fval).add(Dfdx(a, fval).multiply(dval));
but also in my multiplication operations in the methods F, Dfda, and Dfdx, I was able to get my code to work properly.
I used DECIMAL128 because the smaller precision made the calculation non-functional, because I compare them to such low numbers for the tolerance check.
I think that what is going on here is that when n is larger than about 10, your NewtonStep method becomes very slow because none of your multiply invocations limit the scale by providing a MathContext. When no MathContext is provided, the result of a multiply gets the sum of the scales of the multiplicands. With the code above, the scales of dval and fval inside the for loop in NewtonStep get very large for large n, resulting in very slow multiplications in this method and the methods that it calls. Try specifying MathContext.DECIMAL64 (or something else) in the multiply activations as you do for the divides.
Trying to calculate (a+b)^n where n is a real value in a BigDecimal variable, but BigDecimal.pow is designed for accept only integer values.
If the input is within the magnitude range supported by double, and you do not need more than 15 significant digits in the result, convert (a+b) and n to double, use Math.pow, and convert the result back to BigDecimal.
As long as you are just using an integer for the exponent, you can use a simple loop to calculate x^y:
public static BigDecimal pow(BigDecimal x, BigInteger y) {
if(y.compareTo(BigInteger.ZERO)==0) return BigDecimal.valueOf(1); //If the exponent is 0 then return 1 because x^0=1
BigDecimal out = x;
BigInteger i = BigInteger.valueOf(1);
while(i.compareTo(y.abs())<0) { //for(BigDecimal i = 1; i<y.abs(); i++) but in BigDecimal form
out = out.multiply(x);
i = i.add(BigInteger.ONE);
}
if(y.compareTo(BigInteger.ZERO)>0) {
return out;
} else if(y.compareTo(BigInteger.ZERO))<0) {
return BigDecimal.ONE.divide(out, MathContext.DECIMAL128); //Just so that it doesn't throw an error of non-terminating decimal expansion (ie. 1.333333333...)
} else return null; //In case something goes wrong
}
or for a BigDecimal x and y:
public static BigDecimal powD(BigDecimal x, BigDecimal y) {
return pow(x, y.toBigInteger());
}
Hope this helps!