소수를 찾는 가장 빠른 알고리즘은 무엇입니까?


183

C ++를 사용하여 소수를 찾는 가장 빠른 알고리즘은 무엇입니까? 나는 체의 알고리즘을 사용했지만 여전히 더 빠르기를 원합니다!


내가 찾은 흥미로운 기사 : 흥미 롭다 :
소수

29
@Jaider 7 (111)의 낮은 숫자에서는 실패합니다. 1001 = 9에도 실패합니다. 그리고 명확하게는 (- 1, 메르 센 소수이다 - 고전 생성 예 - 즉 항상 양식 (111) ... (1)이 될 것입니다 경우 2 ^ P 적용되지 않습니다) 거의 모든 소수의 일반적인 실패
다니엘 캣츠를

1
@Kasperasky-당신은 어느 체를 언급하지 않았습니까? 아마도 Eranthoses의 체를 의미 할 것입니다!
user2618142

에라토스테네스 체 알고리즘
Emad Aghayi

답변:


79

Atkin체를 매우 빠르게 구현 한 것은 Dan Bernstein의 primgen 입니다. 이 체는 에라토스테네스 보다 더 효율적 입니다. 그의 페이지에는 몇 가지 벤치 마크 정보가 있습니다.


10
실제로 나는 primgen이 가장 빠르거나 심지어 두 번째로 빠르지 않다고 생각합니다. yafu와 primsieve는 일반적으로 더 빠르며 확실히 2 ^ 32 이상입니다. 둘 다 Atkin-Bernstein 체가 아니라 Eratosthenes 체입니다.
찰스

5
Primeeve Eratosthenes (SoE) 체는 가능한 가장 빠른 알고리즘이며, Primsieve는 SoA에 비해 연산 수를 줄이므로 Bernstein을 포함하여 Atkin SoA의 체 구현보다 항상 빠릅니다. 비트 수 범위 (2 ^ 32-1), primsieve는 약 12 ​​억 개의 컬을 수행하는 반면 SoA는 총 약 14 억 개의 결합 된 토글 및 스퀘어없는 연산을 수행합니다. 두 연산은 거의 동일한 복잡성이며 거의 동일한에서 최적화 될 수 있습니다 방법.
GordonBGood

7
계속 : Bernstein은 SoA와 동일한 효과적인 휠 인수 분해를 사용하여 SoE를 비교했습니다. 2,3; 5 휠이며,이 휠을 사용하면 32 비트 숫자 범위에서 약 183 억 개의 컬이 발생합니다. 이로 인해 제한된 다른 SoE 버전을 비교할 때 SoA가 약 30 % 더 빨라 집니다. 그러나 프라임 시브 알고리즘은 2; 3; 5; 7; 11; 13; 17 휠 세그먼트 프리-컬과 함께 2; 3; 5; 7 휠을 사용하여 작동 횟수를 약 12 ​​억으로 줄입니다. 동등한 연산 루프 최적화로 SoA보다 16.7 % 빠릅니다.
GordonBGood

6
Continued2 : 2,3; 5 인수 분해 휠이 알고리즘의 "베이크 인 (baked-in)"부분이므로 SoA con는 더 큰 요소 휠 인수 분해를 사용하지 않습니다.
GordonBGood

4
@Eamon Nerbonne, WP는 정확합니다. 그러나 계산 복잡성이 약간 더 좋다고해서 일반적인 알고리즘이 더 빠르지는 않습니다. 이 의견에서, 나는 에라토스테네스 (SiE of Eratosthenes)의 최대 휠 인수 분해 (Atokin SoA에는 불가능)가 SoE의 작동을 최대 약 10 억 범위까지 약간 줄인다는 것을 언급하고 있습니다. 이 점을 훨씬 넘어 서면 일반적으로 메모리 제한을 극복하기 위해 페이지 분할을 사용해야하며, SoA가 실패하는 경우 범위가 증가함에 따라 지속적으로 증가하는 일정한 오버 헤드가 발생합니다.
GordonBGood

29

정말 빠르면 프라임 목록을 포함시킬 수 있습니다 :
http://www.bigprimes.net/archive/prime/

특정 숫자가 소수인지 알아야 할 경우 wikipedia에 다양한 소수 테스트가 나열됩니다 . 그들은 숫자 인 경우 그들이 당신을 말할 수있는, 특히 있기 때문에, 아마도 많은 수의 소수 있는지 확인하는 가장 빠른 방법입니다 하지 주요.


2
모든 프라임 목록? 나는 당신이 처음 몇 소수의 목록을 의미한다고 생각합니다 ... :)
j_random_hacker

