Tìm số nguyên tố dễ vỡ lớn nhất


21

Hãy xem xét hàm Remove(n, startIndex, count)loại bỏ các countchữ số khỏi số nbắt đầu từ chữ số tại vị trí startIndex. Ví dụ:

Remove(1234, 1, 1) = 234
Remove(123456, 2, 3) = 156
Remove(1507, 1, 2) = 07 = 7
Remove(1234, 1, 4) = 0

Chúng tôi sẽ gọi số nguyên tố X mong manh nếu mọi Removethao tác có thể làm cho nó không phải là số nguyên tố. Ví dụ: 80651 là số nguyên tố dễ vỡ vì tất cả các số sau đây không phải là số nguyên tố:

651, 51, 1, 0, 8651, 851, 81, 8, 8051, 801, 80, 8061, 806, 8065

Mục tiêu

Viết chương trình tìm số nguyên tố dễ vỡ lớn nhất. Chỉnh sửa: xóa giới hạn thời gian vì có một cách tương đối công bằng để phá vỡ nó.

Điểm số là số nguyên tố dễ vỡ được tìm thấy bởi chương trình của bạn. Trong trường hợp hòa, nộp trước đó thắng.

Quy tắc

  • Bạn có thể sử dụng bất kỳ ngôn ngữ và bất kỳ thư viện của bên thứ ba.
  • Bạn chạy chương trình trên phần cứng của riêng bạn.
  • Bạn có thể sử dụng các xét nghiệm nguyên thủy xác suất.
  • Tất cả mọi thứ là trong cơ sở 10.

Mục hàng đầu

  • 6629 chữ số của Qualtagh (Java)
  • 5048 chữ số của Emil (Python 2)
  • 2268 chữ số của Jakube (Python 2)

