C - xác định xem một số có phải là số nguyên tố hay không


75

Tôi đang cố gắng tìm ra một phương thức lấy một số nguyên và trả về một boolean để cho biết số đó có phải là số nguyên tố hay không và tôi không biết nhiều C; có ai quan tâm để cho tôi một số gợi ý?

Về cơ bản, tôi sẽ làm điều này trong C # như thế này:

static bool IsPrime(int number)
{
    for (int i = 2; i < number; i++)
    {
        if (number % i == 0 && i != number)
            return false;
    }
    return true;
}

1
Chắc chắn đây là một câu hỏi toán học hơn là một câu hỏi lập trình?
bdonlan

49
Đây là một số con trỏ: int * ptr; int * ptr2; int * ptr3. Xin lỗi không thể giúp nó. Các con số bạn sẽ kiểm tra lớn đến mức nào? Và ngoài ra, bạn có muốn một heuristic hoặc một cái gì đó luôn hoạt động?
AlbertoPL

3
Hãy đến với bạn thuật toán (theo cách bạn kiểm tra nó mà không cần code) và sau đó có lẽ chúng ta có thể giúp thể hiện nó trong C.
BobbyShaftoe

12
Điểm của 'i! = Number' là gì khi bạn có 'i <number' làm điều kiện để thực hiện vòng lặp?
Matthieu M.

3
Cũng lưu ý rằng việc kiểm tra i < numberlà quá mức cần thiết. Theo định nghĩa, nếu một số x = a * b, ahoặc b< int(sqrt(x))và số kia lớn hơn. Vì vậy, vòng lặp của bạn chỉ cần đi lên int(sqrt(x)).
twalberg

Câu trả lời:


153

OK, vậy quên C. Giả sử tôi đưa cho bạn một số và yêu cầu bạn xác định xem nó có phải là số nguyên tố hay không. Bạn làm nó như thế nào? Viết ra các bước rõ ràng, sau đó lo lắng về việc dịch chúng thành mã.

Khi bạn đã xác định được thuật toán, bạn sẽ dễ dàng hơn nhiều trong việc tìm ra cách viết một chương trình và để những người khác giúp bạn thực hiện nó.

chỉnh sửa: Đây là mã C # bạn đã đăng:

static bool IsPrime(int number) {
    for (int i = 2; i < number; i++) {
        if (number % i == 0 && i != number) return false;
    }
    return true;
}

Đây là C gần như hợp lệ; không có boolkiểu trong C và không có truehoặc false, vì vậy bạn cần sửa đổi nó một chút (chỉnh sửa: Kristopher Johnson chỉ ra một cách chính xác rằng C99 đã thêm tiêu đề stdbool.h). Vì một số người không có quyền truy cập vào môi trường C99 (nhưng bạn nên sử dụng môi trường này!), Hãy thực hiện thay đổi rất nhỏ đó:

int IsPrime(int number) {
    int i;
    for (i=2; i<number; i++) {
        if (number % i == 0 && i != number) return 0;
    }
    return 1;
}

Đây là một chương trình C hoàn toàn hợp lệ thực hiện những gì bạn muốn. Chúng ta có thể cải thiện nó một chút mà không cần nỗ lực quá nhiều. Đầu tiên, lưu ý rằng iluôn luôn nhỏ hơn number, vì vậy việc kiểm tra i != numberluôn thành công; chúng ta có thể thoát khỏi nó.

Ngoài ra, bạn không thực sự cần phải thử các số chia hết number - 1; bạn có thể ngừng kiểm tra khi đạt đến sqrt (số). Vì sqrtlà một phép toán dấu phẩy động và điều đó mang lại rất nhiều sự tinh tế, chúng tôi sẽ không thực sự tính toán sqrt(number). Thay vào đó, chúng ta chỉ có thể kiểm tra rằng i*i <= number:

int IsPrime(int number) {
    int i;
    for (i=2; i*i<=number; i++) {
        if (number % i == 0) return 0;
    }
    return 1;
}