9
100000000을 몇 번 호출하면 그렇습니다. :)
Georg Schölly

58
확실히 100000000은 무한대와 비교하여 "몇몇"이다;)
Timofey

9
AtA의 체 (SoA)가 에라토스테네스의 체 (SoE)보다 더 빠르다고 생각하는 이유는 무엇입니까? 링크 된 Wikipedia 기사에서와 같이 의사 코드를 사용하여 프로그램을 구현하는 것은 아닙니다. SoE가 SoA에서 사용되는 것과 유사한 수준의 가능한 최적화로 SoE를 구현하는 경우 SoE보다 SoA에 대한 매우 큰 체질 범위에 대한 작업이 약간 적지 만 복잡성 증가와 이 계산 복잡성의 추가 상수 인자 오버 헤드는 실제 응용에있어서 SoE가 더 우수하다.
GordonBGood

26

그는 내가 오래된 질문에 대답하는 질문 necromancer라는 것을 알고 있지만, 효율적인 소수 테스트를 구현하는 방법을 찾기 위해이 질문을 검색했습니다.

지금까지 가장 빠른 소수 테스트 알고리즘은 SPRP (강한 확률 프라임)라고 생각합니다. Nvidia CUDA 포럼에서 인용하고 있습니다.

수 이론에서보다 실질적인 틈새 문제 중 하나는 소수를 식별하는 것과 관련이 있습니다. N이 주어지면 그것이 소수인지 어떻게 효율적으로 결정할 수 있습니까? 이것은 단순한 문제가 아니라 특정 범위 내에서 주요 해시 테이블 크기를 동적으로 찾아야 할 때 코드에서 실제로 필요한 것일 수 있습니다. N이 2 ^ 30 정도 인 경우 실제로 요인을 찾기 위해 30000 나누기 테스트를 수행 하시겠습니까? 당연히 아니.

이 문제에 대한 일반적인 실용적인 해결책은 Euler 가능성있는 소수 테스트라고하는 간단한 테스트와 SPRP (강한 가능성있는 소수)라고하는보다 강력한 일반화입니다. 이것은 정수 N에 대해 확률 적으로이를 소수로 분류 할 수 있고 반복 된 테스트로 정확성 확률을 높일 수있는 테스트입니다. 테스트 자체의 느린 부분은 대부분 A ^ (N-1) modulo N과 비슷한 값을 계산하는 것입니다. RSA 공개 키 암호화 변형을 구현하는 사람은이 알고리즘을 사용했습니다. 512 비트와 같은 큰 정수와 일반 32 또는 64 비트 정수 모두에 유용합니다.

N 범위에 대해 항상 성공하는 것으로 알려진 특정 테스트 입력 매개 변수를 사전 계산하여 확률 적 거부에서 우선 순위의 확실한 증거로 테스트를 변경할 수 있습니다. 불행히도 이러한 "최고의 테스트"를 발견하는 것은 사실상 실제로 무한) 도메인. 1980 년 Carl Pomerance가 유용한 테스트의 첫 번째 목록을 작성했습니다 (이차적 시브 알고리즘으로 RSA-129를 인수하는 것으로 유명). 나중에 Jaeschke는 1993 년에 결과를 크게 개선했습니다. 2004 년에 Zhang과 Tang은 이론을 개선했습니다. 그리고 검색 도메인의 한계. Greathouse and 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 소수를 확인할 수 있습니다. 메르 센 소수는 2 ^ p -1 형식입니다. 나는 Lucas-Lehmer 테스트가 Mersenne 소수에서 발견 된 가장 빠른 알고리즘이라고 생각합니다.

또한 가장 빠른 알고리즘뿐만 아니라 가장 빠른 하드웨어를 사용하려면 Nvidia CUDA를 사용하여 구현하고 CUDA 용 커널을 작성한 후 GPU에서 실행하십시오.

충분히 큰 소수를 발견하면 돈을 벌 수 있습니다. EFF는 $ 50K에서 $ 250K까지 상금을 제공합니다. https://www.eff.org/awards/coop


17

AKS Primality TestP 라는 숫자 가 소수인지 또는 복합 인지를 확인하는 100 % 수학 테스트가 있습니다.

개념은 간단하다 : 숫자 주어진 P모든 계수는 경우 (x-1)^P - (x^P-1)로 나눌 수 있습니다 P, 다음 P소수이고, 그렇지 않으면 복합 숫자입니다.

