Any easier way of finding prime numbers than this?

10,837

Solution 1

While this question has already been answered I figured I'd provide my answer anyway in the hopes that somebody may find it useful:

You seem to be primarily concerned with 2 both elegance and efficiency. I'd also like to point out that correctness is equally important. Unless you have a special requirement to treat the number 1 as prime it is no longer considered so. You should equally consider the scenario when the user enters a prime number. You should also give some thought into the boundry condition of what numbers you print. Specifically if I enter the number 7, will your users expect it to output 5,3,2,1 or 7,5,3,2,1. While my personal tendency would be towards the latter, using clear and concise messages can make either option work.

Elegance

The perceived lack of elegance in your solution is largely due to your combination of two concepts: Prime Number Testing and Prime Number Generation.

A Prime Number Test is a (quick) method to determine whether or not a single arbitrarily chosen number is prime. A Prime Number Generator is a way of generating a sequence of prime numbers which are often consecutive.

As your program demonstrates you can generate a consecutive sequence of prime numbers by testing each number within a given range and only selecting those which are prime! Keeping this as our basic strategy for the moment, let's figure out what the code might:

From our description earlier we said that a prime number test was a method (aka function) to determine if some arbitrarily chosen number was prime. So this method should take as input a(n arbitrarily chosen) number and return wether or not the given numbe was prime (ie: true/false). Let's see how it looks:

public interface PrimeNumberTest
{
    bool isPrime(int value);
}

And incorporating your prime number test

public class BruteForcePrimeNumberTester : PrimeNumberTest
{
    public bool isPrime(int value)
    {
        bool isPrime = true;

        for(int i = 2; isPrime && i < value; i++)
        {
            if (value % i == 0)
            {
                isPrime = false;
            }
        }

        return isPrime;
    }
}

Your main program is then responsible for iterating over each number and printing only thsoe which the prime number test identifies as prime.

public static void main(String[] args)
{
    //Determine the range of prime numbers to print
    Scanner scan = new Scanner(System.in);
    System.out.print("Primes smaller than what number should be printed?: ");
    int max = Integer.parseInt(scan.nextLine());

    //Identify how prime numbers will be tested
    PrimeNumberTest test = new BruteForcePrimeNumberTest();

    //Uncomment the line below if you want to include the number 1. Favour adding it here so that you may
    //use re-use your prime number test elsewhere that atually needs to know if a number is prime.
    //System.out.println(1);

    //Print the prime numbers
    for (int i = 2; i < max ; i++)
    {
        if(test.isPrime(i))
        {
            System.out.println(i);
        }
    }
}

Your main program however should only be concerned with prime number generation. It doesn't really care about the semantics of how those primes are generated we just want the primes. It doesn't really matter if the primes were found via primality testing or any other algorithm. So we ask ourselves what does a prime number generator look like?

For starter primes are always whole numbers so we shouldn't be storing them inside floats, doubles or decimals. That leaves 32 and 64 bit integers. If you want to generate larger prime numbers then obviously you should use the long type but I'm just going to use int. In other languages we would also have to consider things like unsigned numbers too.

Now we need to find a way to return all of these numbers at once. Trees don't really make sense as we're going to be generating a consecutive sequence. Stacks don't make sense because consumers typically want the numbers in the order they were generated. Queues could be used as they fit the first-in-first-out rule. In fact if the end application had an asynchronous prime number generator (producer) and a separate asynchronous consumer this type would be ideal. For this example however I want something read-only. Essentially a prime number generator is an Iterable<int>.

public class PrimeNumberTestGenerator : Iterable<int>
{
    private int limit;
    private PrimalityTester tester;

    public PrimeNumberTestGenerator(PrimalityTester tester, int limit)
    {
        this.tester = tester;
        this.limit = limit;
    }

    private class PrimeNumberIterator : Iterator<int>
    {
        private int current;

        public PrimeNumberIterator()
        {
        }

        public bool hasNext()
        {
            return next < limit;
        }

        public int moveNext()
        {
            if (!hasNext())
            {
                throw new NoSuchElementException();
            }

            int result = next;

            do
            {
                next++;
            } while(hasNext() && !tester.isPrime(next));


            return result;
        }

        public void remove()
        {
            throw new UnsupportedOperationExecution();
        }
    }

    public Iterator<int> iterator()
    {
        return new PrimeNumberIterator();
    }
}

So how do we tie them together?

public static void main(String[] args)
{
    //Determine the range of prime numbers to print
    Scanner scan = new Scanner(System.in);
    System.out.print("Primes smaller than what number should be printed?: ");
    int max = Integer.parseInt(scan.nextLine());

    //Identify how prime numbers will be tested
    Iterable<int> primes = new PrimeNumberTestGenerator(max, new BruteForcePrimeNumberTest());

    //Print the prime numbers
    foreach (int prime : primes)
    {
        System.out.println(prime);
    }
}

Efficiency

Now the other side of your question was an efficient way of determining the prime numbers within a specified range. While a quick internet search should yield a number of different "fast" algorithms for determing a set of prime numbers that are much faste than the brute force way. One such approach is the Sieve of Atkin:

public class AtkinSieve : Iterable<int>
{
    private BitSet primes;