Tuy nhiên, một điều cuối cùng; có một lỗi nhỏ trong thuật toán ban đầu của bạn! Nếu numberlà số âm, hoặc không hoặc một, hàm này sẽ xác nhận rằng số là số nguyên tố. Bạn có thể muốn xử lý điều đó đúng cách và bạn có thể muốn numberkhông có dấu, vì bạn có nhiều khả năng chỉ quan tâm đến các giá trị tích cực:

int IsPrime(unsigned int number) {
    if (number <= 1) return 0; // zero and one are not prime
    unsigned int i;
    for (i=2; i*i<=number; i++) {
        if (number % i == 0) return 0;
    }
    return 1;
}

Đây chắc chắn không phải là cách nhanh nhất để kiểm tra xem một số có phải là số nguyên tố hay không, nhưng nó hoạt động và khá đơn giản. Chúng tôi hầu như không phải sửa đổi mã của bạn!


11
FYI, các định nghĩa tiêu chuẩn C99 một <stdbool.h> tiêu đề cung cấp bool, truefalse.
Kristopher Johnson

27
Tôi biết rằng nó là đơn giản để tính toán một hình vuông hơn một căn bậc hai, tuy nhiên tính toán một vuông trên mỗi lần lặp phải chi phí nhiều mà tính căn bậc hai lần và được thực hiện với nó: x
Matthieu M.

6
Trên một máy không có thứ tự hiện đại, độ trễ của lệnh mul đối với i bình phương phải hoàn toàn ẩn trong độ trễ của mô-đun, vì vậy sẽ không có hiệu suất chiến thắng đáng kể. Trên một máy có thứ tự nghiêm ngặt, có một chiến thắng cần có bằng cách sử dụng căn bậc hai được nâng lên, nhưng điều đó có khả năng làm phát sinh vấn đề không chính xác dấu phẩy động nếu mã được biên dịch trên nền tảng có kiểu int lớn (64 bit trở lên) . Tất cả những gì có thể được giải quyết, nhưng tôi nghĩ tốt nhất là giữ mọi thứ đơn giản và dễ di chuyển. Rốt cuộc, nếu bạn quan tâm đến tốc độ, bạn hoàn toàn không sử dụng thuật toán này.
Stephen Canon

5
@Tom bạn có thể cải thiện nhiều hơn nữa bằng cách dừng lại ở sàn (sqrt (số)). Lấy ví dụ 11, sàn (sqrt (11)) = 3. Số đứng sau 3 là 4, 3 * 4 = 12> 11. Nếu bạn đang sử dụng một cái sàng ngây thơ để kiểm tra tính nguyên thủy, bạn chỉ cần kiểm tra lẻ. số lên đến sqrt của bản gốc, ngoài 2.
Calyth

3
-1. Hàm cuối cùng đưa ra câu trả lời không chính xác cho 4294967291 .
davidg

27

Tôi ngạc nhiên rằng không ai đề cập đến điều này.

Sử dụng sàng của Eratosthenes

Chi tiết:

  1. Về cơ bản các số không phải là số chia hết cho một số khác ngoài 1 và chính chúng
  2. Do đó: một số không đúng sẽ là tích của các số nguyên tố.

Rây của Eratosthenes tìm một số nguyên tố và lưu trữ nó. Khi một số mới được kiểm tra tính nguyên tố, tất cả các số nguyên tố trước đó sẽ được kiểm tra với danh sách số nguyên tố đã biết.

Lý do:

  1. Thuật toán / sự cố này được gọi là " Song song đáng xấu hổ "
  2. Nó tạo ra một tập hợp các số nguyên tố
  3. Đây là một ví dụ về một vấn đề lập trình động
  4. Nó nhanh chóng!

8
Nó cũng O(n)nằm trong không gian và miễn là tính toán của bạn chỉ dành cho một giá trị duy nhất, thì đây là một sự lãng phí không gian rất lớn mà không tăng hiệu suất.
R .. GitHub DỪNG TRỢ GIÚP ICE

3
(Trên thực tế O(n log n)hoặc lớn hơn nếu bạn đang hỗ trợ số lượng lớn ...)
R .. GitHub NGỪNG TRỢ GIÚP TỪ

