哪一个找到质数最快的算法?


183

使用C ++找出质数最快的算法是哪一种?我已经使用了sieve的算法,但我仍然希望它更快!


我发现了一篇旧文章,但看起来很有趣:质数有趣
Mvcoile 2012年

29
@Jaider对于低至7(111)的数字将失败。对于1001 = 9,它也会失败。显然,它对于几乎所有的素数都无效(不包括2 ^ p-1的情况,这是梅森素数-经典生成的示例-始终为111 ... 1的形式)
Daniel Kats

1
@Kasperasky-您没有提到哪个筛子?您可能是说Eranthoses筛!
user2618142

Eratosthenes算法
筛网

Answers:


79

丹·伯恩斯坦(Dan Bernstein)的primegen 是阿特金筛网(Sieve of Atkin)的非常快速的实现。该筛子比Eratosthenes筛子更有效。他的页面上有一些基准信息。


10
实际上,我不认为primegen是最快的,甚至不是第二快的。我认为yafu和primesieve总体上都更快,并且肯定超过2 ^ 32。两者都是(改良的)Eratosthenes筛子,而不是Atkin-Bernstein筛子。
查尔斯

5
Primeato的Eratosthenes筛(SoE)是可能的最快算法,并且总是比Atkin SoA的Sieve筛查的任何实现都要快,包括本答案中链接的Bernstein的筛查,因为Primesoeve与SoA相比减少了操作数量:对于32-比特数范围(2 ^ 32-1),primeiseeve进行约12亿剔除,而SoA总共进行约14亿组合切换和无平方运算,这两种操作具有相同的复杂度,并且可以在相同的程度上进行优化方式。
GordonBGood 2013年

7
续:伯恩斯坦只比较了SoE使用与SoA相同的有效转轮分解,即2; 3; 5转轮,使用该转轮会在32位数字范围内产生大约18.3亿剔除。当将此SoE 受限版本与其他等效优化进行比较时,这会使SoA快30%。但是,primesieve算法将2; 3; 5; 7车轮与2; 3; 5; 7; 11; 13; 17车轮分段预剔除结合使用,可将操作次数减少到大约12亿次具有等效操作循环优化功能的SoA速度比SoA快16.7%。
GordonBGood 2013年

6
续2:SoA con没有较高的因子轮分解,因为2; 3; 5因子分解轮是算法的“嵌入”部分,因此差异不大。
GordonBGood 2013年

4
@Eamon Nerbonne,WP是正确的;但是,仅具有稍微更好的计算复杂度并不能使通用算法更快。在这些评论中,我指的是Eratosthenes筛(SoE)的最大车轮分解系数(对于Atkin-SoA筛而言是不可能的)使SoE的运行次数略微减少,达到约十亿范围。远远超过这一点,通常需要使用页面分段来克服内存限制,那就是SoA失败的地方,随着范围的增加,固定开销的增加迅速增加。
GordonBGood 2014年

29

如果必须非常快,则可以包含素数列表:http :
//www.bigprimes.net/archive/prime/

如果您只想知道某个数字是否是质数,那么Wikipedia上列出了各种素数测试。它们可能是确定大数是否为质数的最快方法,尤其是因为它们可以告诉您数字是否不是质数。


2
所有素数的清单?我认为您的意思是前几个素数的列表... :)
j_random_hacker

9
如果您给100000000打电话,那么可以。:)
GeorgSchölly09年

58
与无穷大相比,肯定是100000000;“
Timofey 2012年

9
您为什么认为阿特金筛(SoA)快于Eratosthenes筛(SoE)?当然,不是像您链接的Wikipedia文章中那样使用伪代码实现程序的时候。如果以与SoA相似的可能优化水平来实现SoE,那么对于SoA而言,在非常大的筛分范围内进行的操作要比对SoE进行的操作要少一些,但是这种收益通常会被增加的复杂性和这种计算复杂性的额外恒定因子开销,使得对于实际应用而言,SoE更好。
GordonBGood 2014年

26

他,他,我知道我是一个死灵法师,正在回答旧问题,但是我刚刚发现这个问题正在网上搜索实现有效质数测试的方法。

到目前为止,我相信最快的质数测试算法是强概率质数(SPRP)。我引用Nvidia CUDA论坛:

