Giả hành mạnh mẽ Miller-Rabin


16

Cho một số nguyên không âm N, xuất ra số nguyên dương lẻ nhỏ nhất là giả ngẫu nhiên mạnh cho tất cả các Ncơ sở nguyên tố đầu tiên .

Đây là trình tự OEIS A014233 .

Các trường hợp thử nghiệm (một chỉ mục)

1       2047
2       1373653
3       25326001
4       3215031751
5       2152302898747
6       3474749660383
7       341550071728321
8       341550071728321
9       3825123056546413051
10      3825123056546413051
11      3825123056546413051
12      318665857834031151167461
13      3317044064679887385961981

Các trường hợp thử nghiệm N > 13không có sẵn vì những giá trị này chưa được tìm thấy. Nếu bạn quản lý để tìm (các) thuật ngữ tiếp theo trong chuỗi, hãy chắc chắn gửi nó / chúng cho OEIS!

Quy tắc

  • Bạn có thể chọn lấy Ngiá trị không có chỉ mục hoặc giá trị một chỉ mục.
  • Giải pháp của bạn chỉ chấp nhận được đối với các giá trị có thể biểu thị trong phạm vi số nguyên của ngôn ngữ của bạn (tối đa N = 12cho các số nguyên 64 bit không dấu), nhưng về mặt lý thuyết, giải pháp của bạn phải hoạt động cho mọi đầu vào với giả định rằng ngôn ngữ của bạn hỗ trợ các số nguyên có độ dài tùy ý.

Lý lịch

Bất kỳ số nguyên dương thậm chí xcó thể được viết theo hình thức x = d*2^snơi dlà số lẻ. dscó thể được tính bằng cách chia n2 lần cho đến khi thương số không còn chia hết cho 2. dlà thương số cuối cùng và slà số lần 2 lần chia n.

Nếu một số nguyên dương nlà số nguyên tố, thì định lý nhỏ của Fermat nói:

Fermat

Trong bất kỳ trường hữu hạn nào Z/pZ(trong đó pcó một số nguyên tố), căn bậc hai duy nhất 11-1(hoặc, tương đương, 1p-1).

Chúng ta có thể sử dụng ba sự thật này để chứng minh rằng một trong hai câu lệnh sau phải đúng với một số nguyên tố n(trong đó d*2^s = n-1rlà một số nguyên trong [0, s)):

Điều kiện Miller-Rabin

Các Kiểm tra Miller-Rabin hoạt động bằng cách kiểm tra contrapositive của tuyên bố trên: nếu có một cơ sở anhư vậy mà cả hai điều kiện trên là sai sự thật, sau đó nkhông phải là số nguyên tố. Căn cứ đó ađược gọi là nhân chứng .

Bây giờ, việc kiểm tra mọi cơ sở trong [1, n)sẽ rất tốn kém về thời gian tính toán n. Có một biến thể xác suất của thử nghiệm Miller-Rabin chỉ thử nghiệm một số cơ sở được chọn ngẫu nhiên trong trường hữu hạn. Tuy nhiên, người ta đã phát hiện ra rằng chỉ kiểm tra acác cơ sở chính là đủ, và do đó thử nghiệm có thể được thực hiện theo cách hiệu quả và xác định. Trên thực tế, không phải tất cả các cơ sở chính cần phải được kiểm tra - chỉ cần một số lượng nhất định và con số đó phụ thuộc vào kích thước của giá trị được kiểm tra tính nguyên thủy.

Nếu một số lượng cơ sở nguyên tố không đủ được kiểm tra, thử nghiệm có thể tạo ra dương tính giả - số nguyên tổng hợp lẻ trong đó thử nghiệm không chứng minh được tính phức tạp của chúng. Cụ thể, nếu một cơ sở akhông chứng minh được tính phức tạp của một số hỗn hợp lẻ, thì số đó được gọi là giả ngẫu nhiên mạnh đối với cơ sở a. Thách thức này là về việc tìm ra các số hỗn hợp lẻ là các psuedoprimes mạnh cho tất cả các cơ sở nhỏ hơn hoặc bằng Nsố nguyên tố thứ (tương đương với việc nói rằng chúng là các giả ngẫu nhiên mạnh đối với tất cả các số nguyên tố nhỏ hơn hoặc bằng Nsố nguyên tố thứ nhất) .


1
Bài đăng trên hộp cát (hiện đã bị xóa)
Mego

