Đó là thuật toán nhanh nhất để tìm số nguyên tố?


183

Thuật toán nhanh nhất để tìm ra số nguyên tố sử dụng C ++ là gì? Tôi đã sử dụng thuật toán sàng nhưng tôi vẫn muốn nó nhanh hơn!


Một bài viết cũ tôi đã tìm thấy, nhưng có vẻ thú vị: Vui với số nguyên tố
Mvcoile

29
@Jaider điều này không thành công cho các số thấp nhất là 7 (111). Nó cũng thất bại với 1001 = 9. Và rõ ràng là nó thất bại đối với hầu hết tất cả các số nguyên tố nói chung (không bao gồm trường hợp 2 ^ p - 1, đó là các số nguyên tố Mersenne - ví dụ được tạo theo cách cổ điển - sẽ luôn có dạng 111 ... 1)
Daniel Kats

1
@Kasperasky - Bạn chưa đề cập đến Sàng nào? Bạn có thể có nghĩa là Sàng của Eranthoses!
dùng2618142

Sàng của thuật toán Eratosthenes
Emad Aghayi

Câu trả lời:


79

Một thực hiện rất nhanh trong những Sàng Atkin là Dan Bernstein primegen . Sàng này hiệu quả hơn Sàng của Eratosthenes . Trang của anh ấy có một số thông tin điểm chuẩn.


10
Thật ra tôi không nghĩ primegen là nhanh nhất, hay thậm chí là nhanh thứ hai; Nói chung, yafu và primesease đều nhanh hơn nói chung, và chắc chắn là hơn 2 ^ 32. Cả hai đều là (sàng) sàng của Eratosthenes chứ không phải là sàng Atkin-Bernstein.
Charles

5
Primesieve S sàng of Eratosthenes (SoE) là thuật toán rất nhanh nhất có thể và sẽ luôn nhanh hơn bất kỳ triển khai nào của Sàng của Atkin SoA, bao gồm cả Bernstein như được liên kết trong câu trả lời này bởi vì primesease làm giảm số lượng hoạt động so với SoA: For 32- Phạm vi số bit (2 ^ 32 - 1), số nguyên tố thực hiện khoảng 1,2 tỷ lần loại bỏ trong khi SoA thực hiện tổng cộng khoảng 1,4 tỷ hoạt động chuyển đổi và hoạt động miễn phí bình phương, cả hai hoạt động đều có cùng độ phức tạp và có thể được tối ưu hóa trong cùng một mức độ đường.
GordonBood

7
Tiếp tục: Bernstein chỉ so sánh SoE sử dụng hệ số bánh xe hiệu quả tương tự như đối với SoA, đó là bánh xe 2; 3; 5, sử dụng bánh xe dẫn đến khoảng 1,83 tỷ cull trong phạm vi số 32 bit; điều này làm cho SoA nhanh hơn khoảng 30% khi so sánh phiên bản SoE bị hạn chế này để tối ưu hóa tương đương khác. Tuy nhiên, thuật toán primesease sử dụng bánh xe 2; 3; 5; 7 kết hợp với phân đoạn trước 2; 3; 5; 7; 11; 13; 17 để giảm số lượng hoạt động xuống còn khoảng 1,2 tỷ để chạy Nhanh hơn 16,7% so với SoA với tối ưu hóa vòng lặp hoạt động tương đương.
GordonBood 6/12/13

6
Tiếp tục2: SoA con không có hệ số bánh xe có hệ số cao hơn được sử dụng để tạo ra nhiều sự khác biệt vì bánh xe nhân tố 2; 3; 5 là một phần của "thuật toán".
GordonBood 6/12/13

