I\'ve been interested in the problem of finding a better prime number recognizer for years. I realize this is a huge area of academic research and study - my interest in this i
can you suggest an improvement
Here you go ... not for the algorithm, but for the program itself :)
argc
and argv
, get rid of them== 1
, not != EOF
sqrt()
returnValue
is not needed, you can return a constant: return 0;
main()
function, separate your program in as many functions as you can think of.The time complexity for each primality test in your algorithm is O(sqrt(n))
.
You can always use the fact that all primes except 2 and 3 are of the form: 6*k+1
or 6*k-1
. For example:
int is_prime(int n) {
if (n <= 1) return 0;
if (n == 2 || n == 3) return 1;
if (n % 2 == 0 || n % 3 == 0) return 0;
int k;
for (k = 6; (k-1)*(k-1) <= n; k += 6) {
if (n % (k-1) == 0 || n % (k+1) == 0) return 0;
}
return 1;
}
This optimization does not improve the asymptotic complexity.
EDIT
Given that in your code you are testing numbers repeatedly, you might want to pre-calculate a list of primes. There are only 4792 primes less than or equal to the square root of INT_MAX (assuming 32 bit ints).
Furthermore, if the input numbers are relatively small you can try calculating a sieve.
Here's a combination of both ideas:
#define UPPER_BOUND 46340 /* floor(sqrt(INT_MAX)) */
#define PRIME_COUNT 4792 /* number of primes <= UPPER_BOUND */
int prime[PRIME_COUNT];
int is_prime_aux[UPPER_BOUND];
void fill_primes() {
int p, m, c = 0;
for (p = 2; p < UPPER_BOUND; p++)
is_prime_aux[p] = 1;
for (p = 2; p < UPPER_BOUND; p++) {
if (is_prime_aux[p]) {
prime[c++] = p;
for (m = p*p; m < UPPER_BOUND; m += p)
is_prime_aux[m] = 0;
}
}
}
int is_prime(int n) {
if (n <= 1) return 0;
if (n < UPPER_BOUND) return is_prime_aux[n];
int i;
for (i = 0; i < PRIME_COUNT && prime[i]*prime[i] <= n; i++)
if (n % prime[i] == 0)
return 0;
return 1;
}
Call fill_primes
at the beginning of your program, before starting to process queries. It runs pretty fast.
for (i = 2; i <= ceiling; i++) {
if (input % i == 0) {
factorFound = 1;
break;
}
}
This is the first improvement to make and still stay within the bounds of "same" algorithm. It doesn't require any math at all to see this one.
Beyond that, once you see that input
is not divisible by 2, there is no need to check for 4, 6, 8, etc. If any even number divided into input
, then surely 2 would have because it divides all even numbers.
If you want to step outside of the algorithm a little bit, you could use a loop like the one that Sheldon L. Cooper provides in his answer. (This is just easier than having him correct my code from the comments though his efforts are much appreciated)
this takes advantage of the fact that every prime other than 2 and 3 is of the form n*6 + 1
or n*6 - 1
for some positive integer n
. To see this, just note that if m = n*6 + 2
or m = n*6 + 4
, then n
is divisible by 2. if m = n*6 + 3
then it is divisible by 3.
In fact, we can take this further. If p1, p2, .., pk
are the first k
primes, then all of the integers that are coprime to their product mark out 'slots' that all remaining primes must fit into.
to see this, just let k#
be the product of all primes up to pk
. then if gcd(k#, m) = g
, g
divides n*k# + m
and so this sum is trivially composite if g != 1
. so if you wanted to iterate in terms of 5# = 30
, then your coprime integers are 1, 7, 11, 13, 17, 19, 23 and 29.
technically, I didn't prove my last claim. It's not much more difficult
if g = gcd(k#, m)
, then for any integer, n
, g
divides n*k# + m
because it divides k#
so it must also divide n*k#
. But it divides m
as well so it must divide the sum. Above I only proved it for n = 1
. my bad.
Also, I should note that none of this is changing the fundamental complexity of the algoritm, it will still be O(n^1/2). All it is doing is drastically reducing the coefficient that gets used to calculate actual expected run times.
Here's my algorithm, Complexity remains O(n^0.5)
but i managed to remove some expensive operations in the code...
The algorithm's slowest part is the modulus
operation, i've managed to eliminate sqrt
or doing i * i <= n
This way i save precious cycles...its based on the fact that sum of odd numbers is always a perfect square.
Since we are iterating over odd numbers
anyway, why not exploit it? :)
int isPrime(int n)
{
int squares = 1;
int odd = 3;
if( ((n & 1) == 0) || (n < 9)) return (n == 2) || ((n > 1) && (n & 1));
else
{
for( ;squares <= n; odd += 2)
{
if( n % odd == 0)
return 0;
squares+=odd;
}
return 1;
}
}
Even numbers(except 2) cannot be prime numbers. So, once we know that the number is not even, we can just check if odd numbers are it's factors.
for (i = 3; i <= ceiling; i += 2) {
if (input % i == 0) {
factorFound = 1;
break;
}
}