开发者

Computing prime numbers up to N integers

I am trying to write a little script myself to compute all of the prime numbers up to n (a user submitted argument) and would appreciate a little bit of help. I want to use ArrayLists to write this function, and hopefully make it as efficient as possible - another big thing for me that I can't seem to grasp is doing everything as is standard and good practice (i.e having classes in capital letters, etc) so if you wouldn't mind please point out any mistakes in that regard so I can fix them.

Here is the code I have written thus far, but I know there are many ways of optimising it - specifically, I have a boolean array storing whether a number is prime or not. Surely there is a better way of doing this than cycling through the array and making everything prime, then getting rid of the numbers that are not ?

Thanks a lot for your time ! :)

(tl;dr - Writing a script to computer prime numbers up to N. I wish to use ArrayLists to do this. How can I make my code better - specifically the inefficient boolean array).

import java.util.*;
public class Prime {

  public static void main( String[] args ){

  }
  public static void prime( int initialCapacity){
    int index=0;
    ArrayList<Integer> listOfPrimeNumbers = new ArrayList<Integer>( );
    boolean[] isPrimeNumber = new boolean[initialCapacity + 1]; // 开发者_如何学Goboolean defaults to
    // false
    while ( index <= listOfPrimeNumbers.size() )
    {
      isPrimeNumber[index] = true;
      if (isPrimeNumber[index]) {
        listOfPrimeNumbers.add(index);
      }
      for (int j = index; j * index <= initialCapacity; j++) {
        isPrimeNumber[index * j] = false;
      }

      // Now mark the multiple of i as non-prime number
      index++;
    }
  }

}


You can set the initial capacity of listOfPrimeNumbers, because you can estimate how many prime numbers are under N. See

http://en.wikipedia.org/wiki/Prime_number_theorem

but basically n / (ln n) should be the initial capacity of the listOfPrimeNumbers. This will ensure that your program does not constantly resize the list under the covers, which can be expensive.

That is, if you really want to be efficient. If you dont care, then just set the initial capacity of that list to something higher. Right now you have it set to the default, which means your listOfPrimeNumbers will have expand.

EDIT - I think you have an error -- the line

isPrimeNumber[index] = true;

should only be executed if index is a prime number, so move it into the appropriate if statement. Again, I don't know how your stuff works, but I think this is an issue.

An alternative would be to have a Map as your isPrimeNumber checker.


I want to use ArrayLists to write this function, and hopefully make it as efficient as possible.

Actually, a boolean[] will be more efficient for this application than an ArrayList<Boolean>. An array of booleans occupies less space and operations are faster. (An ArrayList is better if you require a List or if you need an array-like data structure that can grow as you add elements to it.)

another big thing for me that I can't seem to grasp is doing everything as is standard and good practice (i.e having classes in capital letters, etc) so if you wouldn't mind please point out any mistakes in that regard so I can fix them.

I can do better than that. Here is a link to the original Sun Java Style Guide.

Surely there is a better way of doing this than cycling through the array and making everything prime, then getting rid of the numbers that are not?

I suppose that you could change the array to be an array of "not primes", and rely on the fact that an array of boolean is initialized to all false. But that makes the code a bit contorted.

You could also replace the boolean[] with int[] and use shifting / masking to set / clear individual bits. This will allow you to sieve more primes with the same amount of memory, but it won't necessarily make the program run faster.


This answer is split into 2 parts: your prime solver (number sieve) and the array which contains your numbers.

