1

This is the Link to the problem: http://www.spoj.com/problems/PRIME1/

Basically we get two limits and we have to print out all the primes between them...

Here is my Code (Language == C) :

#include <stdio.h>

void IsPrime(int test){
    for(int i= 2; i<test; i++){
        if(test%i==0){
            return;
        }
    }
    printf("%d\n",test);
}

int main(){
    int T,lower,upper;
    scanf("%d",&T);

    while(T--){
        scanf("%d",&lower);
        scanf("%d",&upper);

        for(int i = lower;i<=upper;i++){
            if(i>1){
            IsPrime(i);
            }
        }  
    }
return 0;
}

On my local machine I ran this and it works for the simple test cases... My message from the website is timeout error so I was wondering if there is a more efficient way to solve this problem because apparently I am not solving it fast enough?

Sudocode
  • 49
  • 2
  • 9
  • See, amongst many other related questions, [Fastest way to list all primes below N](https://stackoverflow.com/questions/2068372/fastest-way-to-list-all-primes-below-n). Granted, that's in Python, not C, but the algorithms are there, and references to web sites, etc. – Jonathan Leffler May 29 '16 at 18:42
  • 2
    Note that your `IsPrime()` function should do the calculation and return a true/false indication, leaving the printing to the calling code. Then it could be reused for other tasks. What you have is not so much an `IsPrime()` function as a `PrintNumberPlusNewlineIfNumberIsPrime()` function. – Jonathan Leffler May 29 '16 at 18:45

3 Answers3

3

To begin with, you don't have to go checking every number up to n to determine if n is prime, only to its square root (there is a mathematical proof, not going to give it now). So:

void IsPrime(int test){
    // i <= sqrt(test)
    // but to avoid sqrt you can do i * i <= test
    for(int i= 2; i * i <= test; i++){
        if(test%i==0){
            return;
        }
    }
    printf("%d\n",test);
}

Next, we know that after 2, all other prime numbers are odd, so we can loop by 2 if we treat 2 as special case:

// Do greater than one check only once
if (lower > 1) {
    // Special case - lower is 2
    if (lower == 2) {
        printf("%d\n", 2);
        ++lower;
    }

    for(int i = lower; i <= upper; i += 2){
        IsPrime(i);
    }
}

However since you have to do it T times, you will end up doing the checks a lot more than needed. Also, the problem has limits on n and m so it's basically perfect for a sieve, as @HennoBrandsma said.

Using these optimizations, you should go find all prime numbers to the limit, and store them in a container. Then, when prompted with a range, simply traverse the sieve and print out the numbers.

(That will require you to change up the IsPrime function a bit more - instead of printing the number right away, let it return true or false, and then based on that, add the number to the container)

Franko Leon Tokalić
  • 1,417
  • 1
  • 21
  • 27
  • Note that `i * i <= test` is prone to `int` overflow (undefined behavior) unlike OP's `i – chux - Reinstate Monica May 31 '16 at 15:55
  • @chux yeah that's right, I guess a better way would be to compute sqrt once and then use it in a loop, as mentioned in comments of one of the answers below. – Franko Leon Tokalić May 31 '16 at 16:00
  • Use the _square root_ of `test` is good, but using `sqrt(test)` is not so great. `sqrt()` suffers from 1) `double` may have less exact range than `int` (think 64-bit `int`) and 2) math functions like `sqrt()` are too often surprising inexact resulting in a value _just_ less than the expectant integer value. Better to use a `int isqrt(int x)` - coding it if not available. – chux - Reinstate Monica May 31 '16 at 16:13
  • @chux true, didn't think about that. Thanks! – Franko Leon Tokalić May 31 '16 at 16:14
1

You can try the following which has a slight optimization on the number of tests as well as skipping any even values greater than 2:

int isprime (int v)
{
    int i;

    if (v < 0) v = -v;                          /* insure v non-negative */
    if (v < 2 || !((unsigned)v & 1))    /* 0, 1 + even > 2 are not prime */
        return 0;

    if (v == 2) return 1;

    for (i = 3; i * i <= v; i+=2)
        if (v % i == 0)
            return 0;

    return 1;
}

If you can use the math library and math.h, the following may be faster:

int isprime (int v)
{
    int i;

    if (v < 0) v = -v;                          /* insure v non-negative */
    if (v < 2 || !((unsigned)v & 1))    /* 0, 1 + even > 2 are not prime */
        return 0;

    if (v == 2) return 1;

    for (i = 3; i <= sqrt (v); i+=2)
        if (v % i == 0)
            return 0;

    return 1;
}

I timed both versions over the int range for values between 1-2 million and they are close.

Note: In actual testing with repetitive calls, the version with i * i <= v (isprime2 below) is consistently faster than the call with i <= sqrt (v) (isprime3 below). e.g.:

$ ./bin/isprimetst2
  isprime  (1.650138 sec) - 78497 primes
  isprime2 (0.805816 sec) - 78497 primes
  isprime3 (0.983928 sec) - 78497 primes

The short driver iterated over all primes from 0-2000000, e.g.:

r = 0;
t1 = clock ();
for (v = 0; v < 2000000 - 1; v++) r += isprime2 (v);
t2 = clock ();
printf (" isprime2 (%lf sec) - %u primes\n", (t2-t1)/CLOCKS_PER_SEC, r);