Chỉnh sửa: Tôi đã thêm câu trả lời của riêng tôi.

  • 28164 chữ số của Suboptimus Prime, dựa trên thuật toán của Qualtagh (C #)

5
Ngay cả khi tôi không viết mã câu trả lời, tôi vẫn có thể bắt đầu tìm kiếm tại một điểm rất gần với một nguyên tố mỏng manh lớn. Rõ ràng, không ai muốn bắt đầu tìm kiếm ở 1. Điều gì ngăn tôi làm điều này? Chính xác thì tôi có thể bắt đầu tìm kiếm gần đến mức nào trước khi tôi được gọi ra về cơ bản mã hóa câu trả lời? Tôi thích những thách thức bằng cách này.
Rainbolt

2
@SuboptimusPrime Thay vào đó, bạn hoàn toàn có thể loại bỏ giới hạn thời gian, bởi vì tôi tin rằng ở một thời điểm nào đó sẽ rất hiếm khi tìm thấy cái tiếp theo dù sao đi nữa. (Tương tự như codegolf.stackexchange.com/questions/41021/iêu )
Martin Ender


7
Bạn vẫn đang ở thế bất lợi cho những người có máy tính chậm hơn
John Dvorak

11
Phải mất một thời gian dài lúng túng tôi mới nhận ra rằng "Viết một chương trình tìm ra số nguyên tố dễ vỡ lớn nhất" không có nghĩa là "Có tồn tại một số nguyên tố dễ vỡ lớn nhất. Viết một chương trình tìm thấy nó." Tôi đoán tôi đã làm quá nhiều Project Euler. :-P
ruakh

Câu trả lời:


9

Java - 3144 3322 6629 chữ số

6 0{3314} 8969999

6 0{6623} 49099

Giải pháp này dựa trên câu trả lời của FryAmTheEggman .

  1. Chữ số cuối cùng là 1 hoặc 9.
  2. Nếu chữ số cuối cùng là 1 thì số trước là 0, 8 hoặc 9.
  3. Nếu chữ số cuối cùng là 9 thì số trước là 0, 4, 6 hoặc 9.
  4. ...

Nếu chúng ta đào sâu hơn thì sao?

Nó trở thành một cấu trúc cây:

                        S
             -----------------------
             1                     9
    ------------------         ----------------
    0           8    9         0    4    6    9
---------     -----
0   8   9      ...

Hãy gọi số R đúng hợp số nếu R và tất cả các kết thúc của nó là hỗn hợp.

Chúng tôi sẽ lặp lại tất cả các số tổng hợp bên phải theo chiều rộng đầu tiên: 1, 9, 01, 81, 91, 09, 49, 69, 99, 001, 801, 901, v.v.

Các số bắt đầu bằng 0 không được kiểm tra tính nguyên thủy nhưng cần thiết để xây dựng các số tiếp theo.

Chúng tôi sẽ tìm kiếm một số mục tiêu N ở dạng X00 ... 00R, trong đó X là một trong 4, 6, 8 hoặc 9 và R là hợp số đúng. X không thể là số nguyên tố. X không thể bằng 0. Và X không thể là 1 vì nếu R kết thúc bằng 1 hoặc 9 thì N sẽ chứa 11 hoặc 19.

Nếu XR chứa số nguyên tố sau thao tác "xóa" thì XYR cũng sẽ chứa chúng cho bất kỳ Y. Vì vậy, chúng ta không nên đi qua các nhánh bắt đầu từ R.

Đặt X là hằng số, nói 6.

Mã giả:

X = 6;
for ( String R : breadth-first-traverse-of-all-right-composites ) {
  if ( R ends with 1 or 9 ) {
    if ( remove( X + R, i, j ) is composite for all i and j ) {
      for ( String zeros = ""; zeros.length() < LIMIT; zeros += "0" ) {
        if ( X + zeros + R is prime ) {
          // At this step these conditions hold:
          // 1. X + 0...0 is composite.
          // 2. 0...0 + R = R is composite.
          // 3. X + 0...0 + R is composite if 0...0 is shorter than zeros.
          suits = true;
          for ( E : all R endings )
            if ( X + zeros + E is prime )
              suits = false;
          if ( suits )
            print R + " is fragile prime";
          break; // try another R
                 // because ( X + zeros + 0...0 + R )
                 // would contain prime ( X + zeros + R ).
        }
      }
    }
  }
}

Chúng ta nên giới hạn số lượng số không vì có thể mất quá nhiều thời gian để tìm số nguyên tố ở dạng X + số không + R (hoặc mãi mãi nếu tất cả chúng là hợp số).

Mã thực sự khá dài dòng và có thể được tìm thấy ở đây .

Kiểm tra tính nguyên thủy cho các số trong phạm vi int dài được thực hiện bằng biến thể xác định của kiểm tra Miller. Đối với các số BigInteger, việc phân chia thử nghiệm được thực hiện trước và sau đó là kiểm tra BailliePSW. Đó là xác suất nhưng khá chắc chắn. Và nó nhanh hơn thử nghiệm Miller-Rabin (chúng ta nên thực hiện nhiều lần lặp lại cho số lượng lớn như vậy trong Miller-Rabin để có đủ độ chính xác).

Chỉnh sửa: lần thử đầu tiên không chính xác. Chúng ta cũng nên bỏ qua các nhánh bắt đầu bằng R nếu X0 ... 0R là số nguyên tố. Thì X0 ... 0YR sẽ không dễ vỡ. Vì vậy, một kiểm tra bổ sung đã được thêm vào. Giải pháp này có vẻ đúng.

Chỉnh sửa 2: thêm một tối ưu hóa. Nếu (X + R) chia hết cho 3 thì (X + zeros + R) cũng chia hết cho 3. Vì vậy (X + zeros + R) không thể là số nguyên tố trong trường hợp này và R có thể bị bỏ qua.

Chỉnh sửa 3: không cần thiết phải bỏ qua các chữ số nguyên tố nếu chúng không ở vị trí cuối cùng hoặc đầu tiên. Vì vậy, kết thúc như 21 hoặc 51 là ok. Nhưng nó không thay đổi gì nhiều.

Kết luận:

  1. Câu trả lời cuối cùng của tôi là kiểm tra xem có dễ vỡ trong 100 phút không. Việc tìm kiếm câu trả lời (kiểm tra tất cả các biến thể trước đó) mất khoảng 15 phút. Có, việc hạn chế thời gian tìm kiếm là vô nghĩa (chúng ta có thể bắt đầu tìm kiếm từ số mục tiêu, vì vậy thời gian sẽ bằng không). Nhưng nó có thể có ý nghĩa để hạn chế thời gian kiểm tra như trong câu hỏi này .
  2. Câu trả lời 60 ... 049099 có chữ số 4 ở giữa. Nếu thao tác "xóa" chạm vào nó, số sẽ chia hết cho 3. Vì vậy, chúng ta nên kiểm tra các thao tác xóa ở bên trái và bên phải. Bên phải quá ngắn. Chiều dài bên trái gần như n = chiều dài (N).
  3. Các thử nghiệm nguyên thủy như BPSW và Miller-Rabin sử dụng số mũ lũy thừa không đổi. Độ phức tạp của nó là O (M (n) * n) theo trang này , trong đó M (n) là độ phức tạp nhân. Java sử dụng thuật toán Toom-Cook và Karatsuba nhưng chúng tôi sẽ sử dụng thuật toán học thuật để đơn giản. M (n) = n 2 . Vì vậy, độ phức tạp kiểm tra nguyên thủy là O (n 3 ).
  4. Chúng ta nên kiểm tra tất cả các số từ length = 6 đến 6629. Hãy lấy min = 1 và max = n để biết tính phổ biến. Toàn bộ độ phức tạp kiểm tra là O (1 3 + 2 3 + ... + n 3 ) = O ((n * (n + 1) / 2) 2 ) = O (n 4 ).
  5. Câu trả lời của Emil có cùng sự kiểm tra tiệm cận. Nhưng yếu tố không đổi là thấp hơn. Chữ số "7" đang đứng ở giữa số. Bên trái và bên phải có thể gần như bằng nhau. Nó cho (n / 2) 4 * 2 = n 4 / 8. Tăng tốc: 8X. Các số ở dạng 9 ... 9Y9 ... 9 có thể dài hơn 1,7 lần so với ở dạng X0 ... 0R có cùng thời gian kiểm tra.

1
Cảm ơn về khoản tín dụng, nhưng thuật toán của bạn phức tạp hơn tôi rất nhiều! Công việc tuyệt vời, và chào mừng bạn đến với PPCG! :)
FryAmTheEggman

@FryAmTheEggman: cảm ơn bạn vì ý tưởng! Thật là truyền cảm.
Qualtagh