Solver: The primary area of optimization that you will need to focus on is your "number sieve". This is the method by which you determine that a number is prime, itself. It is currently not possible to turn your sieve into a single expression to determine if it is prime, so you must work toward quick elimination. The reason for this is because division and multiplication are two of the most expensive arithmetic functions you can perform. Eliminating the need to do this wherever possible will save you the most time. So below are some basic rules of primes (some may seem obvious).

  • A number can not be prime if it is even (divisible by 2). The fastest way to check this is:
    if ((i && 1) == 1) //prime candidate
  • A number is prime if it is not divisible by any previous primes. In other words, you only have to check by using the primes you have already found.
    for (currentPrime in listOfPrimes) {
    if (i mod currentPrime == 0) {
        //not Prime: Exit For loop
    }
    } 
  • You only have to check previous primes up 1/3 of the current i you are checking (you have already removed divisible by 2)
  • Of course, there are some other little optimizations, but these are the bulk of them (currently). Here is the adjusted for loop from above.
    if ((i && 1) == 1) {
    threshHold = (int)(i / 3); //So you don't have to keep recalcing
    isPrime = true; //Assume true until told otherwise
    for (currentPrime in listOfPrimes) {
        if ((i mod currentPrime) == 0) {
            isPrime = false; //Flag as non prime
            break; //Exit For Loop early
        }
        if (currentPrime > threshHold) {
            break;
        }
    }
    if (isPrime) {
        //Record new Prime
    }
    }

ArrayList: In regard to your ArrayList, the most efficient way to optimize this is to not store information that you do not need. Since (according to your description) you are looking for primes, and not nonprimes, storing a boolean ArrayList to determine if it is prime is not your best bet.

It would be much more efficient to only store the primes that you find, as anything not in the list is definitely a non-prime. After all, a number is either prime or it isn't, so if in list then it's prime and if not, then it's not. This is particularly true since your number sieve should only be checking vs. previously determined primes, anyway. This results in less writes to the ArrayList and fewer elements, as a whole. Furthermore, your enforced inference of storing only primes makes your boolean value always equal true meaning there is no need to check it.

My recommendation would be thus: change your ArrayList to integer (or long) and use this for both verification and output since it serves both purposes. There are a number of efficient ways to adjust your output based on this, so this does not limit you in any way. Additional functions to determine if a given number is prime (given your list) then is a simple matter, even given the ArrayList.

FuzzicalLogic


A silly solution.

boolean[] notPrime = new boolean[n];
for (int i = 2; i <= n; i++) {
    for (int j = i * 2; j <= n; j+=i) {
        notPrime[j-1] = true;
    }
}
ArrayList<Integer> primes = new ArrayList<Integer>();
for (int i = 0; i < n; i++) {
    if (!notPrime[i]) {
        primes.add(i+1);
    }
}


That doesn't look like it should work properly. You're telling it that every number is a prime number with isPrimeNumber[index] = true then checking to see if isPrimeNumber[index] is true; obviously it's going to be since you just set it to true.

Still, the algorithm you are trying to use is the one I was going to suggest before I realized what you were doing; it's good and very simple. You can optimize it to be more efficient though; no need for multiplication.

Here's some pseudocode to help you out.

make a new int[initialCapacity] // or boolean[]

for i = 0 to initialCapacity-1
    array[i] = 1 // or = i, or whatever, even i+2 and you could start iterating next loop at 0
/* optionally, you can skip the above step and rely on the fact that all values will
    default to 0/false, and you can treat 0/false as prime and >0/true as not prime */

for i = 2 to initialCapacity-1
    if array[i] = 0 continue
    i is prime, do something with it here
    for j = i+i to initialCapacity-1 step i
        array[i] = 0

This uses addition instead of multiplication to traverse the array. The main difference here is that this checks to see if the current number is prime then acts on it instead of saying "this is prime, now that I've declared it prime have I said it's prime? Now treat it as prime and reduce the set further regardless" Also, you need to make sure you don't reduce the set on 1, which is why you skip over 1 and start at 2.


Your current solution looks very broken and if I'm not mistaken will go in an infinite loop. You can easily fix it by moving the line isPrimeNumber[index] = true; outside the while loop (see [1] below), i.e. initialising everything but 0 and 1 to true in the beginning, and starting with index=2, otherwise you're going to set everything to false since everything is a multiple of 1. What you have implented is called the Sieve of Eratosthenes. You can get slightly more efficient with the Sieve of Atkins, but it's probably outside your reach.

[1] To explain what I meant by moving the line outside the while loop (I'm also changing to the more suitable for loop at the same time):

for (int i = 0; i <= initialCapacity; i++)
  isPrimeNumber[i] = true;
for (int index = 0; index <= initialCapacity; index++) {
  if (isPrimeNumber[index]) {
  // rest of code here...
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