Question

I am currently writing a function to find the square root of a given BigInteger. The current number in my test file is 250074134890485729738. The program however always stalls while finding the sqrt at 15813732488, which squared is 250074135202026670144. I have copied this code from another StackOverflow problem, and it ceases converging at the same number. It uses Newtons Method, while I'm using the Babylonian/Heron's Method.

Their Code:

    public static BigInteger sqrtN(BigInteger in) {
final BigInteger TWO = BigInteger.valueOf(2);
int c;

// Significantly speed-up algorithm by proper select of initial approximation
// As square root has 2 times less digits as original value
// we can start with 2^(length of N1 / 2)
BigInteger n0 = TWO.pow(in.bitLength() / 2);
// Value of approximate value on previous step
BigInteger np = in;

do {
    // next approximation step: n0 = (n0 + in/n0) / 2
    n0 = n0.add(in.divide(n0)).divide(TWO);

    // compare current approximation with previous step
    c = np.compareTo(n0);

    // save value as previous approximation
    np = n0;

    // finish when previous step is equal to current
}  while (c != 0);

return n0;}

My Code:

    static BigInteger number;
static BigInteger sqrt;

public static void main(String[] args) throws Exception {

    number = new BigInteger(getFile());
    System.out.println("Factoring: \n\n" + number);
    sqrt = sqrt();
    System.out.println("The root is: " + sqrt.toString());
    System.out.println("Test, should equal nearest square at or above original number: " + sqrt.multiply(sqrt).toString() + "\nOriginal number: " + number.toString());
}
public static BigInteger sqrt() {
   BigInteger guess = number.divide(new BigInteger("500"));
   BigInteger TWO = new BigInteger("2");
   BigInteger HUNDRED = new BigInteger("100");
   boolean go = true;
   while (number.subtract((guess.multiply(guess))).abs().compareTo(HUNDRED) ==  1 &&      go){
       BigInteger numOne = guess.divide(TWO);
       BigInteger numTwo = number.divide(guess.multiply(TWO));
       guess = numOne.add(numTwo);
       if (numOne.equals(numTwo))
           go = false;
       System.out.println(guess.toString());
   }


    return guess.add(BigInteger.ONE);

My Output:

Factoring:

250074134890485729738
250074134890485979
125037067445243488
62518533722622743
31259266861313370
15629633430660684
7814816715338341
3907408357685169
1953704178874583
976852089501290
488426044878644
244213022695321
122106511859659
61053256953828
30526630524913
15263319358455
7631667871224
3815850319588
1907957927606
954044498302
477153309146
238838702566
119942872245
61013907968
32556274730
20118781556
16274333124
15820250501
15813733820
15813732478
15813732478
The root is: 15813732479
Test, should equal nearest square at or above original number: 250074134917379485441
Original number: 250074134890485729738

A couple notes:

  1. I had a couple of ideas while writing this and I tried them. If something doesn't match up, that's my fault. I did check, but I'm not perfect.
  2. While I appreciate people being generous enough to point me towards a different piece of pre-written code/post their own, this (while not school work) is a learning experience for me. PLEASE DO post how this code could be fixed, PLEASE DO NOT just post a different piece of code that does the same.

ANSWER: This actually does work as is, the original input is simply not a perfect square. Therefore, this works perfectly for my purposes. Thanks to all who wasted their time due to my incompetence. I have changed the code to return a value equivalent to (if Math.sqrt/ceil worked on BigInts):

    sqrt = Math.Ceil(Math.Sqrt(A_RANDOM_BIGINTEGER_HERE));

I have also removed unnecessary variables, and updated the output to match. Both these methods work fine, although the first one requires some code to catch the non-convergence cycle, in case any future visitors to this question wish to use them.

Was it helpful?

Solution 2

You have in your while loop in your sqrt() function a compareTo(100) which (I suspect) is always returning 1 ie the absolute value of number minus the guess squared is always greater than 100.

Which after testing I see that it is, add this at the end of your loop and you'll see that the difference once you reach the root is still very large = 4733709254

At this point numOne and numTwo become the same value so guess is always the same for each subsequent iteration also.

System.out.println("Squaring:" + guess.multiply(guess).toString() +
      "; Substracting: " + number.subtract((guess.multiply(guess))).toString());

You also have c < 100 so if that comparison is always true then it will always print 100 lines.

OTHER TIPS

15813732478 is the square root of 250074134890485729738, at least the integral part of it. The real square root is 15813732478.149670840219509075711, according to calc.

There are two problems:

  1. You are looping 100 times instead of stopping at convergence.
  2. Your assumption that sqrt(N)*sqrt(N) = N is fallacious, because you're only computing the integral part, so there will be an error proportional to N.
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top