【问题标题】:Sieve Of Atkin is surprisingly slow阿特金的筛子出奇地慢
【发布时间】:2014-01-04 06:14:04
【问题描述】:

我最近对素数产生了浓厚的兴趣,并尝试编写程序来计算它们。我能够筛选出能够在几秒钟内计算出一百万个素数的 Sundaram 程序。我相信这很快,但我想要更好。我继续尝试制作Sieve of Atkin,从维基百科复制伪代码后,我在 20 分钟内将工作 C++ 代码拼凑在一起。

我知道它并不完美,因为毕竟它是伪代码。虽然我期待至少比我的 Sundaram Sieve 更好,但我错了。它非常非常慢。我已经看过很多次了,但我找不到任何可以做出的重大改变。查看我的代码时请记住,我知道它效率低下,我知道我使用了系统命令,我知道它无处不在,但这不是一个项目或任何重要的东西,它是为我准备的。

#include <iostream>
#include <fstream>
#include <time.h>
#include <Windows.h>
#include <vector>

using namespace std;

int main(){

float limit;
float slimit;
long int n;
int counter = 0;
int squarenum;
int starttime;
int endtime;
vector <bool> primes;

ofstream save;
save.open("primes.txt");
save.clear();

cout << "Find all primes up to: " << endl;
cin >> limit;

slimit = sqrt(limit);

primes.resize(limit);

starttime = time(0);

// sets all values to false
for (int i = 0; i < limit; i++){

    primes[i] = false;
}


//puts in possible primes
for (int x = 1; x <= slimit; x++){

    for (int y = 1; y <= slimit; y++){


        n = (4*x*x) + (y*y);
        if (n <= limit && (n%12 == 1 || n%12 == 5)){

            primes[n] = !primes[n];
        }

        n = (3*x*x) + (y*y);
        if (n <= limit && n% 12 == 7){

            primes[n] = !primes[n];
        }

        n = (3*x*x) - (y*y);
        if ( x > y && n <= limit && n%12 == 11){

            primes[n] = !primes[n];
        }
    }
}

//square number mark all multiples not prime

for (float i = 5; i < slimit; i++){

    if (primes[i] == true){

        for (long int k = i*i; k < limit; k = k + (i*i)){

            primes[k] = false;
        }
    }
}

endtime = time(0);
cout << endl << "Calculations complete, saving in text document" << endl;


// loads to document
for (int i = 0 ; i < limit ; i++){

    if (primes[i] == true){


        save << counter << ") " << i << endl;
        counter++;
    }
}

save << "Found in " << endtime - starttime << " seconds" << endl;

save.close();

system("primes.txt");

system ("Pause");
return 0;
}

【问题讨论】:

  • 也编译选项。
  • 参见维基百科。 “这个伪代码是为清楚起见而编写的。重复和浪费的计算意味着它的运行速度比 Eratosthenes 的筛子要慢。为了提高其效率,必须使用更快的方法来找到三个二次方的解决方案”。可以在这里找到高效的阿特金筛:cr.yp.to/primegen.html
  • 下载东西为我做这件事违背了目的,我意识到我可以在网上找到它,但我不想那样。
  • 您将float 用于limitslimit 有什么原因吗?
  • 关于使用分析器查看瓶颈所在位置的强制性评论。

标签: c++ performance optimization sieve


【解决方案1】:

这并不完全是一个答案(IMO,您已经在 cmets 中得到了答案),而是一个快速的比较标准。埃拉托色尼的筛子应该在一台相当现代的机器上在一秒钟内找到一百万个素数。

#include <vector>
#include <iostream>
#include <time.h>

unsigned long primes = 0;

int main() {
    // empirically derived limit to get 1,000,000 primes
    int number = 15485865; 

    clock_t start = clock();
    std::vector<bool> sieve(number,false);
    sieve[0] = sieve[1] = true;

    for(int i = 2; i<number; i++) {
        if(!sieve[i]) {
            ++primes;
            for (int temp = 2*i; temp<number; temp += i)
                sieve[temp] = true;
        }
    }
    clock_t stop = clock();

    std::cout.imbue(std::locale(""));
    std::cout << "Total primes: " << primes << "\n";
    std::cout << "Time: " << double(stop - start) / CLOCKS_PER_SEC << " seconds\n";
    return 0;
}

在我的笔记本电脑上运行它,我得到以下结果:

Total primes: 1000000
Time: 0.106 seconds

显然,速度会因处理器、时钟速度等而有所不同,但对于任何现代的合理,我仍然希望时间不到一秒。当然,如果您决定将质数写入文件,您可以期望这会增加一些时间,但即使这样,我也希望总时间不到一秒——我的笔记本电脑的硬盘驱动器相对较慢,写出这些数字的总和最多只有 0.6 秒。

【讨论】:

  • 是的,我的 soe 实现使用位筛在 0.052 秒内计算出 2000 万以下的 1270607。
  • @user3114046:这听起来肯定比“几秒钟”更可敬。
  • 大约一年前,我花了相当长的时间试图用我自己的 Meissel-Lehmer 筛子计算较大的 pi(x) 值。我只达到了 pi(10^20) 之类的东西。 bit sieve 使用硬件 popcnt 功能快速计数素数,这实际上是很多筛分的开销。我在 C 中找不到我的普通 SOE 实现,但在 python 中运行一个可以在 0.71 秒内获得 100 万个素数。
  • 哦 - 如果你从 p^2 而不是 2*p for (int temp = i*i; temp&lt;number; temp += i)开始筛选,你会获得一点速度。
  • @user3114046:是的,我意识到有很多可用的优化——我只是把这些作为你应该能够期望的最低的快速基线即使是最简单、优化程度最低的实现(没有真正努力故意减慢它)。
【解决方案2】:

vector 是一个位集。更新不在缓存中的位集值是昂贵的。试试vector,写起来便宜很多。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-11-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多