1

In order to solve a question I have to generate a list of prime numbers from 1 to 3000000, so I tried several ways to do this and unfortunately all failed...

First try: because all prime numbers bigger than 2 are odd numbers, so I first generate a list of odd numbers started with 3 called allOddNums. And then I generate a list of all composite numbers called allComposite. Then I remove all the number in allComposite from allOddNums to obtain prime numbers. Here is my code:

/** Prime Numbers Generation
  * Tony
  */
import java.util.*;

public class PrimeNumG {
  public static void main(String[] args) {

    List <Long> allOddNums = new ArrayList<Long>();
    for (long i = 3; i < 200; i += 2) {
      allOddNums.add(i);
    }

    // composite number generator:
    List <Long> allComposite = new ArrayList<Long>();
    for (long a = 2; a < Math.round(Math.sqrt(3000000)); a += 2) {
      for (long b = 2; b < Math.round(Math.sqrt(3000000)); b += 2) {
        allComposite.add(a*b);
      }
    }

    // remove duplicated:
    Set <Long> hs = new HashSet<Long>();
    hs.addAll(allComposite);
    allComposite.clear();
    allComposite.addAll(hs);

    // remove all composite from allRealNums = allPrime
    allOddNums.removeAll(allComposite);
    allOddNums.add(0, (long)2);

    System.out.printf("%s ", allOddNums);
    Scanner sc = new Scanner(System.in);
    int times = sc.nextInt();

    for (int i = 0; i < times; i++) {
      int index = sc.nextInt();
      System.out.print(allOddNums.get(index) + " ");

    }
  }
}

In this case, when I need to generate a few prime numbers it works fine. However, if I want to generate until 3000000 it fails me(used up memory).

Second try: I searched online and find an algorithm called sieve of Eratosthenes. then I first generate 2, 3, 5, 7, 9...(all odd numbers + 2), then I remove every 3rd number after 3 and every 5th number after 5. The code is as below:

/** Prime Number Generator
  * Tony
  */   
import java.util.*;

public class Solution61 {
  public static void main(String[] args) {
    List<Long> l1 = new ArrayList<Long> ();

    // l1 generator:   3 5 7 9 11 ... 
    for (long d = 3; d < 100; d += 2) {
      l1.add(d);
    }

    l1.add(1, (long)2); // 2 3 5 ...

    removeThird(l1); // rm 3rd after 3

    removeFifth(l1); // rm 5th after 5, now the l1 will be prime number

    Scanner sc = new Scanner(System.in);
    int times = sc.nextInt();
    for (int i = 0; i < times; i++) {
      int index = sc.nextInt();
      System.out.print(l1.get(index) + " ");


    }
  }


  /** removeThird : remove every 3rd number after 3
    * param List | return void
    */
  private static void removeThird(List<Long> l) {

    int i = 1;
    int count = 0;
    while (true) {


      if (count == 3) {
        l.remove(i);
        count = 1;

      }
      i ++;
      count ++;
      if (i > l.size()) {
        break;
      }
    }
  }

  /** removeThird : remove every 5th number after 5
    * param List | return void
    */
  private static void removeFifth(List<Long> l) {

    int i = 2;
    int count = 0;
    while (true) {


      if (count == 5) {
        l.remove(i);
        count = 1;
      }
      i ++;
      count ++;
      if (i > l.size()) {
        break;
      }
    }
  }

}

This is still not up to the task because it also runs out of memory.

3rd try: I tried to generate from 1 to the 3000000, and then remove every number is the product of prime number and another number. The code is as below:

/** print all the prime numbers less than N
  * Tony
  */

public class primeGenerator {
  public static void main(String[] args) {
    int n = 3000000;
    boolean[] isPrime = new boolean[n];
    isPrime[0] = false; // because 1 is not a prime number

    for (int i = 1; i < n; i++) {
      isPrime[i] = true;
    } // we set 2,3,4,5,6...to true

    // the real number is always (the index of boolean + 1)

    for (int i = 2; i <= n; i++) {
      if (isPrime[i-1]) {
        System.out.println(i);
        for (int j = i * i; j < n; j += i /* because j is determined by i, so the third parameter doesn't mater*/) {
          isPrime[j-1] = false;
        }
      }
    }
  }
}

it still fails me, well guess 3000000 is really a big number huh? Is there any simple and brilliant rookie-friendly way to generate prime numbers below 3000000? Thx!

fourth try: @jsheeran Is this code below what your answer means? when I hit 1093 it gets slower and slower and my IDE still crashed. Plz tell me if I misinterprete your approach, thx!

/** new approach to find prime numbers
  * Tony
  */
import java.util.*;

public class PrimeG {

  /** isPrime
    * To determine whether a number is prime by dividing the candidate number by each prime in that list
    */
  static List<Long> primes = new ArrayList<Long> ();

  private static void isPrime(long n) {
    boolean condition = true;
    for (int i = 0; i < primes.size(); i++) {
      if (n % primes.get(i) == 0) {
        condition = condition && false;
      }
    }
    if (condition) {
      findNextPrime(n);
    }
  }