4
@Eamon Nerbonne, WP là chính xác; tuy nhiên, chỉ cần có độ phức tạp tính toán tốt hơn một chút sẽ không tạo ra thuật toán nhanh hơn cho sử dụng chung. Trong các bình luận này, tôi đang đề cập đến hệ số bánh xe tối đa của Sàng của Eratosthenes (SoE) (điều không thể đối với Sàng của Atkin- SoA) làm cho các hoạt động của SoE trở nên ít hơn một chút. Trên mức đó, người ta thường cần sử dụng phân đoạn trang để khắc phục các hạn chế về bộ nhớ và đó là trường hợp SoA thất bại, tăng nhanh số lượng chi phí không đổi với phạm vi tăng dần.
GordonBood

29

Nếu nó phải thực sự nhanh, bạn có thể bao gồm một danh sách các số nguyên tố:
http://www.bigprimes.net/archive/prime/

Nếu bạn chỉ cần biết nếu một số nhất định là số nguyên tố, có nhiều bài kiểm tra nguyên tố khác nhau được liệt kê trên wikipedia . Chúng có lẽ là phương pháp nhanh nhất để xác định xem số lớn có phải là số nguyên tố hay không, đặc biệt là vì chúng có thể cho bạn biết nếu một số không phải là số nguyên tố.


2
Một danh sách của tất cả các số nguyên tố? Tôi nghĩ bạn có nghĩa là một danh sách các số nguyên tố đầu tiên ... :)
j_random_hacker

9
Nếu bạn gọi 100000000 một vài, thì có. :)
Georg Schölly

58
chắc chắn 100000000 là "một vài" so với vô cùng;)
Timofey

9
Tại sao bạn nghĩ rằng Sàng của Atkin (SoA) nhanh hơn Sàng của Eratosthenes (SoE)? Chắc chắn là không khi người ta chỉ thực hiện một chương trình sử dụng mã giả như trong bài viết Wikipedia mà bạn đã liên kết. Nếu SoE được triển khai với mức độ tối ưu hóa tương tự có thể được sử dụng với SoA, thì sẽ có ít hoạt động hơn cho phạm vi sàng rất lớn đối với SoA so với SoE, nhưng mức tăng đó thường được bù đắp nhiều hơn bởi độ phức tạp tăng và thêm yếu tố chi phí không đổi của độ phức tạp tính toán này sao cho các ứng dụng thực tế, SoE tốt hơn.
GordonBood 20/03/2016

26

Anh ấy, anh ấy tôi biết tôi là một câu hỏi cần thiết trả lời các câu hỏi cũ, nhưng tôi vừa tìm thấy câu hỏi này để tìm kiếm các cách để thực hiện các bài kiểm tra số nguyên tố hiệu quả.

Cho đến bây giờ, tôi tin rằng thuật toán kiểm tra số nguyên tố nhanh nhất là Strong Probable Prime (SPRP). Tôi đang trích dẫn từ các diễn đàn NDA của CUDA:

Một trong những vấn đề thực tế hơn trong lý thuyết số có liên quan đến việc xác định các số nguyên tố. Cho N, làm thế nào bạn có thể xác định một cách hiệu quả nếu nó là số nguyên tố hay không? Đây không chỉ là một vấn đề mang tính lý thuyết, nó có thể là một vấn đề thực sự cần thiết trong mã, có lẽ khi bạn cần tự động tìm kích thước bảng băm chính trong phạm vi nhất định. Nếu N là thứ gì đó theo thứ tự 2 ^ 30, bạn có thực sự muốn thực hiện 30000 bài kiểm tra phân chia để tìm kiếm bất kỳ yếu tố nào không? Rõ ràng là không.

Giải pháp thực tế phổ biến cho vấn đề này là một thử nghiệm đơn giản gọi là thử nghiệm nguyên tố có thể xảy ra của Euler và một khái quát mạnh mẽ hơn được gọi là Prime Prime có thể mạnh mẽ (SPRP). Đây là một thử nghiệm mà đối với một số nguyên N có thể phân loại chính xác nó là số nguyên tố hay không, và các thử nghiệm lặp lại có thể làm tăng xác suất chính xác. Phần chậm của bản thân bài kiểm tra chủ yếu liên quan đến việc tính toán một giá trị tương tự như modulo A ^ (N-1) N. Bất kỳ ai thực hiện các biến thể mã hóa khóa công khai RSA đều sử dụng thuật toán này. Nó hữu ích cho cả số nguyên lớn (như 512 bit) cũng như ints 32 hoặc 64 bit thông thường.