Là một thuật toán kiểm tra tất cả các giá trị lẻ từ 1 đến kết quả cho tính nguyên thủy giả mạnh mẽ được cho phép bởi các quy tắc?
dùng202729

@ user202729 Tôi không hiểu tại sao nó lại như vậy. Điều gì sẽ khiến bạn nghĩ rằng nó là?
Mego

Tôi sẽ đề nghị làm cho đây là một câu hỏi mã nhanh nhất bởi vì hầu hết các câu trả lời sẽ chỉ đơn giản là vũ phu.
Neil A.

@NeilA. Tôi không đồng ý rằng điều này sẽ tốt hơn khi mã nhanh nhất. Mặc dù sự thật là các câu trả lời gần như chắc chắn sẽ là sức mạnh vũ phu (vì một thuật toán khác chưa được phát triển và tôi không mong đợi PPCG sẽ làm như vậy), golf golf đơn giản hơn nhiều, có rào cản thấp hơn nhiều (vì người nộp có thể chấm điểm các giải pháp của riêng họ), không yêu cầu tôi phải chạy và ghi điểm mọi giải pháp (và đối phó với thời gian chạy cắt cổ), và vấn đề này đủ thú vị như một thử thách golf.
Mego

Câu trả lời:


4

C, 349 295 277 267 255 byte

N,i;__int128 n=2,b,o,l[999];P(m){i<N&&P(m<2?l[i++]=n:n%m?m-1:n++);}main(r){for(P(scanf("%d",&N));r|!b;)for(++n,b=i=N;i--&&b;){for(b=n-1,r=0;~b&1;b/=2)++r;for(o=1;b--;o=o*l[i]%n);for(b=o==1;r--;o=o*o%n)b|=o>n-2;for(o=r=1;++o<n;r&=n%o>0);}printf("%llu",n);}

Có đầu vào dựa trên 1 trên stdin, ví dụ:

echo "1" | ./millerRabin

Nó chắc chắn sẽ không phát hiện ra bất kỳ giá trị mới nào trong chuỗi bất cứ lúc nào sớm, nhưng nó sẽ hoàn thành công việc. CẬP NHẬT: bây giờ thậm chí chậm hơn!

  • Hơi nhanh và ngắn hơn, với cảm hứng từ câu trả lời của Neil A ( a^(d*2^r) == (a^d)^(2^r))
  • Chậm hơn đáng kể một lần nữa sau khi nhận ra rằng tất cả các giải pháp cho thách thức này sẽ là số lẻ, do đó không cần phải thực thi rõ ràng rằng chúng tôi chỉ kiểm tra các số lẻ.
  • Hiện đang sử dụng GCC __int128, ngắn hơn so với unsigned long longkhi hoạt động với số lượng lớn hơn! Ngoài ra trên các máy ít endian, printf %lluvẫn hoạt động tốt.

Ít khai thác

N,i;
__int128 n=2,b,o,l[999];
P(m){i<N&&P(m<2?l[i++]=n:n%m?m-1:n++);}
main(r){
    for(P(scanf("%d",&N));r|!b;)
        for(++n,b=i=N;i--&&b;){
            for(b=n-1,r=0;~b&1;b/=2)++r;
            for(o=1;b--;o=o*l[i]%n);
            for(b=o==1;r--;o=o*o%n)b|=o>n-2;
            for(o=r=1;++o<n;r&=n%o>0);
        }
    printf("%llu",n);
}

(Lỗi thời) Sự cố

unsigned long long                  // Use the longest type available
n=2,N,b,i,d,p,o,                    // Globals (mostly share names with question)
l[999];                             // Primes to check (limited to 999, but if you
                                    // want it to be "unlimited", change to -1u)
m(){for(o=1;p--;o=o*l[i]%n);}       // Inefficiently calculates (l[i]^p) mod n

// I cannot possibly take credit for this amazing prime finder;
// See /codegolf//a/5818/8927
P(m){i<N&&P(m<2?l[i++]=n:n%m?m-1:n++);}

main(r){
    for(
        P(scanf("%llu",&N));        // Read & calculate desired number of primes
        r|!b;){                     // While we haven't got an answer:
        n=n+1|1;                    // Advance to next odd number
        for(b=1,i=N;i--&&b;){       // For each prime...
            for(d=n-1,r=0;~d&1;d/=2)++r; // Calculate d and r: d*2^r = n-1
            // Check if there exists any r such that a^(d*2^r) == -1 mod n
            // OR a^d == 1 mod n
            m(p=d);
            for(b=o==1;r--;b|=o==n-1)m(p=d<<r);
            // If neither of these exist, we have proven n is not prime,
            // and the outer loop will keep going (!b)
        }
        // Check if n is actually prime;
        // if it is, the outer loop will keep going (r)
        for(i=r=1;++i<n;r&=n%i!=0);
    }
    printf("%llu",n);               // We found a non-prime; print it & stop.
}