2
Ai chỉ tính 1 giá trị cho một số nguyên tố trong vòng đời của ứng dụng? Số nguyên tố là một ứng cử viên tốt để được lưu vào bộ nhớ đệm.
tu

2
Một chương trình dòng lệnh kết thúc sau một truy vấn sẽ là một ví dụ rõ ràng. Trong mọi trường hợp, việc duy trì trạng thái toàn cầu là điều xấu và luôn phải được coi là một sự đánh đổi. Và tôi sẽ đi xa hơn khi nói rằng cái sàng (được tạo ra trong thời gian chạy) về cơ bản là vô dụng. Nếu các ứng cử viên chính của bạn đủ nhỏ để bạn có thể đặt vừa một cái sàng có kích thước như vậy trong bộ nhớ, bạn chỉ nên có một bản static constđồ bit về những số nào là số nguyên tố và sử dụng nó, thay vì lấp đầy nó trong thời gian chạy.
R .. GitHub NGỪNG TRỢ GIÚP ICE

1
Sieve of Eratosthenes là một cách tốt (tốt, tốt) để giải quyết vấn đề "tạo tất cả các số nguyên tố lên đến n ". Đó là một cách lãng phí để giải quyết vấn đề " n số nguyên tố?"
hobbs

16

Stephen Canon đã trả lời nó rất tốt!

Nhưng

  • Thuật toán có thể được cải thiện hơn nữa bằng cách quan sát rằng tất cả các số nguyên tố đều có dạng 6k ± 1, ngoại trừ 2 và 3.
  • Điều này là do tất cả các số nguyên có thể được biểu diễn dưới dạng (6k + i) với một số nguyên k và với i = −1, 0, 1, 2, 3 hoặc 4; 2 phép chia (6k + 0), (6k + 2), (6k + 4); và 3 phép chia (6k + 3).
  • Vì vậy, một phương pháp hiệu quả hơn là kiểm tra xem n có chia hết cho 2 hoặc 3 hay không, sau đó kiểm tra tất cả các số có dạng 6k ± 1 ≤ √n.
  • Điều này nhanh gấp 3 lần so với việc kiểm tra tất cả m lên đến √n.

    int IsPrime(unsigned int number) {
        if (number <= 3 && number > 1) 
            return 1;            // as 2 and 3 are prime
        else if (number%2==0 || number%3==0) 
            return 0;     // check if number is divisible by 2 or 3
        else {
            unsigned int i;
            for (i=5; i*i<=number; i+=6) {
                if (number % i == 0 || number%(i + 2) == 0) 
                    return 0;
            }
            return 1; 
        }
    }
    

2
bạn nên trả về 0khi (số == 1), vì 1 không phải là số nguyên tố.
Ahmad Ibrahim

Những loại tối ưu này không liên quan đến IMO cho nhiệm vụ này: tại sao dừng lại ở dạng 6k ± 1 ngoại trừ 2 và 3 , viết lại trong n ^ 2 mod 6 = 1 , khi bạn có thể có n ^ 4 mod 30 = 1 ngoại trừ 2,3 , 5 ... trên thực tế, bạn có thể đi mãi mãi vì bạn đang sử dụng số nguyên tố để làm tối ưu hóa này ... và đây là rất nguyên tắc của Sieve tổng quát hơn của Eratosthenes thuật toán :)
ghilesZ

1
@GhilesZ: Tôi không đồng ý, điều này rất liên quan đến vấn đề và với một "||" duy nhất cho phép vòng lặp cơ bản chạy hiệu quả nhanh hơn gấp 3 lần.
verdy_p

Ngoài ra cho số == 1 nó đang trở lại một cách chính xác 0 (không thủ) với confition thử nghiệm "(số% 2 == 0)", si không có lỗi gì cả
verdy_p

Eratosthene methoid là một phương pháp hoàn toàn khác đòi hỏi phải phân bổ một mảng boolean lớn O (n) và nó không nhất thiết phải nhanh hơn do các truy cập được lập chỉ mục. Mã này là tốt vì nó tối ưu hóa trường hợp của hai số nguyên tố đầu tiên 2 và 3 (đó là lý do tại sao vòng lặp bước theo 2 * 3).
verdy_p