Thử nghiệm có thể được thay đổi từ một từ chối xác suất thành một bằng chứng xác thực về tính nguyên thủy bằng cách tính toán trước các tham số đầu vào thử nghiệm được biết là luôn thành công trong phạm vi của N. Thật không may, việc phát hiện ra các "thử nghiệm nổi tiếng nhất" này thực sự là một tìm kiếm rất lớn ( trong thực tế vô hạn) miền. Vào năm 1980, một danh sách các thử nghiệm hữu ích đầu tiên đã được Carl Pomerance tạo ra (nổi tiếng là nhân tố RSA-129 với thuật toán Quadratic Seive của ông.) Sau đó Jaeschke đã cải thiện kết quả đáng kể vào năm 1993. Năm 2004, Zhang và Tang đã cải thiện lý thuyết và giới hạn của miền tìm kiếm. Greathouse và Livingstone đã phát hành các kết quả hiện đại nhất cho đến bây giờ trên web, tại http://math.crg4.com/primes.html , kết quả tốt nhất của một miền tìm kiếm khổng lồ.

Xem tại đây để biết thêm: http://primes.utm.edu/prove/prove2_3.htmlhttp://forums.nvidia.com/index.php?showtopic=70483

Nếu bạn chỉ cần một cách để tạo các số nguyên tố rất lớn và không quan tâm đến việc tạo tất cả các số nguyên tố <một số nguyên n, bạn có thể sử dụng phép thử Lucas-Lehmer để xác minh các số nguyên tố Mersenne. Một số nguyên tố Mersenne có dạng 2 ^ p -1. Tôi nghĩ rằng bài kiểm tra Lucas-Lehmer là thuật toán nhanh nhất được phát hiện cho các số nguyên tố Mersenne.

Và nếu bạn không chỉ muốn sử dụng thuật toán nhanh nhất mà còn cả phần cứng nhanh nhất, hãy thử triển khai nó bằng Nvidia CUDA, viết kernel cho CUDA và chạy nó trên GPU.

Bạn thậm chí có thể kiếm được một số tiền nếu bạn phát hiện ra số nguyên tố đủ lớn, EFF đang trao giải thưởng từ $ 50K đến $ 250K: https://www.eff.org/awards/coop


17

Có một bài kiểm tra toán học 100% sẽ kiểm tra xem một số Plà số nguyên tố hay tổng hợp, được gọi là Kiểm tra tính nguyên thủy của AKS .

Khái niệm này rất đơn giản: cho một số P, nếu tất cả các hệ số (x-1)^P - (x^P-1)chia hết cho P, thì đó Plà một số nguyên tố, nếu không nó là một số tổng hợp.

Chẳng hạn, đã cho P = 3, sẽ đưa ra đa thức:

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

Và các hệ số đều chia hết cho nhau 3, do đó số là số nguyên tố.

Và ví dụ, nơi P = 4KHÔNG phải là số nguyên tố sẽ mang lại:

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

Và ở đây chúng ta có thể thấy rằng các hệ số 6không chia hết cho4 , do đó nó KHÔNG phải là số nguyên tố.

Đa thức (x-1)^Psẽ P+1điều khoản và có thể được tìm thấy bằng cách sử dụng kết hợp. Vì vậy, bài kiểm tra này sẽ chạy trong O(n)thời gian chạy, vì vậy tôi không biết điều này sẽ hữu ích như thế nào vì bạn chỉ có thể lặp lại itừ 0 đến pvà kiểm tra phần còn lại.


5
AKS là một phương pháp rất chậm trong thực tế, không cạnh tranh với các phương pháp đã biết khác. Phương pháp bạn mô tả không phải là AKS mà là một bổ đề mở chậm hơn so với phân chia thử nghiệm không tối ưu hóa (như bạn chỉ ra).
DanaJ