r = 0;
t1 = clock ();
for (v = 0; v < 2000000 - 1; v++) r += isprime3 (v);
t2 = clock ();
printf (" isprime3 (%lf sec) - %u primes\n", (t2-t1)/CLOCKS_PER_SEC, r);
David C. Rankin
  • 69,681
  • 6
  • 44
  • 72
  • Better compute the sqrt of n once (round it down), and check that as the limit for i, instead of squaring i every time. – Henno Brandsma May 29 '16 at 18:22
  • Yes, that would be better, but requires `math.h` and linking against the math library. I wasn't sure whether that was permitted or not. – David C. Rankin May 29 '16 at 18:24
  • 1
    Why are you checking every even divisor, having ruled out even numbers? – Weather Vane May 29 '16 at 18:25
  • That's better, I hadn't carried the even check through to the loop -- hold on. – David C. Rankin May 29 '16 at 18:25
  • @DavidC.Rankin I'd say standard library is fair game. – Henno Brandsma May 29 '16 at 18:25
  • Wow didn't think about skipping even #'s >2 ... that is a great optimization but this website is crazy time limit exceeded still.... Not sure what else we can do? There seems to be a trick method somewhere – Sudocode May 29 '16 at 18:30
  • @Sudocode the problem gives you limits on m and n, so you should generate a sieve up to those limits, so you only do prime number checking of each number only once, and then simply traverse the sieve in range. – Franko Leon Tokalić May 29 '16 at 18:33
  • @Sudocode there is, but that is a "challenge question" for *you* to solve. Hint: prepare a sieve and an array of prime numbers *before* you iterate through `t` test cases. – Weather Vane May 29 '16 at 18:34
  • Note that this is OK for finding whether a single number is prime (especially if optimized a bit), but if you use it to check every number between 10,000,000 and 99,999,999 (for example), there are definitely better ways — [Sieve of Eratosthenes](https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes) for instance. – Jonathan Leffler May 29 '16 at 18:39
  • okay so going to the limit sqrt(num) in my isPrime() solved the problem ... but the sieves approach looks cool too.... and is probably more efficient!!! great responses from every1 wish I can give ++'s to every1 learned alot! – Sudocode May 29 '16 at 18:40
  • Glad it helped, it just took longer for me to code the test to validate the timings than I anticipated. Glad you got it sorted. – David C. Rankin May 29 '16 at 18:48
  • @Henno Brandsma `sqrt()` has [weaknesses](http://stackoverflow.com/questions/37513311/prime-number-generator-in-c/37513428?noredirect=1#comment62590058_37513428). Perhaps [`isqrt()`](http://stackoverflow.com/a/25387376/2410359)? – chux - Reinstate Monica May 31 '16 at 16:18
  • Minor: 1) Unclear why the need for the cast in `if (v < 2 || !((unsigned)v & 1))`? 2) `if (v < 0) v = -v;` value is also unclear. Could omit. This also avoid UB with `isprime (INT_MIN)`. – chux - Reinstate Monica May 31 '16 at 16:26
  • Hmm.. good info, that is a new conundrum to add to the mix. I'm going to go test against the *Carmack's Fast Inverse Square Root* for floating point and see how they compare. – David C. Rankin May 31 '16 at 16:26
  • [that](https://en.wikipedia.org/wiki/Fast_inverse_square_root) has only about 23-bit precision. Trouble with integer values just smaller than an integer than is exactly representable as a `float`. – chux - Reinstate Monica May 31 '16 at 16:31
  • Yes, `INT_MIN` is a problem and an overflow test should be added. The cast `(unsigned)v & 1` is largely a cautionary reminder that bitwise operations on signed int required additional care. It can be omitted since the test of the LSB has no implications either way. – David C. Rankin May 31 '16 at 16:31
  • True about additional care typically needed with `int`. OTOH, it makes sense that prime number function only deal with unsigned types. – chux - Reinstate Monica May 31 '16 at 16:34
  • I tested all `isqrt`, `isqr_Newton`, `sqrt_carmak` (returning `double`) with `-O0` over `1 - 100000`, *Carmak's* solution was the fastest `.015968 sec`, followed by `isqrt - .022608 sec` with the slowest being Newton `.032083 sec`. These were consistent over multiple runs. Given the result, it almost makes more sense to use floating point to derive the square root and cast to int/unsigned as required. Over `1000000` Newton and `isqrt` swap by a fraction, but otherwise are similar. – David C. Rankin May 31 '16 at 17:26
0

You can use a library maths.h in C and use sqrt function to calculate the square root of given number. So the program might be like this:

#include <stdio.h>
#include <maths.h>

int isPrime(int number){
    int i;
    if(number % 2 == 0){
        return;
    }

    for(i=3; i<=sqrt(number); i++){
        if(number % i == 0){
            return;
    }
    printf("%d\n",number);
}

int main(){
    int lower,upper,i;
    if(lower >1){
        if(lower == 2){
            printf("2\n");
        }

        for(i=lower; i<=upper; i++){
            isPrime(i);
        }
    return 0;
}

In short you can use some extra checks (like if(number % 2 == 0)) using if-else condition to decrease the time complexity of the program.For example a new if condition may be if(number % 5 ==0) etc. ,so with the help of these conditions check won't go in for loop in many of the cases, and that would decrease the time of the program.

Swr7der
  • 801
  • 9
  • 26