找到质数的有效方法

Efficient way of finding Primes

本文关键字:有效 方法      更新时间:2023-10-16

我有一个循环,它从3到两个用户输入的素数(这些数字可以是任何长度)的phi,找到这两个素数之间的所有素数,并将它们存储到一个数组中。

我的循环代码:
int coPrimes(int num)
{
    for (int i=3; i<=num; i++)
        if(isPrime(i))
            coprimes.push_back(i);

这个循环需要很长时间才能完成。

Enter a prime number for 'a': 601
Enter a prime number for 'b': 769
a value: 601
b value: 769
product: 462169
phi: 460800
pubKey: 19
privKey: 145515
Process returned 0 (0x0)   execution time : **756.670 s**
Press any key to continue.

我需要这个循环以更快的速度工作。有没有更有效的方法来执行这个动作?

注:我的循环调用另一个函数,isPrime,这是它的代码,如果有人想要的话。

bool isPrime(int num)
{
    bool prime = true;
    if(num < 2)
        prime = true;
    for (int i=2; i<num; i++)
        if (num % i == 0)
            prime = false;
    return prime ;
}

我理解你的问题是,你想找到某个范围[a,b]中的所有质数。

如果你有足够的内存,最快的方法可能是使用埃拉托色尼筛法。维基百科有一个很好的例子来说明它是如何工作的:

2  3  4  5  6  7  8  9  10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

列表中的第一个数字是2;每隔两个数字就划掉一个它后面的列表从2开始以2为增量进行计数(这些将是列表中2的所有倍数):

2  3  x  5  x  7  x  9   x 11  x 13  x 15  x 17  x 19  x 21  x 23  x 25

2之后的下一个数是3;划掉每一个从3开始,以增量的方式,从它后面的列表中的第三个数开始计数3(这些将是列表中所有3的倍数):

2  3  x  5  x  7  x  x   x 11  x 13  x  x  x 17  x 19  x  x  x 23  x 25

列表中3之后的下一个未划掉的数字是5;从5开始以5为增量(即5的所有倍数),划掉列表中其后每5个数字:

2  3  x  5  x  7  x  x   x 11  x 13  x  x  x 17  x 19  x  x  x 23  x  x

这比检查每个数字是否为素数要快,因为外部循环只遍历每个素数,而内部循环运行得越来越快。质数的倒数和是loglogn,所以总的时间是O(nloglogn)

比现在的O(n^2)O(n^(3/2))好得多,质数检查更快。

在您的例子中,性能下降来自您的原数测试例程。尝试以下作为第一个优化:

bool isPrime(int num)
{
    bool prime = true;
    if(num < 2)
        prime = true;
    for (int i = 2; i < (int)sqrt(num) + 1; i++)
        if (num % i == 0)
            prime = false;
    return prime ;
}

您可以在这里找到一些基本素数测试的其他优化步骤:http://en.kioskea.net/faq/977-c-language-checking-whether-an-integer-is-a-prime-number

我建议用埃拉托色尼筛法来解决你的问题。但我也看到或多或少有三种非常简单的优化可以大大提高代码的速度!

for (int i=3; i<=num; i += 2)
    if(isPrime(i))
        coprimes.push_back(i);

这将跳过每个偶数。我们知道它们不可能是质数。你的速度已经翻倍了!(不完全是几乎)。

下面是两个优化:

bool isPrime(int num)
{
    // If your function is only executed from that loop above, delete this statement.
    if(num < 2)
        return false;
    if(!(num % 2))
        return num == 2;
    int limit = sqrt(num);
    for (int i=3; i<=limit; i += 2)
        if (!(num % i))
            // You can stop after you found the first divisor
            return false;
    return true;
}

您不需要布尔值,因为除了最后一个return之外,您从不在任何地方使用它。然后首先要检查它是否能被2整除。如果是这样的话,我们可以结束了。这样就可以跳过所有的偶数因子。接下来取你的数字的平方根来检查。我知道这很耗时,但是对于较大的数字来说是值得的,因为你不需要检查大多数数字。

有了这些小小的优化,你的代码将会快得多。我猜它应该能够在不到100秒的时间内完成(如果你之前的结果超过700)。

要搜索大量的素数,最好使用埃拉托色尼的筛子和维基百科,这是一个很好的开始。从那里我学会了这个算法

看起来您可能经常使用这个程序,那么您为什么不尝试实现Eratosthenes筛并将结果存储在文件中呢?

这不仅需要将找到的素数添加到向量中,还需要将结果写入文件。这样,您可能只需要缓慢地运行它一次,并且将来您可以检查phi是否大于文件中的最后一个数字。如果不是,很好,因为您可以使用文件中的数字。如果是,就从n + 1到开始检查,其中n是文件中的最后一个素数。