Phân tích của bạn về độ phức tạp kiểm tra là rất thú vị, nhưng tính đồng bộ tìm kiếm có lẽ cũng quan trọng. Tôi nghĩ rằng thuật toán của bạn yêu cầu các phép thử nguyên thủy ít hơn đáng kể với số lượng lớn (so với Emil) để tìm ra một số nguyên tố dễ vỡ lớn. Và bạn có thể tăng tốc độ kiểm tra tính nguyên thủy bằng cách sử dụng thư viện riêng. Tôi đang sử dụng Mpir.NET và việc kiểm tra số của bạn để trở thành một thủ tướng mong manh chỉ mất vài phút.
Suboptimus Prime

13

Python 2 - 126 1221 1337 1719 2268 chữ số

999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999799999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999

'9' * 1944 + '7' + '9' * 323

Có khoảng len (n) ^ 2 số kết quả của Loại bỏ (n, start Index, đếm). Tôi đã cố gắng giảm thiểu những con số đó. Nếu có nhiều chữ số cạnh nhau là giống nhau, rất nhiều số kết quả này có thể bị bỏ qua, vì chúng xuất hiện nhiều lần.

Vì vậy, tôi đã đưa nó đến cực điểm, chỉ 9s và một chút nguyên tố ở giữa. Tôi cũng đã xem qua số nguyên tố dễ vỡ dưới 1 triệu và thấy rằng có những số nguyên tố dễ vỡ như vậy. Tìm kiếm các số có 2 9 ở cuối hoạt động thực sự tốt, không biết tại sao. 1 số, 3 hoặc 4 9 ở cuối dẫn đến các số nguyên tố mỏng manh nhỏ hơn.

Nó sử dụng mô-đun pyprimes . Tôi không chắc chắn, nếu nó là tốt. Nó sử dụng thử nghiệm miller_rabin, vì vậy nó có xác suất.

Chương trình tìm thấy số nguyên tố mong manh gồm 126 chữ số này trong khoảng 1 phút và trong thời gian còn lại nó tìm kiếm mà không thành công.

biggest_found = 80651

n = lambda a,b,c: '9'*a + b + '9'*c

for j in range(1000):
   for digit in '124578':
      for i in range(2000):
         number = int(n(i,digit,j))
         if is_prime(number):
            if (number > biggest_found):
               if all(not is_prime(int(n(i,digit,k))) for k in range(j)):
                  biggest_found = number
                  print(i+j+1, biggest_found)
            break

chỉnh sửa:

Chỉ cần nhìn thấy, bạn đã loại bỏ giới hạn thời gian. Tôi sẽ chạy chương trình qua đêm, có thể một số số nguyên tố mỏng manh thực sự lớn xuất hiện.

chỉnh sửa 2:

Làm cho chương trình ban đầu của tôi nhanh hơn, nhưng vẫn không có giải pháp nào với hơn 126 chữ số. Vì vậy, tôi đã nhảy lên tàu và tìm kiếm x 9s + 1 chữ số + y 9s. Ưu điểm là, bạn phải kiểm tra số O (n) cho tính nguyên thủy, nếu bạn sửa y. Nó tìm thấy một 1221 khá nhanh chóng.

chỉnh sửa 3:

Đối với số 2268 chữ số tôi sử dụng cùng một chương trình, chỉ chia công việc trên nhiều lõi.


3
"trong khoảng 1 phút" - xin lỗi, phải báo cáo "lỗi" số nhiều. : P
hichris123

Bản chất xác suất của miller-rabin là những gì đã cắn tôi trong vài mục cuối cùng của tôi. Bạn có thể muốn xác minh với một thuật toán khác là tốt.
John Meacham

Tại sao bạn chỉ kiểm tra rằng các số được hình thành từ việc loại bỏ các chữ số từ cuối là hợp số? Tại sao không kiểm tra các số được hình thành bằng cách loại bỏ các chữ số từ phía trước?
isaacg

1
Bởi vì tôi đã kiểm tra những cái này trước trong 'for i'-loop. Ở đây tôi viết thêm số 9 ở đầu và thực hiện kiểm tra chính. Khi tôi tìm thấy số nguyên tố đầu tiên của mẫu này, tôi biết rằng tất cả các số có ít hơn 9 ở đầu không phải là số nguyên tố. Và sau khi kiểm tra loại bỏ số 9 ở cuối, tôi dừng (ngắt), bởi vì bây giờ, mọi số đều có số nguyên tố trong đó và do đó không phải là số nguyên tố.
Jakube

Ah, rất thông minh.
isaacg

7

Python 2.7 - 429623069 99993799

Không tối ưu hóa bất cứ điều gì, cho đến nay. Chỉ cần sử dụng một số quan sát tầm thường về các số nguyên tố mong manh (nhờ Rainbolt trong trò chuyện):

  1. Số nguyên tố dễ vỡ phải kết thúc bằng 1 hoặc 9 (Số nguyên tố không chẵn và chữ số cuối cùng không phải là số nguyên tố)
  2. Các số nguyên tố dễ vỡ kết thúc bằng 1 phải bắt đầu bằng 8 hoặc 9 (số đầu tiên không thể là số nguyên tố, và 11, 41 và 61 và đều là số nguyên tố)
  3. Các số nguyên tố dễ vỡ kết thúc bằng 9 phải bắt đầu bằng 4,6 hoặc 9 (xem lý do cho 1, nhưng chỉ 89 là số nguyên tố)

