3

I am working on a factorisation problem and for small numbers it is working well. I've been able to calculate the factors (getting the answers from Wolfram Alpha) for small numbers, like the one on the Wikipedia page (5959).

Along with the Wikipedia page I have been following this guide. Once again, as my Math knowledge is pretty poor I cannot follow what I need to do next.

EDIT: It finally works! I'll post the working code up here once I've got it fully working so that others in my predicament can learn from it.

Mike B
  • 12,768
  • 20
  • 83
  • 109
  • 1
    How is it not working? What is it doing that is "wrong"? Wrong result, exception, etc? – aperkins Apr 21 '10 at 20:19
  • Are you sure it works for `5959 = 101 * 59`? – trashgod Apr 21 '10 at 20:21
  • Sorry, I missed that bit out. It's returning the wrong values when dealing with longer numbers. I followed the Wikipedia algorithm and whilst the values for a, b, a2 and b2 displayed the same results for longer numbers it displays different results to those provided by Wolfram Alpha. – Mike B Apr 21 '10 at 20:22
  • trashgod: Well, the program states that 6400 - 5959 = 441, the results it displays when 5959 is ran through. As according to Wikipedia a^2 - N = b^2 the program works for that N. – Mike B Apr 21 '10 at 20:25
  • @Ender: My mistake, `5959 = 101 * 59 = (80 + 21) * (80 - 21)`. – trashgod Apr 21 '10 at 20:38
  • trashgod: Ah, you're absolutely right. I've changed the code to reflect the actual factors. – Mike B Apr 21 '10 at 20:50

3 Answers3

2

That getIntSqrt() method... I don't know if it's ok, but it looks awful (convert the BigInteger to String ??) Have you checked it?

Here is a (apparently) better method.

Community
  • 1
  • 1
leonbloy
  • 73,180
  • 20
  • 142
  • 190
  • It works well, but I've taken it out anyway and replaced it with the one suggested. Despite this the program is still displaying faulty results. – Mike B Apr 21 '10 at 20:36
1

Your isSqrt() function isn't correct for what you're trying to do. You want to know if n = root^2 exactly, but your IsSqrt() function is written to return "true" if n merely lies in the interval (root^2, (root+1)^2).

I think all you need to do is to check that n equals root.pow(2).

Jim Lewis
  • 43,505
  • 7
  • 82
  • 96
  • I'm not sure if I follow you correctly. Do you mean that I should scrap my isSqrt() method and just check if n is equal to root.pow(2)? – Mike B Apr 21 '10 at 20:41
  • I've added that change, but the code never enters the while loop. – Mike B Apr 21 '10 at 20:53
1

In your loop:

// while b2 not square
while(!(isSqrt(b2, root))) {

what is the purpose of the following instruction?

    root = root.add(b2.divide(root)).divide(TWO);

I think that in order to check that b2 is a square, you should instead try to compute the square root (the above instruction is just one step of the canonical algorithm to compute square roots), with the method you already have:

    root = getIntSqrt(b2);

The same observation applies to this code:

// ??
final int bitLength = N.bitLength();
BigInteger root = BigInteger.ONE.shiftLeft(bitLength / 2);
root = root.add(b2.divide(root)).divide(TWO);

EDIT. The problem is that your sqrt() method needs an isSqrt() that checks whether root is an approximate root of n, whereas the loop in fermat() needs an exact check. I append some code that seems to pass your last test:

import java.math.BigInteger;

public class Fermat {

private BigInteger a, b, N;
private static final BigInteger TWO = BigInteger.valueOf(2);

private static boolean isApproximateSqrt(BigInteger n, BigInteger root) {
    final BigInteger lowerBound = root.pow(2);
    final BigInteger upperBound = root.add(BigInteger.ONE).pow(2);
    return lowerBound.compareTo(n) <= 0
        && n.compareTo(upperBound) < 0;
}

private static BigInteger intSqrt(BigInteger n) {
    if (n.signum() >= 0) {
        final int bitLength = n.bitLength();
        BigInteger root = BigInteger.ONE.shiftLeft(bitLength / 2);

        while ( ! isApproximateSqrt(n, root) ) {
            root = root.add(n.divide(root)).divide(TWO);
        }
        return root;
    } else {
        throw new ArithmeticException("square root of negative number");
    }
}

private void init() {
    a = intSqrt(N);                             // a <- ceil(sqrt(N))
    BigInteger b2, root;
    do {
        a = a.add(BigInteger.ONE);              // a <- a + 1
        b2 = (a.multiply(a)).subtract(N);       // b2 <- (a * a) - N
        root = intSqrt(b2);
    } while( root.pow(2).compareTo(b2) != 0 );  // while b2 not exact sqrt
    b = root;
}

public void print() {
    BigInteger a2 = a.pow(2);
    BigInteger b2 = b.pow(2);
    BigInteger squareDifference = a2.subtract(b2);
    System.out.println("A: " + a + "\nB: " + b);
    System.out.println("A^2: " + a2 + "\nB^2: " + b2);
    System.out.println("N: " + N);
    if(squareDifference.compareTo(N) != 0) {
        System.out.println("Something is wrong....");
    }
}

public Fermat(BigInteger aNumber) {
    N = aNumber;
    init();
}

public static void main(String[] args) {
    Fermat f = new Fermat(new BigInteger("90283"));
    f.print();
}
}
Federico A. Ramponi
  • 46,145
  • 29
  • 109
  • 133
  • I've changed it in the code, but now the while loop lets squares fall through. – Mike B Apr 21 '10 at 20:44
  • The program now outputs A^2 = 158404 and B^2 = 68121. Finally 158404 - 68121 = 90283. – Federico A. Ramponi Apr 21 '10 at 21:10
  • It works! Every test case I've tried appears to work so I'll accept this answer for now unless I find any problems. Thank you so much for your help! – Mike B Apr 21 '10 at 21:17
  • It looks fantastic, and thanks for clearing that up. The code works perfectly within the application I'm developing (a edutainment game). – Mike B Apr 22 '10 at 20:29