Prime Numbers in Java - Algorithms

2020-04-12 09:45发布

问题:

I have started learning to code in Java and decided I would use the Project Euler site to give me little tasks to try and complete with each bit of new coding I learn. So I came across Problem 3:

The prime factors of 13195 are 5, 7, 13 and 29. What is the largest prime factor of the number 600851475143 ?

I thought about the problem and researched many different theories about prime numbers and how they can be found via various different calculations (Sieve of Eratosthenes being an example) and the solution I conjured up was to test numbers from 2 --> n and see if they were a prime number, if they were then I would divide the Tn variable (in this case 600851475143) by the newly discovered prime number and see if it was a factor. If it was, I would assign it to the variable Hp (Highest Prime number) and at the end of the program I would output Hp to the console to give my result.

Here is my code:

public class Largest_Prime_Factor_NEW_SOLUTION {

    static long Tn = 600851475143L;
    static long Hp = 0;
    static boolean isPrime = false;

    public static void main(String[] args) {

        for (long i=2; i<Tn; i++) {
            System.out.println("TESTING NUMBER " + i);
            for (long k=2; k < i; k++) {
                if (i % k == 0) {
                    System.out.println(i + " IS NOT A PRIME");
                    break;
                } else if (k + 1 == i) {
                    isPrime = true;
                }
            }

            if (isPrime) {
            System.out.println(i + " IS A PRIME");
            if (Tn % i == 0) {
                System.out.println(Tn + " IS DIVISIBLE BY " + i);
                Hp = i;
            } else {
                System.out.println(Tn + " IS NOT DIVISIBLE BY " + i);
            }
            }

            isPrime = false;
        }
        System.out.println("THE HIGHEST PRIME NUMBER OF " + Tn + " IS " + Hp);
    }
}

Now I know that this code is very inefficient and for just starting I have managed to condense it from where I began (there were loops everywhere!) but what I am is asking is, how can I improve this? It's eating away at me because everything I research contradicts what others would do and it's very confusing. I have tried the sieve method but i understand that a boolean array can only be an int array and never a long array?

I understand that when beginning to code I will be limited to what knowledge I can use, but just out of interest, I am keen to see what the final solution would be.

回答1:

What you can do is find the lowest divisor of Tn. Suppose that is p, find the lowest divisor again for Tn/p and so on.

Now, at every step p is prime[explanation below]. So collect them and they are the prime divisors of Tn.

For better time-complexity, you can check for divisors up to upto ceil(sqrt(Tn)) only, instead of Tn-1.

And when you start checking for prime divisor for Tn, you can start with 2. And once you get a prime divisor p don't start again from 2 for Tn/p. Because, Tn/p is also a divisor of Tn and since Tn does not have divisors less than p, Tn/p does not have it too. So start with p again[p can have multiple power in Tn]. If p does not divide Tn, move to p+1.

Example :

Tn = 45
1. start with 2. 2 does not divides 45.
2. next test is for 3. 45 is divisible by 3. So 3 is a prime divisor of it.
3. Now check prime divisors from 45/3 = 15, but start with 3. not from 2 again. 4. Well, 15 is divisible by 3. So start with 15/3 = 5 5. Note for 5, ceil(sqrt(5)) is 3. But 5 is not divisible by 3. But since 4 > ceil(sqrt(5)) and we can say 5 is a prime without any doubt.

So the prime divisor of 45 are 3 and 5.


Why smallest divisor(except 1) of a number is a prime ?

Suppose above statement is false. Then a number N has a smallest yet composite divisor, say C.

So C|N Now C is composite so, it has divisor less than itself but greater than one.
Say such a divisor of C is P.
So P|C , but we have C|N => P|N where 1 < P < C.

This contradicts our assumption that C is the smallest divisor of N, so smallest divisors of a number is always a prime.



回答2:

Thank you for all your help, after reading through the comments and answers I managed to condense the code much further to the following:

    public class Largest_Prime_Factor_NEW_SOLUTION_2 {

    static long Tn = 600851475143L;

    public static void main(String[] args) {

        for (long i = 2; i < Math.sqrt(Tn); i++) {

            if(Tn % i == 0) {
                Tn = Tn / i;
                i--;
            }   
        }
        System.out.println(Tn);
    }
}

and it works perfect! Thanks again for your help and time to help me understand. I understand it was more a mathematical problem than a coding problem, but it helped me understand a few things. I'm now off to learn something else :)



回答3:

Since you are doing this as a learning exercise, when you have improved you current program enough, why not try solving the same problem in a different way? The Fermat Factorization Method finds large factors first.



回答4:

There are many ways to improve a program like this, but the improvements have to do mostly with mathematics and not programming:

  • When looking for factors, check each number, not just primes. If you find a factor check if it's prime. You'll save yourself of many primality checks this way.

  • The greatest prime factor of a composite number can be at most the number's square root, so you can stop the iteration earlier.

  • Use a fast primality test instead of doing trial divisions http://en.wikipedia.org/wiki/Primality_test

Then again, this is a one-off. Don't overcomplicate it.



回答5:

A simple algorithm for factoring a composite number by trial division goes like this:

function factors(n)
    f, fs := 2, []
    while f * f <= n
        while n % f == 0
            fs.append(f)
            n := n / f
        f := f + 1
    if n > 1
        fs.append(n)
    return fs

That algorithm can be improved, and there are better algorithms for factoring large numbers, but it's sufficient for your task. When you are ready for more, I modestly recommend the essay Programming with Prime Numbers at my blog, which includes implementations of that algorithm and others in Java.



回答6:

This is java version of this:

 static boolean isPrime(int n){
    if (n == 2) return true;
    if (n == 3) return true;
    if (n % 2 == 0) return false;
    if (n % 3 == 0) return false;

    int i = 5;
    int w = 2;
    while (i * i <= n) {
        if(n % i == 0)
        return false;

        i += w;
        w = 6 - w;
    }
    return true;
}

As it is described by @Alexandru: It's a variant of the classic O(sqrt(N)) algorithm. It uses the fact that a prime (except 2 and 3) is of form 6k-1 and 6k+1 and looks only at divisors of this form.