Chỉ cần cố gắng để có được quả bóng lăn :)

Kỹ thuật này chạy nhẹ hơn 15 phút, nhưng nó chỉ kiểm tra một số duy nhất trong thời gian thêm.

is_primeđược lấy từ đây (isaacg đã sử dụng ở đây ) và có xác suất.

def substrings(a):
    l=len(a)
    out=set()
    for i in range(l):
        for j in range(l-i):
            out.add(a[:i]+a[len(a)-j:])
    return out

import time

n=9
while time.clock()<15*60:
    if is_prime(n):
        if not any(map(lambda n: n!='' and is_prime(int(n)), substrings(`n`))):
            print n
    t=`n`
    if n%10==9 and t[0]=='8':n+=2
    elif n%10==1 and t[0]!='8':n+=8
    elif t[0]=='1' or is_prime(int(t[0])):n+=10**~-len(t)
    else:n+=10

Chỉ cần một lưu ý, khi tôi bắt đầu điều này với n=429623069tôi nhận được lên 482704669. Chữ số phụ dường như thực sự giết chết chiến lược này ...


Không tồi cho một khởi đầu! Mặc dù có vẻ như is_prime thực hiện kiểm tra xác định đầy đủ cho các giá trị 32 bit, điều này hơi quá. Tôi nghĩ phương pháp is_prime có thể hoạt động nhanh hơn nếu bạn nhận xét phần phân chia dùng thử đầy đủ.
Suboptimus Prime

@SuboptimusPrime ơi, cảm ơn. Tôi thậm chí không nhìn vào nó: P
FryAmTheEggman

@SuboptimusPrime Tôi nghĩ rằng kiểm tra xác định đầy đủ nhanh hơn đối với các giá trị nhỏ vì tác giả đã xác định các bước để thực hiện giữa các yếu tố ứng cử viên. Cảm ơn một lần nữa vì ý tưởng này, nhưng có vẻ nhanh hơn nhiều khi để nó vào :)
FryAmTheEggman

Chỉnh sửa nhỏ cho câu trả lời của bạn: 91 = 13x7, vì vậy 91 là tổng hợp và các số nguyên tố dễ vỡ kết thúc bằng 1 thực sự có thể bắt đầu bằng 9.
Suboptimus Prime

@SuboptimusPrime Hoàn toàn đúng, dunno tôi đã làm thế nào. Giá trị tôi đã đăng vẫn phải hợp lệ, vì tôi chỉ bỏ qua một số giá trị có thể.
FryAmTheEggman

7

Python 2, 828 chữ số 5048 chữ số

99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999799999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
155*'9'+'7'+4892*'9'

Như @Jakube đã chỉ ra, số nguyên tố đầu tiên tôi gửi không thực sự dễ vỡ do lỗi trong mã của tôi. Sửa lỗi là dễ dàng nhưng nó cũng làm cho thuật toán chậm hơn đáng kể.

Tôi giới hạn bản thân trong một tập hợp con có thể tìm kiếm dễ dàng của các số nguyên tố mong manh, cụ thể là các số nguyên tố chỉ bao gồm chữ số 9 và chính xác là một chữ số 7.

def fragile_prime_generator(x, b_max):
  bs, cs = set(), set()
  prime = dict()

  def test_prime(b,c):
    if (b,c) not in prime:
      prime[(b,c)] = is_prime(int('9'*b+`x`+'9'*c))
    return prime[(b,c)]

  def test_frag(b,c):
    for b2 in xrange(b):
      if test_prime(b2,c):
        bs.add(b2)
        return False
    for c2 in xrange(c):
      if test_prime(b,c2):
        cs.add(c2)
        return False
    return True

  a = 1
  while len(bs)<b_max:
    for b in xrange(min(a, b_max)):
      c = a-b
      if b not in bs and c not in cs and test_prime(b,c):
        bs.add(b)
        cs.add(c)
        if test_frag(b,c): yield b,c
    a += 1
  print "no more fragile primes of this form"

for b,c in fragile_prime_generator(7, 222):
  print ("%d digit fragile prime found: %d*'9'+'%d'+%d*'9'"
          % (b+c+1, b, x, c))

Tôi đã sử dụng is_primechức năng tương tự (từ đây ) là @FryAmTheEggman.

Chỉnh sửa:

Tôi đã thực hiện hai thay đổi để làm cho thuật toán nhanh hơn:

  • Tôi cố gắng bỏ qua càng nhiều kiểm tra nguyên thủy càng tốt và chỉ quay lại khi tìm thấy một nguyên tố mong manh tiềm tàng để đảm bảo nó thực sự dễ vỡ. Có một số lượng nhỏ các kiểm tra trùng lặp, vì vậy tôi đã ghi nhớ một cách thô thiển chức năng kiểm tra chính.

  • Đối với các số của mẫu b*'9' + '7' + c*'9'tôi giới hạn kích thước của b. Giới hạn càng thấp, số lượng phải kiểm tra càng ít, nhưng cơ hội tăng lên để không tìm thấy bất kỳ số nguyên tố mong manh lớn nào cả. Tôi loại tùy ý chọn 222 là giới hạn.