10
  1. Xây dựng một bảng các số nguyên tố nhỏ và kiểm tra xem chúng có chia cho số đầu vào của bạn không.
  2. Nếu con số sống sót là 1, hãy thử kiểm tra tính nguyên thủy giả với cơ sở ngày càng tăng. Xem thử nghiệm tính nguyên thủy Miller-Rabin chẳng hạn.
  3. Nếu số của bạn tồn tại đến 2, bạn có thể kết luận nó là số nguyên tố nếu nó nằm dưới một số giới hạn đã biết. Nếu không, câu trả lời của bạn sẽ chỉ là "có thể là số nguyên tố". Bạn sẽ tìm thấy một số giá trị cho các giới hạn này trong trang wiki.

3
+1: hoàn thành quá mức cần thiết cho những gì người hỏi đang hỏi, nhưng vẫn đúng.
Stephen Canon

Lưu ý rằng Guy L. gần đây cũng đề xuất sử dụng Miller-Rabin trong một câu trả lời và được liên kết với rosettacode.org/wiki/Miller-Rabin_primality_test#C - hiển thị triển khai trong C bằng GMP . Mục nhập cũng có một số triển khai trong nhiều ngôn ngữ khác nhau.
Jonathan Leffler

4

chương trình này rất hiệu quả để kiểm tra một số duy nhất để kiểm tra tính nguyên thủy.

bool check(int n){
    if (n <= 3) {
        return n > 1;
    }

    if (n % 2 == 0 || n % 3 == 0) {
        return false;
    }
        int sq=sqrt(n); //include math.h or use i*i<n in for loop
    for (int i = 5; i<=sq; i += 6) {
        if (n % i == 0 || n % (i + 2) == 0) {
            return false;
        }
    }

    return true;
}

1
Để kiểm tra một số nguyên tố, bạn nên đi tất cả các cách từ i=2đến i<=ceil(sqrt(n)). Bạn đã bỏ lỡ 2 số trong bài kiểm tra của mình: Đầu tiên, ép kiểu để (int)tạo sqrt(n)đường trục thành các số thập phân. Thứ hai, bạn đã sử dụng i<sq, khi nó nên được i<=sq. Bây giờ, giả sử một số phù hợp với vấn đề này. Một số tổng hợp nceil(sqrt(n))nhân tố nhỏ hơn. Vòng lặp bên trong của bạn chạy cho tôi như: (5, 7), (11, 13), (17, 19), (23, 25), (29, 31), (35, 37), (41, 43), và như vậy, n%in%(i+2). Giả sử chúng ta nhận được sqrt(1763)=41.98. Là 1763=41*43một số tổng hợp. Vòng lặp của bạn sẽ chỉ chạy cho đến khi (35, 37)thất bại.
DrBeco

@DrBeco quan sát tốt đẹp! cảm ơn chẳng hạn. đã cập nhật mã.
GorvGoyl

2
Sau khi phân tích kỹ ceil()vấn đề, tôi nhận ra rằng mặc dù rất nhiều trang web giới thiệu nó, nhưng nó chỉ là quá mức cần thiết. Bạn có thể trung kế và kiểm tra chỉ i<=sqrt(n)và nó sẽ ổn. Các trường hợp kiểm tra là các số nguyên tố hai tuổi lớn. Ví dụ: 86028221*86028223=7400854980481283sqrt(7400854980481283)~86028222. Và các số nguyên tố tween nhỏ hơn biết, 23, cho sqrt(6)=2.449rằng trung kế sẽ vẫn để lại 2. (Nhưng nhỏ hơn không phải là một trường hợp thử nghiệm, chỉ là một so sánh để đưa ra quan điểm). Vì vậy, có, thuật toán là chính xác. Không cần sử dụng ceil().
DrBeco

3

Kiểm tra môđun của mỗi số nguyên từ 2 trở lên đến gốc của số bạn đang kiểm tra.