xin chào @Kousha, xviết tắt của từ gì? trong (x-1)^P - (x^P-1). Bạn có một mã mẫu cho việc này? trong C ++ để xác định xem số nguyên có phải là số nguyên tố hay không?
kiLLua

@kiLLua X chỉ là một biến. Đó là hệ số của X xác định xem số đó có phải là số nguyên tố hay không. Và không, tôi không có mã. Tôi không khuyên bạn nên thực sự sử dụng phương pháp này để xác định xem một số có phải là số nguyên tố hay không. Đây chỉ là một hành vi toán học rất hay của các số nguyên tố, nhưng nếu không thì nó cực kỳ kém hiệu quả.
Kousha

5

Là vấn đề của bạn để quyết định xem một số cụ thể là số nguyên tố? Sau đó, bạn cần một bài kiểm tra nguyên thủy (dễ dàng). Hay bạn cần tất cả các số nguyên tố lên đến một số đã cho? Trong trường hợp đó, sàng nguyên tố là tốt (dễ, nhưng đòi hỏi bộ nhớ). Hay bạn cần các yếu tố chính của một số? Điều này sẽ đòi hỏi yếu tố hóa (khó khăn cho số lượng lớn nếu bạn thực sự muốn các phương pháp hiệu quả nhất). Làm thế nào lớn là những con số bạn đang nhìn? 16 bit? 32 bit? to hơn?

Một cách thông minh và hiệu quả là tính toán trước các bảng số nguyên tố và giữ chúng trong một tệp bằng cách sử dụng mã hóa mức bit. Tệp được coi là một vectơ bit dài trong khi bit n đại diện cho số nguyên n. Nếu n là số nguyên tố, bit của nó được đặt thành một và bằng không nếu không. Tra cứu rất nhanh (bạn tính toán bù byte và mặt nạ bit) và không yêu cầu tải tệp trong bộ nhớ.


Một thử nghiệm nguyên thủy tốt có khả năng cạnh tranh với độ trễ bộ nhớ chính cho các bảng nguyên tố có thể phù hợp một cách hợp lý, vì vậy tôi sẽ không sử dụng điều này trừ khi nó có thể phù hợp với L2.
Charles

3

Rabin-Miller là một thử nghiệm nguyên thủy xác suất tiêu chuẩn. (bạn chạy nó K lần và số đầu vào chắc chắn là tổng hợp hoặc có thể là nguyên nhân có xác suất xảy ra lỗi 4 -K . (vài trăm lần lặp và nó gần như chắc chắn cho bạn biết sự thật)

Có một biến thể không xác suất (xác định) của Rabin Miller .

Các vĩ đại Internet Mersenne Thủ Search (GIMPS) đã tìm thấy kỷ lục thế giới cho nguyên tố lớn nhất đã được chứng minh (2 74.207.281 - 1 tháng Sáu 2017), sử dụng một số thuật toán , nhưng đây là những số nguyên tố trong các hình thức đặc biệt. Tuy nhiên, trang GIMPS ở trên không bao gồm một số thử nghiệm nguyên thủy xác định chung. Chúng xuất hiện để chỉ ra rằng thuật toán nào là "nhanh nhất" phụ thuộc vào kích thước của số được kiểm tra. Nếu số của bạn vừa với 64 bit thì có lẽ bạn không nên sử dụng một phương pháp dự định hoạt động trên các số nguyên tố vài triệu chữ số.


2

Nó phụ thuộc vào ứng dụng của bạn. Có một số cân nhắc:

  • Bạn chỉ cần thông tin cho dù một vài số là số nguyên tố, bạn có cần tất cả các số nguyên tố đến một giới hạn nhất định hay bạn cần (có khả năng) tất cả các số nguyên tố?
  • Làm thế nào lớn là những con số bạn phải đối phó?

Các thử nghiệm Miller-Rabin và tương tự chỉ nhanh hơn một cái sàng cho các con số trên một kích thước nhất định (tôi tin rằng khoảng vài triệu). Dưới mức đó, sử dụng một bộ phận dùng thử (nếu bạn chỉ có một vài số) hoặc một cái sàng sẽ nhanh hơn.


-1

Tôi luôn sử dụng phương pháp này để tính các số nguyên tố theo thuật toán sàng.

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

Tôi sẽ để bạn quyết định xem nó có nhanh nhất hay không.

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);
    }
}
}

