Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Any easier way of finding prime numbers than this?

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;    
    }
} 
like image 564
Waj Avatar asked Jan 02 '13 13:01

Waj


3 Answers

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);
like image 168
Chris Kerekes Avatar answered Nov 07 '22 15:11

Chris Kerekes


  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.
like image 3
Sergey Kalinichenko Avatar answered Nov 07 '22 13:11

Sergey Kalinichenko


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++)
like image 3
Saty Avatar answered Nov 07 '22 14:11

Saty