这个素数生成器是不是效率低下的C++

Is this prime generator inefficient C++?

本文关键字:效率 C++ 是不是      更新时间:2023-10-16

这被视为一个高效的素数生成器吗。在我看来,这是相当有效的。是流的使用使程序运行速度变慢了吗?

我正试图将此提交给SPOJ,它告诉我超过了时间限制。。。

#include <iostream>
#include <sstream>
using namespace std;
int main() {
    int testCases, first, second, counter = 0;
    bool isPrime = true;
    stringstream out;
    cin >> testCases;
    for (int i = 0; i < testCases; i++) {
        // get the next two numbers
        cin >> first >> second;
        if (first%2 == 0)
            first++;
        // find the prime numbers between the two given numbers
        for (int j = first; j <= second; j+=2) {
            // go through and check if j is prime
            for (int k = 2; k < j; k++) {
                if (j%k == 0) {
                    isPrime = false;
                    break;
                }
            }
            if (isPrime) {
                out << j << "n";
            }
            isPrime = true;
        }
        out << "n";
    }
    cout << out.str();
    return 0;
}

编辑:该程序应该在输入中指定的数字之间生成素数。(请参阅此处了解更多详细信息:Prime Generator问题)

-Tomek

这比朴素算法高出一步(跳过偶数)。我建议将埃拉托斯梯尼筛作为一种更有效的算法。从上面的链接:

算法的复杂性为O((nlogn)(loglogn))与内存O(n)的要求。分段的Eratosthenes筛的版本,具有基本优化功能,如车轮因式分解,使用O(n)运算以及记忆力

你给出的算法在O(n^2)附近。跳过偶数得到的加速并不是很好,因为你会发现偶数在第一次测试中不是素数。sive对内存的要求要高得多,但对于大型N,运行时的复杂性要高得多。

您搜索的数字比您必须搜索的要多,最多只需要转到<= (sqrt(num))

这里有一个简单的Eratosthenes筛。它不需要预先定义一个大的布尔数组,但它在时间和空间上仍然是>>O(n)。不过,只要你有足够的记忆力,它应该比你现在天真的方法快得多。

#include <iostream>
#include <map>
using namespace std;
template<typename T = int, typename M = map<T, T> >
class prime_iterator {
    public:
        prime_iterator() : current(2), skips() { skips[4] = 2; }
        T operator*() { return current; }
        prime_iterator &operator++() {
            typename M::iterator i;
            while ((i = skips.find(++current)) != skips.end()) {
                T skip = i->second, next = current + skip;
                skips.erase(i);
                for (typename M::iterator j = skips.find(next);
                        j != skips.end(); j = skips.find(next += skip)) {}
                skips[next] = skip;
            }
            skips[current * current] = current;
            return *this;
        }
    private:
        T current;
        M skips;
};
int main() {
    prime_iterator<int> primes;
    for (; *primes < 1000; ++primes)
        cout << *primes << endl;
    return 0;
}

如果这对你来说仍然太慢,你可能想追求阿特金筛,一种优化的Eratosthenes筛。

实际上,只有当生成素数的范围开始较低时,这些方法才相对有效。如果下限已经相当大,而上限并没有比下限大多少,那么筛选方法就是浪费工作,你最好进行一次初选测试。

还有一件事,不要在循环中使用sqrt(n):

for(int k=1;k<sqrt(n);++k)

如果没有好的优化,那么每次迭代都会计算sqrt。

使用

for (int k=1;k*k < n;++k)

或者只是

int sq = sqrt ( n );
for (int k=1;k<sq;++k)

它可以稍微提高效率。你不需要从2开始k,你已经在确保不要测试偶数了。所以从3开始k。
然后每次将k增加2,因为你不需要测试其他偶数。我能想到的最有效的方法是只测试一个数字是否可以被已知素数整除(然后当你找到另一个素数时,把它加到你测试的列表中)。

for (int k = 2; k < j; k++) {
     if (j%k == 0) {
         isPrime = false;
         break;
     }
}

应该是:

for(int k = 3; k <= j/2; k+=2 )
{
  if( j % k == 0 )
      break;
}

j/2确实应该是sqrt(j),但它通常是一个足够好的估计。