简体   繁体   中英

Efficient Prime Number Computing Program Java

So I was trying to make a prime number calculator in java that goes from 1 up to the upper limit of the range given by the user and prints out all of the primes.

One way of approaching this was by simply using a nested for loop and testing divisibility for every number in the range by all of the numbers less than it.

However, I figured it would be more efficient to only test prime numbers to avoid repeated factors and speed up the program.

For example, if the number we were on was 16, rather than testing if it's divisible by 2,3,4,5,...14,15,16, I could only test out 2,3,5,7,11,13 and stop once a factor is found.

So I tried to make an array to store all of the primes found so far and only use those values to test for the next number.

Here's my code, I can't figure out why it's not working

    Scanner sc = new Scanner (System.in);
    System.out.print ("Enter the upper limit: ");
    int high = sc.nextInt();

    boolean isPrime = true;
    int[] primearray = new int[0];

    for (int num = 1; num <= high; num++)
    {
        for (int x: primearray)
        {
            if (num%x==0)
            {
                isPrime = false;
                break;
            }
        }
        if (isPrime == true) {
            int size = primearray.length;
            primearray = new int[size+1];
            primearray [size] = num;

            System.out.println (num);
        }
    }

First of all, 1 isn't prime. By starting your outer loop at 1, your prime array ends up with 1 in it and everything else will then test as not prime. Start your outer loop at int num = 2 .

Second, you aren't copying over the existing known primes when you expand primearray . You can use

primearray = Arrays.copyOf(primearray, size+1);

which will make a new array with all the old contents copied and space for one more value.

Finally, you might want to check out the Sieve of Eratosthenes . A careful implementation of that algorithm will be more efficient than your current algorithm, which requires an expensive array reallocation every time you find a prime. You can use a BitSet to keep track of the flags that the sieve needs.

You have wrong in logic

boolean isPrime = true;

this variable should be declared in for loop, let 's imagine, if you find out 4 is not prime then isPrime = false , then you check 5 but there is not any code block that set isPrime = true .

And this block:

if (isPrime == true) {
    int size = primearray.length;
    primearray = new int[size+1];
    primearray [size] = num;

    System.out.println (num);
}

You created new array of prime number, primearray with size increased by 1, so primearray does not contain any old prime number, that will make wrong while checking prime. So you need to copy old prime numbers to new array.

And because the prime numbers start by 2, so your code should be:

Scanner sc = new Scanner(System.in);
System.out.print("Enter the upper limit: ");
int high = sc.nextInt();

int[] primeArray = new int[0];
for (int num = 2; num <= high; num++)
{
    boolean isPrime = true;
    for (int x : primeArray)
    {
        if (num % x == 0)
        {
            isPrime = false;
            break;
        }
    }

    if (isPrime == true)
    {
        primeArray = Arrays.copyOf(primeArray, primeArray.length + 1);
        primeArray[primeArray.length - 1] = num;
        System.out.println(num);
    }
}

You should use a Sieve of Eratosthenes to find primes, rather than testing each number for divisibility; that method is far slower than sieving. Here is an implementation of sieving from my blog :

public static LinkedList sieve(int n)
{
    BitSet b = new BitSet(n);
    LinkedList ps = new LinkedList();

    b.set(0,n);

    for (int p=2; p<n; p++)
    {
        if (b.get(p))
        {
            ps.add(p);
            for (int i=p+p; i<n; i+=p)
            {
                b.clear(i);
            }
        }
    }

    return ps;
}

As you say the key simplification is to only test primes when you find the next prime. For example:

public class PrimeGenerator {
    private long current = 1;
    private final List<Long> primes = new ArrayList<>();

    public long next() {
        do {
            current++;
        } while (primes.stream().anyMatch(n -> current % n == 0));
        primes.add(current);
        return current;
    }

    public LongStream stream() {
        return LongStream.generate(this::next);
    }
}

This records each prime as it is generated.

You can generate get all primes to a certain value with

generator.stream().takeWhile(p -> p < value)...

Previous answers already explains what is wrong with your code.

I just want to share another approach which is more efficient. The sample implementation is as per below. Basically, once we know x is prime number, we also know i*x is NOT a prime number. Further reading and visualization is available here

public int countPrimes(int n) {
            if (n == 0 || n == 1) return 0;

    int count = 0;
    boolean[] check = new boolean[n+1];

    for (int i = 2; i < n; i++) {
        if (check[i]) continue;

        for (int j = 1; j <= n / i; j++) {
            check[j * i] = true;
        }

        count++;
    }

    return count;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM