简体   繁体   English

如何在 PHP 中在不到 1 分钟的时间内计算 0 到 100000000 之间的素数?

[英]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:请帮我计算 0 到 100000000 之间的质数,因为我曾经写过,但它工作得非常慢:

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;

Check out Sieve of Eratosthenes .检查埃拉托色尼筛 This problem can be solved in less than 2 seconds on a modern desktop.在现代桌面上,这个问题可以在不到 2 秒的时间内解决。 You can also try Bitwise Sieve which performs better in term of memory and speed.您还可以尝试在内存和速度方面表现更好的Bitwise Sieve

As mentioned before use Sieve of Eratosthenes如前所述,使用埃拉托色尼筛

  • on my AMD 3.2 GHz Win7 x64 single threaded 32bit C++ (BDS2006) App:在我的 AMD 3.2 GHz Win7 x64 单线程 32 位 C++ (BDS2006) 应用程序上:
  • [4929.114 ms] find primes <= 100000000 found 5761456 primes
  • so it took almost 5 seconds to compute所以计算需要将近 5 秒
  • my implementation use single bit per any odd number so you need N/16 Bytes of memory我的实现对任何奇数使用单个位,因此您需要 N/16 字节的内存
  • for your 100 000 000 it is almost 6 MB which is OK I think对于您的 100 000 000,它几乎是 6 MB,我认为这还可以

this is the source in C++:这是 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您可以使用#define addprime(prime) {...}代替函数
  • 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)甚至可以进一步优化和加速(init sieves 部分)
  • but I am too lazy to try但我懒得尝试
  • this is faster on my setup then N/2 Bytes version probably due to Caching这在我的设置上比 N/2 字节版本更快,可能是由于缓存

[edit1] init sieves by primes only [edit1] init 仅按素数筛选

// 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现在大约需要 1.5 秒
  • I checked first 1000000 primes (up to 15485863) and the output is correct我检查了前 1000000 个素数(最多 15485863)并且输出是正确的

Change the algorithm.改变算法。 Using Euler sieve.使用欧拉筛。

this is my C++ code.这是我的 C++ 代码。 It will only cost 3 or 4 seconds.它只需要 3 或 4 秒。

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

skipping even numbers greater 2 and stopping at square root of i should give some speed up跳过大于 2 的偶数并停在 i 的平方根处应该可以加快速度

my c code:我的 C 代码:

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++;
}
  • Based on https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes基于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)循环只需要到 n 的平方根(在原始帖子中,循环从 0 到 n)
  • Using SplFixedArray instead of normal PHP arrays (faster)使用 SplFixedArray 而不是普通的 PHP 数组(更快)
  • Setting 1 in array to flag non primes (bit faster than setting true, according to my tests)在数组中设置 1 以标记非素数(根据我的测试,比设置 true 快一点)
    /*
    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);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM