【问题标题】:Sieve of Eratosthenes for large numbers c++用于大量 C++ 的 Eratosthenes 筛
【发布时间】:2015-03-19 04:06:37
【问题描述】:

就像question 一样,我也在研究 Eratosthenes 的筛子。同样来自“使用 c++ 的编程原理和实践”一书,第 4 章。我能够正确实现它,并且它的功能与练习所要求的完全一样。

#include <iostream>
#include <vector>

using namespace std;

int main() {
    unsigned int amount = 0;

    cin >> amount;

    vector<int>numbers;

    for (unsigned int i = 0; i <= amount; i++) {
        numbers.push_back(i);
    }

    for (unsigned int p = 2; p < amount; p++) {
        if (numbers[p] == 0)
            continue;

        cout << p << '\n';

        for (unsigned int i = p + p; i <= amount; i += p) {
            numbers[i] = false;
        }
    }

    return 0;
}

现在,我将如何处理amount 输入中的实数? unsigned int 类型应该允许我输入 2^32=4,294,967,296 的数字。但我不能,我的内存用完了。是的,我已经算过:存储 2^32 个 int 数量,每个 32 位。所以 32/8*2^32=16 GiB 的内存。我只有 4 GiB...

所以我在这里真正要做的是将非素数设置为零。所以我可以使用布尔值。但是,它们仍然需要 8 位,因此每个 1 字节。理论上我可以达到unsigned int (8/8*2^32=4 GiB) 的极限,将我的一些交换空间用于操作系统和开销。但是我有一台 x86_64 的 PC,那么大于 2^32 的数字呢?

知道素数是important in cryptography,一定有更有效的方法吗?还有没有办法优化找到所有这些素数所需的时间?

【问题讨论】:

    标签: c++ primes sieve-of-eratosthenes


    【解决方案1】:

    就存储而言,您可以使用std::vector&lt;bool&gt; 容器。由于它的工作原理,您必须以speed 换取存储空间。因为这实现了每个布尔值一位,所以您的存储效率提高了 8 倍。如果您的所有 RAM 都可用于这个程序,您应该可以获得接近 8*4,294,967,296 的数字。您唯一需要做的就是使用 unsigned long long 释放 64 位数字的可用性。

    注意:使用下面的代码示例测试程序,amount 输入为 80 亿,导致程序运行时内存使用量约为 80 亿。 975 MiB,证明了理论数字。

    您还可以获得一些时间,因为您可以一次声明完整的向量,无需迭代:vector&lt;bool&gt;numbers (amount, true); 创建一个 size 等于输入 amount 的向量,所有元素都设置为true。现在,您可以调整代码以将非素数设置为 false 而不是 0。

    此外,一旦您按照 筛子 直到 amount 的平方根,所有保持 正确 的数字都是质数。插入if (p * p &gt;= amount) 作为附加的continue 条件,就在您输出质数之后。此外,这对您的处理时间来说是一个不起眼的改进。

    编辑:在最后一个循环中,p 可以平方,因为直到p 平方之前的所有数字都已被先前的数字证明不是素数。

    你应该得到这样的结果:

    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    int main() {
        unsigned long long amount = 0;
    
        cin >> amount;
    
        vector<bool>numbers (amount, true);
    
        for (unsigned long long p = 2; p < amount; p++) {
            if ( ! numbers[p])
                continue;
    
            cout << p << '\n';
    
            if (p * p >= amount)
                continue;
    
            for (unsigned long long i = p * p; i <= amount; i += p) {
                numbers[i] = false;
            }
        }
    
        return 0;
    }
    

    【讨论】:

    • 你不应该在最后一个循环中从 i=p*p 开始循环吗?
    • 不,这会使数字平方。想象一下第一个循环在 p=3 处。然后 i = 3 * 3 = 9。这将跳过 6。等等……已经被 2 设置为假。让我想想 p=5。然后 i = 25。 10 已经被 2 和 15 被 3 误认为了。我认为你是对的,我会检查
    • 是的,程序输出完全相同的数字,但它提高了速度。我将编辑答案。
    【解决方案2】:

    你问了几个不同的问题。

    对于高达 2**32 的素数,筛选是合适的,但您需要分段而不是在一个大博客中工作。我的回答 here 告诉我该怎么做。

    对于大得多的密码素数,该过程是选择一个数字,然后使用概率测试(例如 Miller-Rabin 测试或 Baillie-Wagstaff 测试)对其进行质数测试。这个过程并不完美,有时可能会选择复合而不是素数,但这种情况非常罕见。

    【讨论】:

    • 如果我理解正确,段会消除我遇到的内存限制。那么有可能接近 2^64 吗? (这将远远超过我现在的限制)。
    • 它消除了对筛子大小的限制。但是您还必须预先计算和存储筛选素数,或者即时计算它们。筛到2**64是可以的,但是比较难。你可能会觉得 this page 很有趣。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-06
    • 1970-01-01
    相关资源
    最近更新 更多