Nếu môđun bằng 0 thì nó không phải là số nguyên tố.

mã giả:

bool IsPrime(int target)
{
  for (i = 2; i <= root(target); i++)
  {
    if ((target mod i) == 0)
    {
      return false;
    }
  }

  return true;
}

2
Tất nhiên, nhược điểm là sqrt được tính trên mỗi lần lặp, điều này sẽ làm chậm nó đi rất nhiều.
Rich Bradshaw

9
Bất kỳ trình biên dịch hợp lý nào cũng có thể phát hiện ra rằng gốc (đích) là một bất biến của vòng lặp và nâng nó lên.
Stephen Canon

1
(và nếu bạn có một trình biên dịch mà không thể làm tối ưu hóa điều đó, bạn nên hoàn toàn nộp một lỗi để cho trình biên dịch nhà văn bí quyết rằng họ đang bỏ lỡ tối ưu hóa này.)
Stephen Canon

cùng với nhiều khả năng tối ưu (vi mô) khác, Nếu bạn lấy sqrt theo cách thủ công trước câu lệnh for, bạn cũng có thể kiểm tra mod của điều đó (và trả về false nếu 0).
Matt Lacey

2
Nếu giá trị mục tiêu là 1 thì sao?
ffffff01 5/12/12

3

Sau khi đọc câu hỏi này, tôi bị thu hút bởi thực tế là một số câu trả lời cung cấp tối ưu hóa bằng cách chạy một vòng lặp với bội số của 2 * 3 = 6.

Vì vậy, tôi tạo một hàm mới với ý tưởng tương tự, nhưng với bội số của 2 * 3 * 5 = 30.

int check235(unsigned long n)
{
    unsigned long sq, i;

    if(n<=3||n==5)
        return n>1;

    if(n%2==0 || n%3==0 || n%5==0)
        return 0;

    if(n<=30)
        return checkprime(n); /* use another simplified function */

    sq=ceil(sqrt(n));
    for(i=7; i<=sq; i+=30)
        if (n%i==0 || n%(i+4)==0 || n%(i+6)==0 || n%(i+10)==0 || n%(i+12)==0 
           || n%(i+16)==0 || n%(i+22)==0 || n%(i+24)==0)
            return 0;

        return 1;
}

Bằng cách chạy cả hai chức năng và kiểm tra thời gian, tôi có thể nói rằng chức năng này thực sự nhanh hơn. Cho phép xem 2 bài kiểm tra với 2 số nguyên tố khác nhau:

$ time ./testprimebool.x 18446744069414584321 0
f(2,3)
Yes, its prime.    
real    0m14.090s
user    0m14.096s
sys     0m0.000s

$ time ./testprimebool.x 18446744069414584321 1
f(2,3,5)
Yes, its prime.    
real    0m9.961s
user    0m9.964s
sys     0m0.000s

$ time ./testprimebool.x 18446744065119617029 0
f(2,3)
Yes, its prime.    
real    0m13.990s
user    0m13.996s
sys     0m0.004s

$ time ./testprimebool.x 18446744065119617029 1
f(2,3,5)
Yes, its prime.    
real    0m10.077s
user    0m10.068s
sys     0m0.004s

Vì vậy, tôi nghĩ, liệu ai đó có đạt được quá nhiều nếu khái quát không? Tôi đã nghĩ ra một hàm sẽ thực hiện một cuộc bao vây trước tiên để làm sạch một danh sách các số nguyên tố ban đầu đã cho, và sau đó sử dụng danh sách này để tính toán số nguyên tố lớn hơn.