Như đã đề cập, điều này sử dụng đầu vào dựa trên 1. Nhưng với n = 0, nó tạo ra 9, theo trình tự liên quan https://oeis.org/A006945 . Không còn nữa; bây giờ nó bị treo trên 0.

Nên hoạt động cho tất cả n (ít nhất là cho đến khi đầu ra đạt 2 ^ 64) nhưng cực kỳ chậm. Tôi đã xác minh nó trên n = 0, n = 1 và (sau rất nhiều chờ đợi), n = 2.


Tôi tạo ra một bước đột phá cho giải pháp của mình, và sau đó bạn chỉ cần một lần nữa thôi ... Thật tuyệt!
Neil A.

@NeilA. Lấy làm tiếc! Tôi đã chơi với các loại int ngắn hơn trước khi bạn đăng cập nhật của bạn. Tôi chắc chắn bạn sẽ tìm thấy 2 byte ở đâu đó; điều này hóa ra có khả năng cạnh tranh đáng ngạc nhiên khi xem xét đó là 2 ngôn ngữ không chơi gôn khác nhau: D
Dave

3

Python 2, 633 465 435 292 282 275 256 247 byte

Chỉ số 0

Hỏi bạn thực hiện và thử một cái gì đó mới

Chuyển đổi từ một chức năng sang một chương trình tiết kiệm một số byte bằng cách nào đó ...

Nếu Python 2 cho tôi một cách ngắn hơn để làm điều tương tự, thì tôi sẽ sử dụng Python 2. Division theo số nguyên mặc định, vì vậy cách dễ dàng hơn để chia cho 2 và printkhông cần dấu ngoặc đơn.

n=input()
f=i=3
z={2}
a=lambda:min([i%k for k in range(2,i)])
while n:
 if a():z|={i};n-=1
 i+=2
while f:
 i+=2;d,s,f=~-i,0,a()
 while~d&1:d/=2;s+=1
 for y in z:
  x=y**d%i
  if x-1:
   for _ in[]*s:
    x*=x
    if~x%i<1:break
   else:f=1
print i

Hãy thử trực tuyến!

Python nổi tiếng là chậm so với các ngôn ngữ khác.

Xác định một thử nghiệm phân chia thử nghiệm cho tính chính xác tuyệt đối, sau đó liên tục áp dụng thử nghiệm Miller-Rabin cho đến khi tìm thấy giả ngẫu nhiên.

Hãy thử trực tuyến!

EDIT : Cuối cùng đã đánh golf câu trả lời

EDIT : Được sử dụng mincho thử nghiệm tính nguyên thủy phân chia thử nghiệm và thay đổi nó thành a lambda. Ít hiệu quả hơn, nhưng ngắn hơn. Cũng không thể tự giúp mình và sử dụng một vài toán tử bitwise (không chênh lệch độ dài). Về lý thuyết, nó sẽ hoạt động (hơi) nhanh hơn.

EDIT : Cảm ơn @Dave. Biên tập viên của tôi đã troll tôi. Tôi nghĩ rằng tôi đang sử dụng các tab nhưng nó đã được chuyển đổi thành 4 không gian thay thế. Cũng đã trải qua khá nhiều mẹo Python và áp dụng nó.

EDIT : Chuyển sang lập chỉ mục 0, cho phép tôi lưu một vài byte bằng cách tạo các số nguyên tố. Cũng suy nghĩ lại một vài so sánh

EDIT : Được sử dụng một biến để lưu trữ kết quả của các bài kiểm tra thay vì các for/elsecâu lệnh.

EDIT : Đã chuyển lambdabên trong hàm để loại bỏ sự cần thiết của một tham số.

EDIT : Chuyển đổi thành chương trình để lưu byte

EDIT : Python 2 giúp tôi tiết kiệm byte! Ngoài ra tôi không phải chuyển đổi đầu vào thànhint


+1 cho cách bạn xử lý a^(d*2^r) mod n!
Dave

Bạn có biết rằng bạn có thể sử dụng thụt lề một dấu cách (hoặc một tab) trong Python để lưu lại rất nhiều byte, thực sự
Dave