数论中较实际的利基问题之一与质数的确定有关。给定N,您如何有效确定其是否为质数?这不仅是一个理论上的问题,而且可能是代码中真正需要的一个问题,也许当您需要动态地找到特定范围内的原始哈希表大小时。如果N为2 ^ 30的数量级,您是否真的要进行30000个除法测试以查找任何因子?显然不是。

解决此问题的常用方法是使用一个简单的测试(称为Euler可能素数测试)和一个更强大的概括(称为强概率素数(SPRP))。这是一个对于整数N可以概率性地将其分类为质数或不存在质数的测试,重复测试可以提高正确性的可能性。测试本身的较慢部分主要涉及计算类似于A ^(N-1)模N的值。实现RSA公钥加密变体的任何人都使用了此算法。对于巨大的整数(例如512位)以及普通的32位或64位整数,它都非常有用。

通过预先计算已知在N范围内总是成功的某些测试输入参数,可以将测试从概率拒绝更改为确定性的原始证明。不幸的是,发现这些“最知名的测试”实际上是在寻找巨大的(实际上是无限的)域。1980年,卡尔·波莫兰斯(Carl Pomerance)创建了第一个有用的测试列表(以使用二次方Seive算法而成为RSA-129的分解因子而闻名)。后来的杰斯凯(Jaeschke)于1993年对结果进行了重大改进。2004年,张和唐改进了这一理论。和搜索域的限制。到目前为止,Greathouse和Livingstone已在http://math.crg4.com/primes.html上发布了最新的搜索结果,这是一个庞大的搜索域中的最佳搜索结果。

有关更多信息,请参见此处:http : //primes.utm.edu/prove/prove2_3.htmlhttp://forums.nvidia.com/index.php?showtopic=70483

如果您只需要一种生成非常大的素数的方法,而又不想生成所有小于整数n的素数,则可以使用Lucas-Lehmer测试来验证Mersenne素数。Mersenne质数的形式为2 ^ p -1。我认为Lucas-Lehmer检验是针对梅森质数的最快算法。

而且,如果您不仅要使用最快的算法,还要使用最快的硬件,请尝试使用Nvidia CUDA来实现它,为CUDA编写内核并在GPU上运行它。

如果发现足够大的素数,您甚至可以赚到一些钱,EFF提供的奖金从$ 50K到$ 250K:https//www.eff.org/awards/coop


17

有一个100%数学测试将检查数字P是素数还是合成数,称为AKS Primality Test

概念很简单:给定一个数字P,如果的所有系数(x-1)^P - (x^P-1)都可被整除PP则为质数,否则为合成数。

例如,给定P = 3,将给出多项式:

   (x-1)^3 - (x^3 - 1)
 = x^3 + 3x^2 - 3x - 1 - (x^3 - 1)
 = 3x^2 - 3x

并且系数都可以被整除3,因此数字为素数。

举个例子P = 4,其中不是素数的会产生:

   (x-1)^4 - (x^4-1)
 = x^4 - 4x^3 + 6x^2 - 4x + 1 - (x^4 - 1)
 = -4x^3 + 6x^2 - 4x

在这里我们可以看到系数6不能被整除4,因此不是素数。

多项式(x-1)^P将成为P+1项,并且可以使用组合找到。因此,此测试将在O(n)运行时运行,因此我不知道这将有多大用处,因为您可以简单地i从0 迭代到p其余部分并进行测试。


5
AKS在实践中是一种非常缓慢的方法,与其他已知方法不具有竞争力。您所描述的方法不是AKS,而是一个开放式引理,它比未优化的试验划分要慢(如您所指出的)。
DanaJ 2014年

你好@Kousha,x代表什么?在中(x-1)^P - (x^P-1)。您是否有示例代码?在C ++中确定整数是否为质数?
kiLLua

@kiLLua X只是一个变量。X的系数确定数字是否为质数。不,我没有代码。我不建议实际使用此方法来确定数字是否为质数。这只是素数的一个很酷的数学行为,但否则效率极低。

5

您是否要确定某个数字是否为质数?然后,您需要进行素数测试(简单)。还是您需要所有给定数的素数?在那种情况下,主要的筛子是好的(容易,但需要记忆)。还是您需要数字的主要因素?这将需要分解(如果您确实想要最有效的方法,则很难进行大量分解)。您要查看的数字有多大?16位?32位?大?