Với vài nghìn chữ số, một lần kiểm tra chính có thể khiến chương trình của tôi mất vài giây. Vì vậy, tôi có lẽ không thể làm tốt hơn với phương pháp này.

Xin vui lòng kiểm tra tính chính xác của trình của tôi. Do tính nguyên thủy kiểm tra xác suất, số của tôi về mặt lý thuyết có thể không phải là số nguyên tố, nhưng nếu có, nó sẽ rất dễ vỡ. Hoặc tôi đã làm sai điều gì đó. :-)


2
Nguyên tố tìm thấy của bạn không phải là mong manh. Nếu bạn gọi Xóa (n, 83.838) [Xóa mọi thứ trừ 82 chữ số đầu tiên], bạn sẽ kết thúc bằng một số nguyên tố.
Jakube

1
À, cảm ơn @Jakube. Tôi đã cố gắng để quá thông minh. Hóa ra tôi đã bỏ qua kiểm tra nguyên thủy hơn thì tôi nên có. Tôi đang trên đường sửa nó.
Emil

1
Kiểm tra lại, bây giờ kết quả của bạn là chính xác.
Jakube

Số 5048 chữ số của bạn, thực sự, là một số nguyên tố dễ vỡ theo chương trình của tôi.
Suboptimus Prime

@SuboptimusPrime: Tuyệt vời, cảm ơn bạn đã kiểm tra!
Emil

4

C #, 10039 28164 chữ số

6 0{28157} 169669

Chỉnh sửa: Tôi đã thực hiện một chương trình khác dựa trên thuật toán của Qualtagh với một số sửa đổi nhỏ:

  • Tôi đang tìm kiếm các số có dạng L000 ... 000R, trong đó L là hỗn hợp trái, R là hỗn hợp phải. Tôi đã cho phép số hỗn hợp L bên trái có nhiều chữ số, mặc dù đây chủ yếu là một thay đổi về kiểu dáng và có lẽ nó không ảnh hưởng đến hiệu quả của thuật toán.
  • Tôi đã thêm đa luồng để tăng tốc tìm kiếm.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Threading;
using System.Threading.Tasks;
using Mpir.NET;

class Program
{
    const int PrimeNotFound = int.MaxValue;

    private static BitArray _primeSieve;
    private static HashSet<Tuple<int, int>> _templatesToSkip = new HashSet<Tuple<int, int>>();

    static void Main(string[] args)
    {
        int bestDigitCount = 0;
        foreach (Tuple<int, int> template in GetTemplates())
        {
            int left = template.Item1;
            int right = template.Item2;
            if (SkipTemplate(left, right))
                continue;

            int zeroCount = GetZeroCountOfPrime(left, right);
            if (zeroCount != PrimeNotFound)
            {
                int digitCount = left.ToString().Length + right.ToString().Length + zeroCount;
                if (digitCount >= bestDigitCount)
                {
                    string primeStr = left + " 0{" + zeroCount + "} " + right;
                    Console.WriteLine("testing " + primeStr);
                    bool isFragile = IsFragile(left, right, zeroCount);
                    Console.WriteLine(primeStr + " is fragile: " + isFragile);

                    if (isFragile)
                        bestDigitCount = digitCount;
                }

                _templatesToSkip.Add(template);
            }
        }
    }

    private static int GetZeroCountOfPrime(int left, int right)
    {
        _zeroCount = 0;

        int threadCount = Environment.ProcessorCount;
        Task<int>[] tasks = new Task<int>[threadCount];
        for (int i = 0; i < threadCount; i++)
            tasks[i] = Task.Run(() => InternalGetZeroCountOfPrime(left, right));
        Task.WaitAll(tasks);

        return tasks.Min(task => task.Result);
    }

    private static int _zeroCount;

    private static int InternalGetZeroCountOfPrime(int left, int right)
    {
        const int maxZeroCount = 40000;
        int zeroCount = Interlocked.Increment(ref _zeroCount);
        while (zeroCount <= maxZeroCount)
        {
            if (zeroCount % 1000 == 0)
                Console.WriteLine("testing " + left + " 0{" + zeroCount + "} " + right);

            if (IsPrime(left, right, zeroCount))
            {
                Interlocked.Add(ref _zeroCount, maxZeroCount);
                return zeroCount;
            }
            else
                zeroCount = Interlocked.Increment(ref _zeroCount);
        }

        return PrimeNotFound;
    }

    private static bool SkipTemplate(int left, int right)
    {
        for (int leftDiv = 1; leftDiv <= left; leftDiv *= 10)
            for (int rightDiv = 1; rightDiv <= right; rightDiv *= 10)
                if (_templatesToSkip.Contains(Tuple.Create(left / leftDiv, right % (rightDiv * 10))))
                    return true;

        return false;
    }

    private static bool IsPrime(int left, int right, int zeroCount)
    {
        return IsPrime(left.ToString() + new string('0', zeroCount) + right.ToString());
    }

    private static bool IsPrime(string left, string right, int zeroCount)
    {
        return IsPrime(left + new string('0', zeroCount) + right);
    }

    private static bool IsPrime(string s)
    {
        using (mpz_t n = new mpz_t(s))
        {
            return n.IsProbablyPrimeRabinMiller(20);
        }
    }