  /** findNextPrime
    * expand the list of prime numbers 
    */
  private static void findNextPrime(long n) {
    primes.add(n);
  }





  public static void main(String[] args) {
    primes.add((long)2);
    primes.add((long)3);
    primes.add((long)5);
    primes.add((long)7);

    for (int i = 8; i < 3000000; i++) {
      isPrime(i);
      System.out.printf("%s", primes);
    }


  }
}
Tony Chen
  • 183
  • 1
  • 16
  • 3
    Yes there is. See https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes. – Bathsheba Sep 23 '16 at 08:10
  • 1
    " well guess 3000000 is really a big number huh" Not really. The Sieve of Eratosthenes should handle this trivially. – Andy Turner Sep 23 '16 at 08:10
  • Oh! I know that. That's the way I used on the second and third try but it still runs out of memory. Could you look at my code in the second and third try and tell me what went wrong? Plz! – Tony Chen Sep 23 '16 at 08:14
  • `int j = i * i` => `int j = 2 * i`, for a start. – Andy Turner Sep 23 '16 at 08:15
  • The `primeGenerator` method will not run out of memory, provided it can allocate the `isPrime` array: no memory is allocated thereafter. – Andy Turner Sep 23 '16 at 08:18
  • @AndyTurner yeah I thought i*i is more efficient seems it caused the index out of boundary problem. but even so, the code is print numbers for a while and then it gets really really slow. When it hits around 92699, it stops working and I have to force quit my IDE. – Tony Chen Sep 23 '16 at 08:23
  • That's because `92699*92699 == 8593104601`, which is greater than `Integer.MAX_VALUE`. Actually, it'd have stopped working after `46340 (== floor(sqrt(Integer.MAX_VALUE)))`. – Andy Turner Sep 23 '16 at 08:24
  • I mean after I changed `int j = i * i` into `int j = 2 * i` I still have to force quit my IDE. – Tony Chen Sep 23 '16 at 08:27
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/124012/discussion-between-tony-chen-and-andy-turner). – Tony Chen Sep 23 '16 at 08:29

4 Answers4

3

Fixed implementation of Sieve of Eratosthenes (your third try). I believe it should satisfy your needs.

public static void main (String[] args) throws java.lang.Exception {
    int n = 3000000;

    boolean[] isPrime = new boolean[n+1];
    for (int i = 2; i <= n; i++) {
        isPrime[i] = true;
    }

    for (int factor = 2; factor*factor <= n; factor++) {
        if (isPrime[factor]) {
            for (int j = factor; factor*j <= n; j++) {
                isPrime[factor*j] = false;
            }
        }
    }

    for (int i = 2; i <= n; i++) {
        if (isPrime[i]) System.out.println(i);
    }
}
siarheib
  • 159
  • 1
  • 8
  • 1
    Also consider using a [BitSet](https://docs.oracle.com/javase/7/docs/api/java/util/BitSet.html) if memory is an issue. – jsheeran Sep 23 '16 at 08:46
  • Um... I don't know... it still get slower and slower, then I have to force quit the IDE. – Tony Chen Sep 23 '16 at 21:30
2

An alternative approach would be to begin with a list of primes consisting of 2 and 3. Have a method isPrime(int) to determine whether a number is prime by dividing the candidate number by each prime in that list. Define another method, findNextPrime(), which isPrime() can call to expand the list as needed. This approach has far lower overhead than maintaining lists of all odd and composite numbers.

jsheeran
  • 2,912
  • 2
  • 17
  • 32
1

Memory is not an issue in your case. Array of size n = 3000000 can be defined inside the stack frame of a function. Actually array of size 10^8 can be defined safely inside a function. If you need more than that define it as a gloabal variable(Instance variable). Coming to your code there is an IndexOutOfBoundsException in your third code. You need to check for factors of a number only uptill sqrt(n). Factors exist in pairs one factor <=sqrt(n) and other >=sqrt(n). So you can optimize the sieve of Eratosthenes algorithm. Here is a link to one wonderful tutorial on various optimizations of sieve.

RamenChef
  • 5,557
  • 11
  • 31
  • 43
amit
  • 11
  • 2
  • It would be helpful to your answer if you included some code fixing the problems in the sieve implementation or provided a brief example of the improved algorithm you suggest. – Nat Dempkowski Sep 23 '16 at 16:59
0

This can generate prime numbers up to Integer.MAX_VALUE in few milliseconds. It also doesn't take as much memory as in Sieve of Eratosthenes approach.

public class Prime {

  public static IntStream generate(int limit) {
    return IntStream.range(2, Integer.MAX_VALUE).filter(Prime::isPrime).limit(limit);
  }

  private static boolean isPrime(int n) {
    return IntStream.rangeClosed(2, (int) Math.sqrt(n)).noneMatch(i -> n % i == 0);
  }
}
Gayan Weerakutti
  • 11,904
  • 2
  • 71
  • 68