예를 들어 given 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)^PP+1용어를 사용하며 조합을 사용하여 찾을 수 있습니다. 따라서이 테스트는 런타임에서 O(n)실행되므로 단순히 i0에서 반복 p하여 나머지를 테스트 할 수 있기 때문에 이것이 얼마나 유용한 지 알 수 없습니다 .


5
AKS는 실제로 알려진 다른 방법과 경쟁하지 않는 매우 느린 방법입니다. 설명하는 방법은 AKS가 아니라 최적화되지 않은 평가판 분할보다 느린 개방형 정리입니다.
DanaJ

안녕하세요 @Kousha, 무엇을 x의미합니까? 에서 (x-1)^P - (x^P-1). 이에 대한 샘플 코드가 있습니까? 정수가 소수인지 아닌지를 결정하기 위해 C ++에서?
kiLLua

@kiLLua X는 변수 일뿐입니다. 숫자가 소수인지 여부를 결정하는 것은 X의 계수입니다. 그리고 코드가 없습니다. 숫자가 소수인지 아닌지를 결정하기 위해 실제로이 방법을 사용하지 않는 것이 좋습니다. 이것은 소수의 매우 멋진 수학적 동작이지만 그렇지 않으면 엄청나게 비효율적입니다.
Kousha

5

특정 숫자가 소수인지 결정하는 데 문제가 있습니까? 그런 다음 우선 테스트가 필요합니다 (쉽게). 아니면 주어진 숫자까지 모든 소수가 필요합니까? 이 경우 프라임 시브는 양호합니다 (쉽지만 메모리가 필요합니다). 아니면 숫자의 주요 요소가 필요합니까? 이것은 인수 분해가 필요합니다 (가장 효율적인 방법을 원한다면 많은 수의 경우 어려움). 보고있는 숫자가 얼마나됩니까? 16 비트? 32 비트? 더 큰?

영리하고 효율적인 방법 중 하나는 소수 테이블을 미리 계산하여 비트 수준 인코딩을 사용하여 파일에 보관하는 것입니다. 파일은 하나의 긴 비트 벡터로 간주되는 반면 비트 n은 정수 n을 나타냅니다. n이 소수이면 비트는 1로 설정되고 그렇지 않으면 0으로 설정됩니다. 조회가 매우 빠르며 (바이트 오프셋과 비트 마스크를 계산) 파일을 메모리에로드 할 필요가 없습니다.


좋은 원시성 테스트는 합리적으로 적합 할 수있는 프라임 테이블의 기본 메모리 대기 시간과 경쟁이 치열하므로 L2에 적합하지 않으면 이것을 사용하지 않습니다.
찰스

3

Rabin-Miller 는 표준 확률 적 우선 성 테스트입니다. (K 번 실행하고 입력 번호는 확실히 복합적이거나 아마도 오류 4 -K 확률로 소수입니다 . (수백 번 반복되며 거의 확실하게 진실을 알려줍니다))

Rabin Miller 의 비 확률 적 (결정 론적) 변형이 있습니다.

큰 인터넷 메르 센 국무 검색 최대 규모의 검증 된 소수의 세계 기록을 발견했다 (원기 충전) (2 74207281 - 6 월 2017로 1) 사용 여러 가지 알고리즘을 하지만, 이러한 특별한 형태의 소수입니다. 그러나 위의 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.40GHz 프로세서가 장착 된 Core 2 Duo 랩톱에서 1에서 1,000,000 사이의 소수를 찾아 인쇄하는 데 약 82 초가 걸립니다 . 그리고 78,498 개의 소수를 찾았 습니다 .


3
이것은 너무 느립니다. 문제는 i <= (ToCheck / 3)입니다. 이어야합니다 i <= (ToCheck / i). 그것으로, 대신 0.1 초 안에 실행될 수 있습니다.
Will 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

-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)그래도 친절합니다. :)
Will Ness

1
@ Will Ness 나는 이것을 그 질문에 대한 답으로 하향 조정했을 것입니다. 매크로가 작동 할 때 왜 for 루프를 사용합니까? :)
Rob Grant

-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
이것은 소수의 소수를 생성하는 경우 많은 문제를 야기 할 것이며, sqrt가 상당히 느리기 때문에 비교를 위해 P [j] * P [j] <= k를 사용하는 것이 좋습니다
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

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

@WillNess 아마도 약간 쌍곡선 일 것입니다. 그는 2 대신 1부터 for 루프를 쉽게 시작할 수 있었고 j <i 대신 j <= i를 추가했습니다. :)
Kenny Cason

3
이 게시물을 삭제해야한다고 생각하지 않습니다.
Will Ness
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.