0

please help me to count prime number between 0 to 100000000 because I use to write but it works very slowly:

Here is my code:

$n =100000000;
$answer =0;
for ($i = 2, $j = 2; $i <= $n; $i++) {
    for ($j = 2; $j < $i; $j++) {
        if ($i % $j == 0) {
            break;
        }
    }
    if ($j == $i) {
        $answer++;
    }
}

echo $answer . PHP_EOL;
Fallen
  • 4,435
  • 2
  • 26
  • 46
user3422175
  • 11
  • 1
  • 1

5 Answers5

5

Check out Sieve of Eratosthenes. This problem can be solved in less than 2 seconds on a modern desktop. You can also try Bitwise Sieve which performs better in term of memory and speed.

Community
  • 1
  • 1
Fallen
  • 4,435
  • 2
  • 26
  • 46
1

As mentioned before use Sieve of Eratosthenes

  • on my AMD 3.2 GHz Win7 x64 single threaded 32bit C++ (BDS2006) App:
  • [4929.114 ms] find primes <= 100000000 found 5761456 primes
  • so it took almost 5 seconds to compute
  • my implementation use single bit per any odd number so you need N/16 Bytes of memory
  • for your 100 000 000 it is almost 6 MB which is OK I think

this is the source in C++:

//---------------------------------------------------------------------------
int primes_found=0;
void addprime(int p)
    {
    // here do the stuff you want to do with prime p=2,3,5,7,...
    // I just count the primes found ...
    primes_found++;
    }
//---------------------------------------------------------------------------
void getprimes(int p)                       // compute all primes up to p
    {
    // sieves N/16 bytes
    //  ------------------------------
    //   0  1  2  3  4  5  6  7 bit
    //  ------------------------------
    //   1  3  5  7  9 11 13 15 +-> +2
    //  17 19 21 23 25 27 29 31 |
    //  33 35 37 39 41 43 45 47 V +16
    //  ------------------------------
    int N=(p|15),M=(N>>4)+1;            // store only odd values 1,3,5,7,... each bit ...
    char *m=new char[M];                // m[i] ->  is 1+i+i prime? (factors map)
    int i,j,k;
    // init sieves
    m[0]=254; for (i=1;i<M;i++) m[i]=255;
    for(i=3;i<=N;i+=2)
     for(k=i+i,j=i+k;j<=N;j+=k)
      m[j>>4]&=255-(1<<((j>>1)&7));
    // compute primes
    addprime(2);
    for(i=1,j=i>>4;j<M;i+=16,j++)
        {
        k=m[j];
        if (!k) continue;
        if (int(k&  1)!=0) addprime(i   );
        if (int(k&  2)!=0) addprime(i+ 2);
        if (int(k&  4)!=0) addprime(i+ 4);
        if (int(k&  8)!=0) addprime(i+ 6);
        if (int(k& 16)!=0) addprime(i+ 8);
        if (int(k& 32)!=0) addprime(i+10);
        if (int(k& 64)!=0) addprime(i+12);
        if (int(k&128)!=0) addprime(i+14);
        }
    delete[] m;
    }
//---------------------------------------------------------------------------
  • you can use #define addprime(prime) {...} instead of function
  • but in my compiler that is a bit slower (don know why)

usage:

getprimes(100000000);

[Notes]

  • even this can be further optimized and speed-ed up (the init sieves part)
  • but I am too lazy to try
  • this is faster on my setup then N/2 Bytes version probably due to Caching

[edit1] init sieves by primes only

