'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 |