    private static bool IsFragile(int left, int right, int zeroCount)
    {
        string leftStr = left.ToString();
        string rightStr = right.ToString();

        for (int startIndex = 0; startIndex < leftStr.Length - 1; startIndex++)
            for (int count = 1; count < leftStr.Length - startIndex; count++)
                if (IsPrime(leftStr.Remove(startIndex, count), rightStr, zeroCount))
                    return false;

        for (int startIndex = 1; startIndex < rightStr.Length; startIndex++)
            for (int count = 1; count <= rightStr.Length - startIndex; count++)
                if (IsPrime(leftStr, rightStr.Remove(startIndex, count), zeroCount))
                    return false;

        return true;
    }

    private static IEnumerable<Tuple<int, int>> GetTemplates()
    {
        const int maxDigitCount = 8;
        PreparePrimeSieve((int)BigInteger.Pow(10, maxDigitCount));
        for (int digitCount = 2; digitCount <= maxDigitCount; digitCount++)
        {
            for (int leftCount = 1; leftCount < digitCount; leftCount++)
            {
                int rightCount = digitCount - leftCount;
                int maxLeft = (int)BigInteger.Pow(10, leftCount);
                int maxRight = (int)BigInteger.Pow(10, rightCount);

                for (int left = maxLeft / 10; left < maxLeft; left++)
                    for (int right = maxRight / 10; right < maxRight; right++)
                        if (IsValidTemplate(left, right, leftCount, rightCount))
                            yield return Tuple.Create(left, right);
            }

        }
    }

    private static void PreparePrimeSieve(int limit)
    {
        _primeSieve = new BitArray(limit + 1, true);
        _primeSieve[0] = false;
        _primeSieve[1] = false;

        for (int i = 2; i * i <= limit; i++)
            if (_primeSieve[i])
                for (int j = i * i; j <= limit; j += i)
                    _primeSieve[j] = false;
    }

    private static bool IsValidTemplate(int left, int right, int leftCount, int rightCount)
    {
        int rightDigit = right % 10;
        if ((rightDigit != 1) && (rightDigit != 9))
            return false;

        if (left % 10 == 0)
            return false;

        if ((left + right) % 3 == 0)
            return false;

        if (!Coprime(left, right))
            return false;

        int leftDiv = 1;
        for (int i = 0; i <= leftCount; i++)
        {
            int rightDiv = 1;
            for (int j = 0; j <= rightCount; j++)
            {
                int combination = left / leftDiv * rightDiv + right % rightDiv;
                if (_primeSieve[combination])
                    return false;

                rightDiv *= 10;
            }

            leftDiv *= 10;
        }

        return true;
    }

    private static bool Coprime(int a, int b)
    {
        while (b != 0)
        {
            int t = b;
            b = a % b;
            a = t;
        }
        return a == 1;
    }
}

Câu trả lời cũ:

8 0{5436} 4 0{4600} 1

Đây là một vài mẫu đáng chú ý cho các số nguyên tố dễ vỡ:

600..00X00..009
900..00X00..009
800..00X00..001
999..99X99..999

trong đó X có thể là 1, 2, 4, 5, 7 hoặc 8.

Đối với những số như vậy, chúng tôi chỉ phải xem xét (độ dài - 1) có thể Remove hoạt động . Các Removehoạt động khác tạo ra trùng lặp hoặc rõ ràng là số tổng hợp. Tôi đã cố gắng tìm kiếm tất cả các số như vậy với tối đa 800 chữ số và nhận thấy rằng 4 mẫu xuất hiện thường xuyên hơn các mẫu còn lại: 8007001, 8004001, 9997999 và 6004009. Vì Emil và Jakube đang sử dụng mẫu 999X999, tôi quyết định chỉ sử dụng 8004001 để thêm một số loại.

Tôi đã thêm các tối ưu hóa sau vào thuật toán:

  • Tôi bắt đầu tìm kiếm từ các số có 7000 chữ số và sau đó tăng chiều dài thêm 1500 mỗi khi tìm thấy một số nguyên tố dễ vỡ. Nếu không có số nguyên tố dễ vỡ với độ dài cho trước thì tôi tăng nó thêm 1. 7000 và 1500 chỉ là những con số tùy ý có vẻ phù hợp.
  • Tôi đang sử dụng đa luồng để tìm kiếm các số có độ dài khác nhau cùng một lúc.
  • Kết quả của mỗi lần kiểm tra chính được lưu trữ trong bảng băm để ngăn kiểm tra trùng lặp.
  • Tôi đang sử dụng triển khai Miller-Rabin từ Mpir.NET , tốc độ rất nhanh (MPIR là một nhánh của GMP).
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using Mpir.NET;

class Program
{
    const string _template = "8041";

    private static ConcurrentDictionary<Tuple<int, int>, byte> _compositeNumbers = new ConcurrentDictionary<Tuple<int, int>, byte>();
    private static ConcurrentDictionary<int, int> _leftPrimes = new ConcurrentDictionary<int, int>();
    private static ConcurrentDictionary<int, int> _rightPrimes = new ConcurrentDictionary<int, int>();