@Dave: Đây là sử dụng 1 tab cho mỗi cấp độ thụt lề
Neil A.

Tôi nghĩ rằng IDE của bạn đang gây rối cho bạn và tiết kiệm không gian trong khi cho bạn biết nó đang sử dụng các tab; Khi tôi thay thế chúng cho các không gian đơn lẻ, tôi nhận được số byte chỉ là 311 byte! Hãy thử trực tuyến!
Dave

@Dave: Ok, thật lạ, cảm ơn, tôi sẽ cập nhật câu trả lời.
Neil A.

2

Perl + Math :: Prime :: Util, 81 + 27 = 108 byte

1 until!is_provable_prime(++$\)&&is_strong_pseudoprime($\,2..nth_prime($_));$_=""

Chạy với -lpMMath::Prime::Util=:all(hình phạt 27 byte, ouch).

Giải trình

Nó không chỉ là Mathicala có tích hợp sẵn cho mọi thứ. Perl có CPAN, một trong những kho thư viện lớn đầu tiên và có một bộ sưu tập lớn các giải pháp làm sẵn cho các nhiệm vụ như thế này. Thật không may, theo mặc định, chúng không được nhập (hoặc thậm chí được cài đặt), có nghĩa là về cơ bản không bao giờ là một lựa chọn tốt để sử dụng chúng trong , nhưng khi một trong số chúng xảy ra để phù hợp với vấn đề một cách hoàn hảo

Chúng tôi chạy qua các số nguyên liên tiếp cho đến khi chúng tôi tìm thấy một số nguyên tố không phải là số nguyên tố, và một giả số mạnh mẽ cho tất cả các cơ sở số nguyên từ 2 đến số nguyên tố thứ n . Tùy chọn dòng lệnh nhập thư viện chứa nội dung được đề cập và cũng đặt đầu vào ẩn (thành dòng tại một thời điểm; Math::Prime::Utilcó thư viện bignum dựng sẵn không giống như dòng mới trong số nguyên của nó). Điều này sử dụng thủ thuật Perl tiêu chuẩn của việc sử dụng $\(dấu tách dòng đầu ra) làm biến để giảm phân tích cú pháp vụng về và cho phép đầu ra được tạo hoàn toàn.

Lưu ý rằng chúng ta cần sử dụng is_provable_prime ở đây để yêu cầu một bài kiểm tra chính xác định, thay vì xác suất. (Đặc biệt là khi thử nghiệm chính xác suất có khả năng sử dụng Miller-Rabin ở nơi đầu tiên, mà chúng tôi không thể mong đợi sẽ cho kết quả đáng tin cậy trong trường hợp này!)

Perl + Math :: Prime :: Util, 71 + 17 = 88 byte, phối hợp với @Dada

1until!is_provable_prime(++$\)&is_strong_pseudoprime$\,2..n‌​th_prime$_}{

Chạy với -lpMntheory=:all(hình phạt 17 byte).

Điều này sử dụng một vài thủ thuật đánh gôn Perl mà tôi không biết (rõ ràng là Math :: Prime :: Util có tên viết tắt!), Biết về nhưng không nghĩ đến việc sử dụng ( }{để xuất ra $\một lần, thay vì "$_$\"ngầm mỗi dòng) , hoặc biết về nhưng bằng cách nào đó quản lý để thất bại (loại bỏ dấu ngoặc đơn khỏi các lệnh gọi hàm). Cảm ơn @Dada đã chỉ ra những điều này cho tôi. Ngoài ra, nó giống hệt nhau.


Tất nhiên một ngôn ngữ golf-ish đến và đánh bại phần còn lại. Làm tốt!
Neil A.

Bạn có thể sử dụng ntheorythay vì Math::Prime::Util. Ngoài ra, }{thay vì ;$_=""nên ổn. Và bạn có thể bỏ qua khoảng trắng sau 1và dấu ngoặc đơn của một vài lệnh gọi hàm. Ngoài ra, &làm việc thay vì &&. Điều đó sẽ cung cấp 88 byte:perl -Mntheory=:all -lpe '1until!is_provable_prime(++$\)&is_strong_pseudoprime$\,2..nth_prime$_}{'
Dada

Tôi hoàn toàn quên mất }{. (Thật kỳ lạ, tôi nhớ điều dấu ngoặc đơn nhưng đã được một thời gian kể từ khi tôi chơi gôn ở Perl và không thể nhớ các quy tắc để loại bỏ nó.) ntheoryMặc dù vậy, tôi không biết gì về chữ viết tắt.
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.