int checkn(unsigned long n, unsigned long *p, unsigned long t)
{
    unsigned long sq, i, j, qt=1, rt=0;
    unsigned long *q, *r;

    if(n<2)
        return 0;

    for(i=0; i<t; i++)
    {
        if(n%p[i]==0)
            return 0;
        qt*=p[i];
    }
    qt--;

    if(n<=qt)
        return checkprime(n); /* use another simplified function */

    if((q=calloc(qt, sizeof(unsigned long)))==NULL)
    {
        perror("q=calloc()");
        exit(1);
    }
    for(i=0; i<t; i++)
        for(j=p[i]-2; j<qt; j+=p[i])
            q[j]=1;

    for(j=0; j<qt; j++)
        if(q[j])
            rt++;

    rt=qt-rt;
    if((r=malloc(sizeof(unsigned long)*rt))==NULL)
    {
        perror("r=malloc()");
        exit(1);
    }
    i=0;
    for(j=0; j<qt; j++)
        if(!q[j])
            r[i++]=j+1;

    free(q);

    sq=ceil(sqrt(n));
    for(i=1; i<=sq; i+=qt+1)
    {
        if(i!=1 && n%i==0)
            return 0;
        for(j=0; j<rt; j++)
            if(n%(i+r[j])==0)
                return 0;
    }
    return 1;
}

Tôi cho rằng tôi đã không tối ưu hóa mã, nhưng điều đó là công bằng. Bây giờ, các bài kiểm tra. Vì có rất nhiều bộ nhớ động, tôi dự kiến ​​danh sách 2 3 5 sẽ chậm hơn một chút so với 2 3 5 được mã hóa cứng. Nhưng nó vẫn ổn như bạn có thể thấy dưới đây. Sau đó, thời gian ngày càng nhỏ lại, dẫn đến danh sách tốt nhất là:

2 3 5 7 11 13 17 19

Với 8,6 giây. Vì vậy, nếu ai đó muốn tạo một chương trình mã hóa cứng sử dụng kỹ thuật như vậy, tôi khuyên bạn nên sử dụng danh sách 2 3 và 5, bởi vì lợi ích không lớn như vậy. Nhưng ngoài ra, nếu sẵn sàng viết mã, danh sách này là ok. Vấn đề là bạn không thể nêu tất cả các trường hợp mà không có vòng lặp, hoặc mã của bạn sẽ rất lớn (Sẽ có 1658879 ORs, đó là ||trong nội bộ tương ứng if). Danh sách tiếp theo:

2 3 5 7 11 13 17 19 23

thời gian bắt đầu lớn hơn, với 13 giây. Đây là toàn bộ bài kiểm tra:

$ time ./testprimebool.x 18446744065119617029 2 3 5
f(2,3,5)
Yes, its prime.
real    0m12.668s
user    0m12.680s
sys     0m0.000s

$ time ./testprimebool.x 18446744065119617029 2 3 5 7
f(2,3,5,7)
Yes, its prime.
real    0m10.889s
user    0m10.900s
sys     0m0.000s

$ time ./testprimebool.x 18446744065119617029 2 3 5 7 11
f(2,3,5,7,11)
Yes, its prime.
real    0m10.021s
user    0m10.028s
sys     0m0.000s

$ time ./testprimebool.x 18446744065119617029 2 3 5 7 11 13
f(2,3,5,7,11,13)
Yes, its prime.
real    0m9.351s
user    0m9.356s
sys     0m0.004s

$ time ./testprimebool.x 18446744065119617029 2 3 5 7 11 13 17
f(2,3,5,7,11,13,17)
Yes, its prime.
real    0m8.802s
user    0m8.800s
sys     0m0.008s

$ time ./testprimebool.x 18446744065119617029 2 3 5 7 11 13 17 19
f(2,3,5,7,11,13,17,19)
Yes, its prime.
real    0m8.614s
user    0m8.564s
sys     0m0.052s

$ time ./testprimebool.x 18446744065119617029 2 3 5 7 11 13 17 19 23
f(2,3,5,7,11,13,17,19,23)
Yes, its prime.
real    0m13.013s
user    0m12.520s
sys     0m0.504s

$ time ./testprimebool.x 18446744065119617029 2 3 5 7 11 13 17 19 23 29
f(2,3,5,7,11,13,17,19,23,29)                                                                                                                         
q=calloc(): Cannot allocate memory

Tái bút. Tôi đã không cố ý giải phóng (r), giao nhiệm vụ này cho Hệ điều hành, vì bộ nhớ sẽ được giải phóng ngay sau khi chương trình thoát ra, để có thêm thời gian. Nhưng sẽ là khôn ngoan nếu bạn giải phóng nó nếu bạn định tiếp tục chạy mã của mình sau khi tính toán.