Mất khoảng 82 giây để tìm và in các số nguyên tố trong phạm vi từ 1 đến 1.000.000, trên máy tính xách tay Core 2 Duo của tôi với bộ xử lý 2,40 GHz. Và nó đã tìm thấy 78.498 số nguyên tố.


3
Đây là cách quá chậm. vấn đề là i <= (ToCheck / 3). nó phải được i <= (ToCheck / i). với nó, nó có thể chạy trong 0,1 giây thay thế.
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 được sử dụng trước khi được khởi tạo
zumalifeguard

-3

Tôi không biết về bất kỳ thuật toán được xác định trước nào nhưng tôi đã tạo ra thuật toán của riêng mình rất nhanh. Nó có thể xử lý 20 chữ số trong chưa đầy 1 giây. Khả năng tối đa của chương trình này là 18446744073709551615. Chương trình này là:

#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
đây phải là một câu trả lời về "Cách viết mã không có cấu trúc mà không thực sự sử dụng GOTO". Tất cả sự nhầm lẫn này chỉ để mã một bộ phận thử nghiệm đơn giản! (n%2)+1+(3*n)là loại tốt mặc dù. :)
Will Ness

1
@Will Ness Tôi đã đánh giá thấp điều này như một câu trả lời cho câu hỏi đó; Tại sao sử dụng vòng lặp for khi macro sẽ làm gì? :)
Rob Grant

-4

Tôi biết điều đó muộn hơn một chút, nhưng điều này có thể hữu ích cho những người đến đây từ các tìm kiếm. Dù sao, đây là một số JavaScript dựa trên thực tế là chỉ cần kiểm tra các yếu tố chính, do đó, các số nguyên tố trước đó được tạo bởi mã được sử dụng lại làm yếu tố kiểm tra cho các yếu tố sau. Tất nhiên, tất cả các giá trị chẵn và mod 5 được lọc ra trước tiên. Kết quả sẽ nằm trong mảng P và mã này có thể tạo ra 10 triệu số nguyên tố trong vòng dưới 1,5 giây trên PC i7 (hoặc 100 triệu trong khoảng 20). Viết lại bằng C nên rất nhanh.

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
Điều này sẽ mang lại cho bạn nhiều rắc rối nếu bạn tạo ra một số lượng lớn các số nguyên tố và để so sánh, sử dụng tốt hơn P [j] * P [j] <= k, vì sqrt khá chậm
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
Đây là về chậm nhất bạn có thể đi về nó.
Will Ness

1
Điều này rất chậm, nếu giới hạn trên cho phép 10000000 thì mã này sẽ tiêu tốn rất nhiều thời gian !!
Dixit Singla

mã này là O (N ^ 2 / log N). không có break;nó thậm chí còn chậm hơn, O (N ^ 2), nhưng đó có thể được coi là một lỗi mã hóa. lưu và kiểm tra theo các số nguyên tố là O (N ^ 2 / (log N) ^ 2) và kiểm tra theo các số nguyên tố chỉ dưới căn bậc hai của số, là O (N ^ 1.5 / (log N) ^ 2).
Will Ness

@WillNess Có lẽ hơi cường điệu. Anh ta có thể dễ dàng bắt đầu vòng lặp for từ 1 thay vì 2 và thêm j <= i thay vì j <i. :)
Kenny Cason

3
Tôi không nghĩ bài đăng này nên bị xóa, nó phục vụ như một ví dụ có giá trị.
Will Ness
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.