一种聪明而有效的方法是预先计算素数表,并使用位级编码将它们保存在文件中。该文件被视为一个长位向量,而位n代表整数n。如果n为质数,则其位设置为1,否则设置为0。查找速度非常快(您可以计算字节偏移量和位掩码),并且不需要将文件加载到内存中。


良好的素数测试与可以合理拟合的素数表的主内存延迟相比具有竞争力,因此,除非它适合L2,否则我不会使用它。
查尔斯

3

Rabin-Miller是标准的概率素数测试。(您将其运行了K次,并且输入数字肯定是复合数字,或者它可能是素数,出现错误的可能性为4 -K。(几百次迭代,几乎可以肯定地说出了真相)

Rabin Miller有一个非概率(确定性)变体

互联网梅森素数大搜索已找到了世界对大探明黄金记录(GIMPS)(2 74207281 - 1 6月2017),采用多种算法,但这些都是特殊形式的素数。但是,上面的GIMPS页面确实包含一些常规的确定性素数测试。它们似乎表明哪种算法“最快”取决于要测试的数字的大小。如果您的数字适合64位,那么您可能不应该使用旨在处理数百万个质数的方法。


2

这取决于您的应用程序。有一些注意事项:

  • 您是否仅需要一些数字是否为质数的信息,是否需要达到一定限制的所有质数,还是(可能)所有质数?
  • 您必须处理多少数字?

对于一定大小的数字(我相信大约在几百万左右),Miller-Rabin和模拟测试仅比筛子更快。在此之下,使用试验师(如果您只有几个数字)或筛子更快。


-1

我总是使用这种方法来计算筛子算法之后的素数。

void primelist()
 {
   for(int i = 4; i < pr; i += 2) mark[ i ] = false;
   for(int i = 3; i < pr; i += 2) mark[ i ] = true; mark[ 2 ] = true;
   for(int i = 3, sq = sqrt( pr ); i < sq; i += 2)
       if(mark[ i ])
          for(int j = i << 1; j < pr; j += i) mark[ j ] = false;
  prime[ 0 ] = 2; ind = 1;
  for(int i = 3; i < pr; i += 2)
    if(mark[ i ]) ind++; printf("%d\n", ind);
 }

-1

我会让你决定是否最快。

using System;
namespace PrimeNumbers
{

public static class Program
{
    static int primesCount = 0;


    public static void Main()
    {
        DateTime startingTime = DateTime.Now;

        RangePrime(1,1000000);   

        DateTime endingTime = DateTime.Now;

        TimeSpan span = endingTime - startingTime;

        Console.WriteLine("span = {0}", span.TotalSeconds);

    }


    public static void RangePrime(int start, int end)
    {
        for (int i = start; i != end+1; i++)
        {
            bool isPrime = IsPrime(i);
            if(isPrime)
            {
                primesCount++;
                Console.WriteLine("number = {0}", i);
            }
        }
        Console.WriteLine("primes count = {0}",primesCount);
    }



    public static bool IsPrime(int ToCheck)
    {

        if (ToCheck == 2) return true;
        if (ToCheck < 2) return false;


        if (IsOdd(ToCheck))
        {
            for (int i = 3; i <= (ToCheck / 3); i += 2)
            {
                if (ToCheck % i == 0) return false;
            }
            return true;
        }
        else return false; // even numbers(excluding 2) are composite
    }

    public static bool IsOdd(int ToCheck)
    {
        return ((ToCheck % 2 != 0) ? true : false);
    }
}
}

在配备2.40 GHz处理器的Core 2 Duo笔记本电脑上,查找和打印1至1,000,000范围内的质数大约需要82秒。它发现了78,498个质数。


3
这太慢了。问题是i <= (ToCheck / 3)。应该是i <= (ToCheck / i)。使用它,它可能会在0.1秒内运行。
尼斯(Ness Ness)

-3
#include<stdio.h>
main()
{
    long long unsigned x,y,b,z,e,r,c;
    scanf("%llu",&x);
    if(x<2)return 0;
    scanf("%llu",&y);
    if(y<x)return 0;
    if(x==2)printf("|2");
    if(x%2==0)x+=1;
    if(y%2==0)y-=1;
    for(b=x;b<=y;b+=2)
    {
        z=b;e=0;
        for(c=2;c*c<=z;c++)
        {
            if(z%c==0)e++;
            if(e>0)z=3;
        }
        if(e==0)
        {
            printf("|%llu",z);
            r+=1;
        }
    }
    printf("|\n%llu outputs...\n",r);
    scanf("%llu",&r);
}    

使用r进行初始化之前
zumalifeguard 2011年

-3

我不知道任何预定义的算法,但是我创建了自己的算法,速度非常快。它可以在不到1秒的时间内处理20位数字。该程序的最大容量为18446744073709551615。该程序是:

#include <iostream>
#include <cmath>
#include <stdlib.h>

using namespace std;

unsigned long long int num = 0;

bool prime() {
    if (num % 2 == 0 || num == 1) {
        return false;
    }

    unsigned long int square_root = sqrt(num);
    for (unsigned long int i = 3; i <= square_root; i += 2) {
        if (num % i == 0) {
            return false;
        }
    }

    return true;
}

int main() {
    do {
        system("cls");
        cout << "Enter number : ";
        cin >> num;

        if (prime()) {
            cout << "The number is a prime number" << endl << endl << endl << endl;
        } else {
            cout << "The number is not a prime number" << endl << endl << endl << endl;
        }
        system("pause");
    } while (1);

    return 0;
}

-4
#include <iostream>

using namespace std;

int set [1000000];

int main (){

    for (int i=0; i<1000000; i++){
        set [i] = 0;
    }
    int set_size= 1000;
    set [set_size];
    set [0] = 2;
    set [1] = 3;
    int Ps = 0;
    int last = 2;

    cout << 2 << " " << 3 << " ";

    for (int n=1; n<10000; n++){
        int t = 0;
        Ps = (n%2)+1+(3*n);
        for (int i=0; i==i; i++){
            if (set [i] == 0) break;
            if (Ps%set[i]==0){
                t=1;
                break;
            }
        }
        if (t==0){
            cout << Ps << " ";
            set [last] = Ps;
            last++;
        }
    }
    //cout << last << endl;


    cout << endl;

    system ("pause");
    return 0;
}

12
这应该是“如何在不实际使用GOTO的情况下编写非结构化代码”的答案。所有这些混淆只是为了编写一个简单的试验师!(n%2)+1+(3*n)虽然很好。:)
内斯