TẶNG KEM

int check2357(unsigned long n)
{
    unsigned long sq, i;

    if(n<=3||n==5||n==7)
        return n>1;

    if(n%2==0 || n%3==0 || n%5==0 || n%7==0)
        return 0;

    if(n<=210)
        return checkprime(n); /* use another simplified function */

    sq=ceil(sqrt(n));
    for(i=11; i<=sq; i+=210)
    {    
        if(n%i==0 || n%(i+2)==0 || n%(i+6)==0 || n%(i+8)==0 || n%(i+12)==0 || 
   n%(i+18)==0 || n%(i+20)==0 || n%(i+26)==0 || n%(i+30)==0 || n%(i+32)==0 || 
   n%(i+36)==0 || n%(i+42)==0 || n%(i+48)==0 || n%(i+50)==0 || n%(i+56)==0 || 
   n%(i+60)==0 || n%(i+62)==0 || n%(i+68)==0 || n%(i+72)==0 || n%(i+78)==0 || 
   n%(i+86)==0 || n%(i+90)==0 || n%(i+92)==0 || n%(i+96)==0 || n%(i+98)==0 || 
   n%(i+102)==0 || n%(i+110)==0 || n%(i+116)==0 || n%(i+120)==0 || n%(i+126)==0 || 
   n%(i+128)==0 || n%(i+132)==0 || n%(i+138)==0 || n%(i+140)==0 || n%(i+146)==0 || 
   n%(i+152)==0 || n%(i+156)==0 || n%(i+158)==0 || n%(i+162)==0 || n%(i+168)==0 || 
   n%(i+170)==0 || n%(i+176)==0 || n%(i+180)==0 || n%(i+182)==0 || n%(i+186)==0 || 
   n%(i+188)==0 || n%(i+198)==0)
            return 0;
    }
    return 1;
}

Thời gian:

$ time ./testprimebool.x 18446744065119617029 7
h(2,3,5,7)
Yes, its prime.
real    0m9.123s
user    0m9.132s
sys     0m0.000s

Phần thưởng: 101- 199tất cả các loài linh trưởng đều thất bại ở đây bởi vì 101 % (11+90).
vp_arth,

1
cần phải dừng lại ở n%(i+86)hoặc sécn > i+k
vp_arth

Làm tốt lắm, thưa ngài. Tôi sẽ xem qua một chút. Cảm ơn bạn. Vấn đề tương tự xảy ra với chức năng check235()cho số nguyên tố 7, 11, 13, 17, 19, 23 và 29
DrBeco

Giải pháp: cần phải di chuyển các nhắc nhở để một mảng, lặp qua và phá vỡ lặp khii+arr[k] >= n
vp_arth

Tôi đã nghĩ về điều đó, nhưng tôi không muốn một mảng, vì ifhằng số with có thể được trình biên dịch tối ưu hóa tốt hơn. Tôi đã chỉnh sửa để thêm một ngoại lệ và giữ nguyên cấu trúc hiện tại. Nhưng tôi đồng ý, với một mảng có thể tốt hơn cho mắt người.
DrBeco

2

Tôi chỉ nói thêm rằng không có số chẵn (thanh 2) nào có thể là số nguyên tố. Điều này dẫn đến một điều kiện khác trước vòng lặp for. Vì vậy, mã kết thúc sẽ giống như sau:

int IsPrime(unsigned int number) {
    if (number <= 1) return 0; // zero and one are not prime
    if ((number > 2) && ((number % 2) == 0)) return 0; //no even number is prime number (bar 2)
    unsigned int i;
    for (i=2; i*i<=number; i++) {
        if (number % i == 0) return 0;
    }
    return 1;
}

1
int is_prime(int val)
{
   int div,square;

   if (val==2) return TRUE;    /* 2 is prime */
   if ((val&1)==0) return FALSE;    /* any other even number is not */

   div=3;
   square=9;    /* 3*3 */
   while (square<val)
   {
     if (val % div == 0) return FALSE;    /* evenly divisible */
     div+=2;
     square=div*div;
   }
   if (square==val) return FALSE;
   return TRUE;
}