    static void Main(string[] args)
    {
        int threadCount = Environment.ProcessorCount;
        Task[] tasks = new Task[threadCount];
        for (int i = 0; i < threadCount; i++)
        {
            int index = i;
            tasks[index] = Task.Run(() => SearchFragilePrimes());
        }
        Task.WaitAll(tasks);
    }

    private const int _lengthIncrement = 1500;
    private static int _length = 7000;
    private static object _lengthLock = new object();
    private static object _consoleLock = new object();

    private static void SearchFragilePrimes()
    {
        int length;
        lock (_lengthLock)
        {
            _length++;
            length = _length;
        }

        while (true)
        {
            lock (_consoleLock)
            {
                Console.WriteLine("{0:T}: length = {1}", DateTime.Now, length);
            }

            bool found = false;
            for (int rightCount = 1; rightCount <= length - 2; rightCount++)
            {
                int leftCount = length - rightCount - 1;
                if (IsFragilePrime(leftCount, rightCount))
                {
                    lock (_consoleLock)
                    {
                        Console.WriteLine("{0:T}: {1} {2}{{{3}}} {4} {2}{{{5}}} {6}",
                            DateTime.Now, _template[0], _template[1], leftCount - 1,
                            _template[2], rightCount - 1, _template[3]);
                    }
                    found = true;
                    break;
                }
            }

            lock (_lengthLock)
            {
                if (found && (_length < length + _lengthIncrement / 2))
                    _length += _lengthIncrement;
                else
                    _length++;
                length = _length;
            }
        }
    }

    private static bool IsFragilePrime(int leftCount, int rightCount)
    {
        int count;
        if (_leftPrimes.TryGetValue(leftCount, out count))
            if (count < rightCount)
                return false;

        if (_rightPrimes.TryGetValue(rightCount, out count))
            if (count < leftCount)
                return false;

        if (!IsPrime(leftCount, rightCount))
            return false;

        for (int i = 0; i < leftCount; i++)
            if (IsPrime(i, rightCount))
                return false;

        for (int i = 0; i < rightCount; i++)
            if (IsPrime(leftCount, i))
                return false;

        return true;
    }

    private static bool IsPrime(int leftCount, int rightCount)
    {
        Tuple<int, int> tuple = Tuple.Create(leftCount, rightCount);
        if (_compositeNumbers.ContainsKey(tuple))
            return false;

        using (mpz_t n = new mpz_t(BuildStr(leftCount, rightCount)))
        {
            bool result = n.IsProbablyPrimeRabinMiller(20);

            if (result)
            {
                _leftPrimes.TryAdd(leftCount, rightCount);
                _rightPrimes.TryAdd(rightCount, leftCount);
            }
            else
                _compositeNumbers.TryAdd(tuple, 0);

            return result;
        }
    }

    private static string BuildStr(int leftCount, int rightCount)
    {
        char[] chars = new char[leftCount + rightCount + 1];
        for (int i = 0; i < chars.Length; i++)
            chars[i] = _template[1];
        chars[0] = _template[0];
        chars[leftCount + rightCount] = _template[3];
        chars[leftCount] = _template[2];
        return new string(chars);
    }
}

Trong khi tôi đang cố gắng xác minh câu trả lời đầu tiên của bạn, bạn đã đăng một câu hỏi mới)). Kiểm tra đã mất 24 giờ. Câu trả lời dường như là chính xác. Tôi không thể tin rằng BigInteger của Java chậm hơn nhiều so với các triển khai riêng. Tôi nghĩ chậm hơn khoảng 2, 3 hoặc thậm chí 10 lần. Nhưng 24 giờ so với vài phút là quá nhiều.
Qualtagh

@Qualtagh Để công bằng, số 10039 chữ số mất 35 giờ để tìm do thuật toán kém hơn :) Chương trình hiện tại của tôi mất khoảng 3 phút để tìm số 6629 chữ số của bạn và 6 giờ để tìm số 28164 chữ số.
Suboptimus Prime

Câu trả lời đầu tiên của bạn là chính xác. Đã xác minh! Xác minh mất 48 giờ. Và tôi thậm chí sẽ không thử xác minh câu trả lời thứ hai)). Tôi đang tự hỏi tại sao BigInteger lại chậm như vậy so với MPIR. Có phải chỉ có JVM / khác biệt bản địa? Tôi đặt cờ "máy chủ", vì vậy mong mã được biên dịch JIT. Các thuật toán của lũy thừa mô-đun khác nhau: cả Java và MPIR đều sử dụng cửa sổ trượt 2 <sup> k </ sup> -ary, nhưng k = 3 được cố định trong Java và MPIR chọn k theo kích thước của số mũ. Là MPIR sử dụng tính toán song song trên một số lõi hoặc có thể là khả năng của GPU? BigInteger của Java thì không.
Qualtagh

1
@Qualtagh Tôi khá chắc chắn MPIR chỉ sử dụng một lõi CPU. Tôi đã tự thêm đa luồng, kết quả là tìm kiếm nhanh hơn gần 4 lần trên CPU lõi tứ. Tôi đã không so sánh việc triển khai nội bộ của MPIR và Java BigInteger, nhưng tôi đoán rằng MPIR đang sử dụng các thuật toán tốt hơn để nhân và chia mô đun. Ngoài ra, nó có thể được tối ưu hóa tốt hơn cho CPU 64 bit (xem điểm chuẩn trong bài đăng trên blog này ).
Suboptimus Prime