// init sieves
m[0]=254; for (i=1;i<M;i++) m[i]=255;
for(i=1;i<=N;) // here could be also sqrt(N) instead of N (or half the bits number)
    {
    int a=m[i>>4];
    if (int(a&  1)!=0) for(k=i+i,j=i+k;j<=N;j+=k) m[j>>4]&=255-(1<<((j>>1)&7)); i+=2;
    if (int(a&  2)!=0) for(k=i+i,j=i+k;j<=N;j+=k) m[j>>4]&=255-(1<<((j>>1)&7)); i+=2;
    if (int(a&  4)!=0) for(k=i+i,j=i+k;j<=N;j+=k) m[j>>4]&=255-(1<<((j>>1)&7)); i+=2;
    if (int(a&  8)!=0) for(k=i+i,j=i+k;j<=N;j+=k) m[j>>4]&=255-(1<<((j>>1)&7)); i+=2;
    if (int(a& 16)!=0) for(k=i+i,j=i+k;j<=N;j+=k) m[j>>4]&=255-(1<<((j>>1)&7)); i+=2;
    if (int(a& 32)!=0) for(k=i+i,j=i+k;j<=N;j+=k) m[j>>4]&=255-(1<<((j>>1)&7)); i+=2;
    if (int(a& 64)!=0) for(k=i+i,j=i+k;j<=N;j+=k) m[j>>4]&=255-(1<<((j>>1)&7)); i+=2;
    if (int(a&128)!=0) for(k=i+i,j=i+k;j<=N;j+=k) m[j>>4]&=255-(1<<((j>>1)&7)); i+=2;
    }
  • [1520.160 ms] find primes <= 100000000 found 5761456 primes
  • now it takes ~1.5 seconds
  • I checked first 1000000 primes (up to 15485863) and the output is correct
Spektre
  • 49,595
  • 11
  • 110
  • 380
  • @WillNess as stated it can be further optimized (this is also not sophisticated nor optimized at all just packet into bits) just the SQRT(N) will match your time alone but you can not compare times on different HW/Compiler/OS setup and make assumptions about algo speed especialy with comparing values on WOW64 emulator. The point is that `1.5sec <<< 60sec` which is what the OP want – Spektre Aug 06 '14 at 16:25
0

Change the algorithm. Using Euler sieve.

this is my C++ code. It will only cost 3 or 4 seconds.

bool isprm[100000000+10];
int prm[5000000];
clr(isprm, true);
int cnt = 0;
for(int i=2; i<=n; i++)
{
    if(isprm[i])    prm[++cnt] = i;
    for(int j=1; j<=cnt && LL(i)*prm[j]<=n; j++)
    {
        isprm[ i*prm[j] ] = false;
        if(i % prm[j] == 0)  break;
    }
}
mickeyandkaka
  • 1,452
  • 2
  • 11
  • 21
0

skipping even numbers greater 2 and stopping at square root of i should give some speed up

my c code:

int max = 100000000;
int i, j;
int answer = 0;
for(i=2;i<max;i++) {
    if(i%2 == 0) continue;
    for(j=3;j*j<i;j+=2) {
       if(i%j == 0) break;
    }
    if(j*j >= i) answer++;
}
Optokopper
  • 203
  • 1
  • 5
0
  • Based on https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes
  • Loop is only needed up to the square root of n (in original post the loop goes from 0 to n)
  • Using SplFixedArray instead of normal PHP arrays (faster)
  • Setting 1 in array to flag non primes (bit faster than setting true, according to my tests)
    /*
    PHP 7.3.7 x64
    About 13~14 secs with AMD Ryzen 5 3600 (3,6Ghz)
    */
    
    ini_set('memory_limit','2048M');
    
    function countPrimes($n) {
        $st = microtime(true);
        $arr = new \SplFixedArray($n+1);
        $cnt = 0; 
        for ($i=2;$i<=floor(sqrt($n));$i+=1) {
            if (!isset($arr[$i])) {
                for ($j=$i*2;$j<=$n;$j+=$i) {
                    if (!isset($arr[$j])) {
                        $arr[$j]=1;
                        $cnt++;
                    }
                }               
            }
        }
        echo ($n-$cnt-1).' primes found from 0 to '.$n.' (in '.round((microtime(true) - $st),3).' secs)';
    }
    
    countPrimes(100000000);
ilvi
  • 3
  • 2
  • 1
    Please include some description of how this implementation differs from other answers and from the original post, and describe the algorithm you are using. – Brent K. May 16 '22 at 15:43