Xử lý số 2 và số chẵn được giữ ngoài vòng lặp chính mà chỉ xử lý số lẻ chia cho số lẻ. Điều này là do mô-đun số lẻ một số chẵn sẽ luôn đưa ra câu trả lời khác 0, điều này làm cho những bài kiểm tra đó trở nên thừa. Hay nói một cách khác, một số lẻ thể chia hết cho một số lẻ khác nhưng không bao giờ chia hết cho một số chẵn (E * E => E, E * O => E, O * E => E và O * O => Ơ).

Một bộ phận / mô-đun thực sự tốn kém trên kiến ​​trúc x86 mặc dù chi phí khác nhau như thế nào (xem http://gmplib.org/~tege/x86-timing.pdf ). Mặt khác, các phép nhân có giá thành khá rẻ.


1

Tránh lỗi tràn

unsigned i, number;
...
for (i=2; i*i<=number; i++) {  // Buggy
for (i=2; i*i<=number; i += 2) {  // Buggy
// or
for (i=5; i*i<=number; i+=6) { // Buggy

Các dạng này không chính xác khi numberlà số nguyên tố và i*igần giá trị lớn nhất của kiểu.

Vấn đề tồn tại với tất cả các kiểu số nguyên signed, unsignedvà rộng hơn.

Thí dụ:

Gọi UINT_MAX_SQRTlà tầng của căn bậc hai của giá trị nguyên lớn nhất. Ví dụ: 65535 khi unsignedlà 32-bit.

Với for (i=2; i*i<=number; i++), thất bại 10 năm tuổi này xảy ra vì khi UINT_MAX_SQRT*UINT_MAX_SQRT <= numbernumberlà số nguyên tố, lần lặp tiếp theo dẫn đến tràn phép nhân. Đã loại được một loại, tràn là UB. Với các kiểu không dấu , bản thân nó không phải là UB, nhưng logic đã bị chia nhỏ. Các tương tác tiếp tục cho đến khi vượt quá một sản phẩm bị cắt bớtnumber . Một kết quả không chính xác có thể xảy ra. Với 32-bit unsigned, hãy thử 4,294,967,291 là một số nguyên tố.

Nếu some_integer_type_MAXlà một Mersenne Thủ , i*i<=numberkhông bao giờ thành sự thật.


Để tránh lỗi này, hãy xem xét rằng number%i, number/ihiệu quả trên nhiều trình biên dịch vì các phép tính thương số và phần dư được thực hiện cùng nhau, do đó không phải trả thêm chi phí để thực hiện cả hai so với chỉ 1.

Một giải pháp toàn dải đơn giản:

bool IsPrime(unsigned number) {
    for(unsigned i = 2; i <= number/i; i++){
        if(number % i == 0){
            return false;
        }
    }
    return number >= 2;
}

0

Sử dụng Sieve of Eratosthenes, tính toán khá nhanh hơn so với thuật toán số nguyên tố "đã biết trước".

Bằng cách sử dụng mã giả từ wiki của nó ( https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes ), tôi có thể có giải pháp trên C #.

public bool IsPrimeNumber(int val) {
    // Using Sieve of Eratosthenes.
    if (val < 2)
    {
        return false;
    }

    // Reserve place for val + 1 and set with true.
    var mark = new bool[val + 1];
    for(var i = 2; i <= val; i++)
    {
        mark[i] = true;
    }

    // Iterate from 2 ... sqrt(val).
    for (var i = 2; i <= Math.Sqrt(val); i++)
    {
        if (mark[i])
        {
            // Cross out every i-th number in the places after i (all the multiples of i).
            for (var j = (i * i); j <= val; j += i)
            {
                mark[j] = false;
            }
        }
    }

    return mark[val];
}

IsPrimeNumber (1000000000) mất 21 giây 758ms.

LƯU Ý : Giá trị có thể thay đổi tùy thuộc vào thông số kỹ thuật phần cứng.

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.