2
MPIR thực sự là lõi đơn và nó không sử dụng GPU. Đó là sự pha trộn tối ưu và tinh chỉnh cao của mã C và trình biên dịch mã. Có phiên bản MPIR chỉ sử dụng C (vì lý do tính di động), nhưng phiên bản C + ASM nhanh hơn đáng kể. Phiên bản MPIR tôi đang sử dụng cho MPIR.Net là C + ASM sử dụng bộ hướng dẫn K8 (1st gen x64), vì tôi muốn MPIR.Net chạy trên tất cả các PC x64. Các phiên bản cho các bộ hướng dẫn sau này nhanh hơn đáng kể trong điểm chuẩn tiền điện tử của tôi, nhưng điều đó tất nhiên có thể khác với các hoạt động khác.
John Reynold

2

Haskell - 1220 1277 chữ số được cố định cho thực tế thực

99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999997999999999999999999999999999999999999999999999999999999999999999999999

9{1150} 7 9{69}

Tốt hơn một - 1277 chữ số

9{871} 8 9{405}

Mã Haskell

downADigit :: Integer -> [Integer]
downADigit n = f [] 1 where
     f xs a | nma /= n = f (((n `div` a10)*a + nma):xs) a10
            | otherwise = xs where
        a10 = a * 10
        nma = n `mod` a

isFragile = all (not . isPrime') . downADigit
findNextPrime :: Integer -> Integer
findNextPrime n | even n = f (n + 1)
                | otherwise = f n where
    f n | isPrime' n  = n
        | otherwise = f (n + 2)

primesFrom n = f (findNextPrime n) where
    f n = n:f (findNextPrime $ n + 1)

primeLimit = 10000

isPrime' n | n < primeLimit = isPrime n
isPrime' n = all (millerRabinPrimality n) [2,3,5,7,11,13,17,19,984,7283,6628,8398,2983,9849,2739]

-- (eq. to) find2km (2^k * n) = (k,n)
find2km :: Integer -> (Integer,Integer)
find2km n = f 0 n
    where 
        f k m
            | r == 1 = (k,m)
            | otherwise = f (k+1) q
            where (q,r) = quotRem m 2        

-- n is the number to test; a is the (presumably randomly chosen) witness
millerRabinPrimality :: Integer -> Integer -> Bool
millerRabinPrimality n a
    | a <= 1 || a >= n-1 = 
        error $ "millerRabinPrimality: a out of range (" 
              ++ show a ++ " for "++ show n ++ ")" 
    | n < 2 = False
    | even n = False
    | b0 == 1 || b0 == n' = True
    | otherwise = iter (tail b)
    where
        n' = n-1
        (k,m) = find2km n'
        b0 = powMod n a m
        b = take (fromIntegral k) $ iterate (squareMod n) b0
        iter [] = False
        iter (x:xs)
            | x == 1 = False
            | x == n' = True
            | otherwise = iter xs

-- (eq. to) pow' (*) (^2) n k = n^k
pow' :: (Num a, Integral b) => (a->a->a) -> (a->a) -> a -> b -> a
pow' _ _ _ 0 = 1
pow' mul sq x' n' = f x' n' 1
    where 
        f x n y
            | n == 1 = x `mul` y
            | r == 0 = f x2 q y
            | otherwise = f x2 q (x `mul` y)
            where
                (q,r) = quotRem n 2
                x2 = sq x

mulMod :: Integral a => a -> a -> a -> a
mulMod a b c = (b * c) `mod` a
squareMod :: Integral a => a -> a -> a
squareMod a b = (b * b) `rem` a

-- (eq. to) powMod m n k = n^k `mod` m
powMod :: Integral a => a -> a -> a -> a
powMod m = pow' (mulMod m) (squareMod m)

-- simple for small primes
primes :: [Integer]
primes = 2:3:primes' where
    1:p:candidates = [6*k+r | k <- [0..], r <- [1,5]]
    primes'        = p : filter isPrime candidates
    isPrime n      = all (not . divides n)
                                   $ takeWhile (\p -> p*p <= n) primes'
    divides n p    = n `mod` p == 0
isPrime :: Integer -> Bool
isPrime n | n < 2 = False
          | otherwise = f primes where
            f (p:ps) | p*p <= n = if n `rem` p == 0 then False else f ps
                     | otherwise = True

main = do
    print . head $ filter isFragile (primesFrom $ 10^1000)

Tôi nghĩ bạn có thể xóa mọi thứ trừ 3 ngày cuối ...
Sp3000

nó kết thúc sau 5 nếu tôi xóa 3 cuối cùng để chia hết cho 5
John Meacham

2
Không, tôi có nghĩa là loại bỏ tất cả mọi thứ cho đến khi bạn chỉ còn 3 cuối cùng, đó là số nguyên tố.
Sp3000

1
@JohnMeacham Chương trình của tôi gợi ý rằng số này biến thành số nguyên tố nếu bạn loại bỏ 386 chữ số từ bên trái.
Suboptimus Prime

1
Xin vui lòng, xác minh số của bạn trước khi đăng. Nếu bạn xóa 1256 chữ số bên trái khỏi số 1276 chữ số của mình, bạn sẽ nhận được 99999994999999999999, là số nguyên tố.
Suboptimus Prime
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.