    public AtkinSieve(int limit)
    {
        primes = new BitSet(limit);

        int root = (int)Math.sqrt(limit);

        primes.set(2);
        primes.set(3);

        //this section can be further optimized but is the approach used by most samples
        for (int x = 1; x <= root; x++)
        {
            for (int y = 1; y <= root; y++)
            {
                int number;
                int remainder;


                number = (4 * x * x) + (y * y);
                remainder = number % 12;
                if (number < limit && (remainder == 1 || remainder == 5))
                {
                    primes.flip(number);
                }

                number = (3 * x * x) + (y * y);
                remainder = number % 12;
                if (number < limit && remainder == 7)
                {
                    primes.flip(number);
                }

                if (x < y)
                {
                    number = (3 * x * x) - (y * y);
                    remainder = number % 12;
                    if (number < limit && remainder == 11)
                    {
                        primes.flip(number);
                    }
                }
            }
        }

        for (int i = 5; i <= root; i++)
        {
            if (primes.get(i))
            {
                int square = i * i;
                for (int j = square; j < limit; j += square)
                {
                    primes.clear(j);
                }
            }
        }
    }
}

public class SetBitIterator : Iterator<int>
{
    private BitSet bits;
    private int next;
    private bool isReadOnly;

    public SetBitIterator(BitSet bits)
    {
        this.bits = bits;
        next = bits.nextSetBit(0);   
    }

    public bool hasNext()
    {
        return next <> -1;
    }

    public int moveNext()
    {
        int result = next;

        next = bits.nextSetBit(next);

        return result;
    }

    public void remove()
    {
        throw new UnsupportedOperationException();
    }
}

Conveniently we can now use this prime number generator by only changing a single line in our previous main program!

Change:

//Identify how prime numbers will be tested
Iterable<int> primes = new PrimeNumberTestGenerator(max, new BruteForcePrimeNumberTest());

To:

//Identify how prime numbers will be tested
Iterable<int> primes = new AtkinSieve(max);

Solution 2

  1. You can speed up your search for new primes by storing the primes that you have already found in a private collection inside the PrimeGenerator. By trying only them as potential divisors instead of your for(int i = 2; i < number; i++) loop, you will have to do much fewer divisions
  2. You can stop the "find divisors" loop well before you reach the number: specifically, you can stop when your candidate divisor exceeds the square root of the target number. This works, because you try the candidate divisors in ascending order: if there were divisors above the square root, the result of the division would have been below the square root, so you would have already found them.
  3. Your getNextPrime method should call isPrime internally before returning the value to the caller. Otherwise, the call of getNextPrime cannot be said to return the next prime.

Solution 3

First and most important thing is.... U need not to check till i

for(int i = 2; i < number; i++)

U need to to check only untill i is less than number/2...

for(int i = 2; i < (number/2); i++)

Solution 4

This is how I might have written it for simplicity

public static void main(String... args) {
    System.out.print("Enter an integer that you'd like the system to print the prime numbers till: ");
    Scanner scan = new Scanner(System.in);
    int input = scan.nextInt();

    if (input >= 2)
        System.out.println(2);
    OUTER: for (int i = 3; i <= input; i += 2) { // skip every even number
        for (int j = 3; j * j <= i; j += 2) // stop when j <= sqrt(i)
            if (i % j == 0)
                continue OUTER;
        System.out.println(i); // 99+% of the time will be spent here. ;)
    }
}

Solution 5

Yeah there are. I don´t know if it´s the most efficient, but it is way more efficient then this one. Check the Miller Rabin test.

Even so, if you want to work with your Code, i could tell you, you should do it like this:

public boolean isPrime(int number)
{
    // You should know, that every straight number can not be prime,so you can say i+= 2
   if (number == 2)
       return true;
   if (number % 2 == 0)
   {
      return false;
   }
    for(int i = 3; i < number; i+=2)
    {
       if (number % i == 0)
       {
          number--;
          return false;
       }
     --number;
     return true;
 }
Share:
10,837
Waj
Author by

Waj

Updated on June 05, 2022

Comments

  • Waj
    Waj almost 2 years

    is there a more efficient, cleaner/elegant way of finding prime numbers than this? The code works fine, but I just wrote what seemed most logical to me and I can't figure out any other way, but to be honest it just doesn't look nice :P. I know coding isn't the most elegant of activities.

    Here's my main method:

    import java.util.Scanner;
    public class DisplayPrimeNumbers
    {
        public static void main(String[] args)
        {
            Scanner scan = new Scanner(System.in);
            System.out.print("Enter an integer that you'd like the system to print the prime numbers till: ");
            String input1 = scan.nextLine();
            int input = Integer.parseInt(input1);
    
            PrimeGenerator prime = new PrimeGenerator(input);
    
            for (int i = 1; i < input ; i++)
            {
                if(prime.isPrime())
                {
                System.out.println(prime.getNextPrime());
                }
            }
            System.out.println(1);
    
        }
    }
    

    Here's my class:

    public class PrimeGenerator 
    {
        private int number;
    
        public PrimeGenerator(int n)
        {
            number = n;
        }
    
        public int getNextPrime ()
        {
            return number+1;
        }
    
    
        public boolean isPrime()
        {
            for(int i = 2; i < number; i++)
            {
                if (number % i == 0)
                {
                    number--;
                    return false;
                }
            }
        number--;   
        return true;    
        }
    }