'How to count prime number between 0 to 100000000 in less than 1 minute in PHP?

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;


Solution 1:[1]

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.

Solution 2:[2]

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

Solution 3:[3]

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;
    }
}

Solution 4:[4]

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++;
}

Solution 5:[5]

  • 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);

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Community
Solution 2
Solution 3 mickeyandkaka
Solution 4
Solution 5