1
@Will Ness我会否决这个问题的答案;为什么在宏会用时使用for循环?:)
罗布·格兰特

-4

我知道会晚一些,但是这对于从搜索中到达这里的人们可能很有用。无论如何,这里有一些JavaScript依赖于这样一个事实,即仅需要测试素数因子,因此,由代码生成的较早素数将被重新用作后面的素数。当然,所有偶数和mod 5值都将首先被滤除。结果将在数组P中,并且此代码可以在i7 PC上在1.5秒内处理1000万个质数(或在20个左右中处理1亿个质数)。用C重写它应该非常快。

var P = [1, 2], j, k, l = 3

for (k = 3 ; k < 10000000 ; k += 2)
{
  loop: if (++l < 5)
  {
    for (j = 2 ; P[j] <= Math.sqrt(k) ; ++j)
      if (k % P[j] == 0) break loop

    P[P.length] = k
  }
  else l = 0
}

2
如果生成大量素数,这会给您带来很多麻烦,并且为了进行比较,最好使用P [j] * P [j] <= k,因为sqrt非常慢
Simon

-11
#include<iostream>
using namespace std;

void main()
{
    int num,i,j,prime;
    cout<<"Enter the upper limit :";
    cin>>num;

    cout<<"Prime numbers till "<<num<<" are :2, ";

    for(i=3;i<=num;i++)
    {
        prime=1;
        for(j=2;j<i;j++)
        {
            if(i%j==0)
            {
                prime=0;
                break;
            }
        }

        if(prime==1)
            cout<<i<<", ";

    }
}

60
这是最慢的事情。
Will Ness 2012年

1
这是非常慢的,如果上限是说10000000,那么此代码将消耗大量时间!
Dixit Singla

该代码为O(N ^ 2 / log N)。没有break;它会更慢,O(N ^ 2),但这已经可以看作是编码错误。通过质数的保存和测试为O(N ^ 2 /(log N)^ 2),仅在数字的平方根以下的质数进行测试是O(N ^ 1.5 /(log N)^ 2)。
Will Ness 2014年

@WillNess也许有点夸张。他可以轻松地从1而不是2开始for循环,并添加j <= i而不是j <i。:)
肯尼·卡森

3
我认为不应删除此帖子,它可以作为有价值的反例。
Will Ness
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.