Làm thế nào để xác định nếu một số là một số nguyên tố với regex?


128

Tôi đã tìm thấy ví dụ mã sau cho Java trên RosettaCode :

public static boolean prime(int n) {
  return !new String(new char[n]).matches(".?|(..+?)\\1+");
}
  • Tôi không biết cụ thể về Java nhưng hiểu tất cả các khía cạnh của đoạn trích này ngoại trừ chính regex
  • Tôi có kiến ​​thức cơ bản đến nâng cao cơ bản về Regex khi bạn tìm thấy nó trong các hàm PHP tích hợp

Làm thế nào để .?|(..+?)\\1+khớp số nguyên tố?


9
@Amir Rachum: !new String(new char[n]).matches(".?|(..+?)\\1+")tương đương với !((new String(new char[n])).matches(".?|(..+?)\\1+")).
Gumbo

14
Điều này không chỉ tốn kém về mặt tính toán mà còn có khả năng gây tốn kém bộ nhớ. Nếu bất cứ ai chọn sử dụng phương pháp này, điều mà tôi khuyên là vì thuật toán tìm số nguyên tố rất đơn giản (tại sao trên thế giới lại làm phức tạp nó và khiến nó trở nên lãng phí), nên tiến hành kiểm tra trước "char mới [n ] "Để đảm bảo nó dưới ngưỡng hợp lý. Ví dụ: Gọi "số nguyên tố (Integer.MAX_VALUE)" và sau đó báo lỗi khi nó ném OutOfMemoryError.
nicerobot

28
@nicerobot: Sáng lên?
Cam

6
@nicerobot: thực ra, tôi lấy lại. Ban đầu tôi nhận ra bản chất học thuật của câu hỏi này ngụ ý nó chỉ được sử dụng cho mục đích học tập, và rằng bạn là một twat đáng ghét. Tuy nhiên, trong suy nghĩ thứ hai, đó không phải là trường hợp; nó không bao giờ được đề cập hoặc thậm chí ngụ ý trong câu hỏi rằng regex chỉ dành cho mục đích học tập. Trong thực tế, ấn tượng đầu tiên của tôi về nó là nó trông rất đơn giản theo như các đoạn mã, vì vậy một người mới bắt đầu thực sự có thể cho rằng nó có thể được sử dụng trong thực tế. +1.
Cam

7
@incrediman không phải lo lắng. Tôi có thể thấy làm thế nào bạn có thể nghĩ rằng. Đó chỉ là ý định của tôi để cảnh báo về hậu quả của việc sử dụng này, không được khuyến khích học cách nó hoạt động. Một cách đơn giản "Xin đừng triển khai cái này." trước phần còn lại của bình luận của tôi có thể đã làm cho nó ít âm thanh hơn từ quan điểm ban đầu của bạn.
nicerobot

Câu trả lời:


120

Bạn nói rằng bạn hiểu phần này, nhưng chỉ cần nhấn mạnh, Chuỗi được tạo có độ dài bằng với số được cung cấp. Vì vậy, chuỗi có ba ký tự khi và chỉ khi n == 3.

.?

Phần đầu tiên của regex nói, "bất kỳ ký tự nào, 0 hoặc 1 lần". Về cơ bản, không có hoặc có một ký tự-- hay, theo những gì tôi đã đề cập ở trên , n == 0 || n == 1. Nếu chúng ta có trận đấu, sau đó trả lại sự phủ định của điều đó. Điều này tương ứng với thực tế là số 0 và số KHÔNG là số nguyên tố.

(..+?)\\1+

Phần thứ hai của regex phức tạp hơn một chút, dựa vào các nhóm và phản hồi. Một nhóm là bất cứ thứ gì trong ngoặc đơn, sau đó sẽ được ghi lại và lưu trữ bởi công cụ regex để sử dụng sau. Backreference là một nhóm phù hợp được sử dụng sau này trong cùng một regex.

Nhóm chụp 1 ký tự, sau đó 1 hoặc nhiều hơn bất kỳ ký tự nào. (Ký tự + có nghĩa là một hoặc nhiều, nhưng CHỈ của ký tự hoặc nhóm trước đó. Vì vậy, đây không phải là "hai hoặc bốn hoặc sáu ký tự, v.v.", mà là "hai hoặc ba, v.v." + giống như +, nhưng nó cố gắng khớp càng ít ký tự càng tốt. + thường cố gắng ngấu nghiến toàn bộ chuỗi nếu có thể, điều này rất tệ trong trường hợp này vì nó ngăn phần phản hồi hoạt động.)

Phần tiếp theo là phản hồi: Cùng một bộ ký tự (hai hoặc nhiều hơn), xuất hiện lại. Nói phản ứng xuất hiện một hoặc nhiều lần.

Vì thế. Nhóm bị bắt tương ứng với số lượng ký tự tự nhiên (từ 2 trở đi) bị bắt. Nhóm nói sau đó xuất hiện một số lần tự nhiên (cũng từ 2 trở đi). Nếu có một kết quả trùng khớp, điều này có nghĩa là có thể tìm thấy một sản phẩm có hai số lớn hơn hoặc bằng 2 khớp với chuỗi có độ dài n ... có nghĩa là bạn có tổng hợp n. Vì vậy, một lần nữa, trả lại phủ định của trận đấu thành công: n KHÔNG phải là số nguyên tố.

Nếu không tìm thấy kết quả khớp nào, thì bạn không thể đưa ra sản phẩm có hai số tự nhiên lớn hơn hoặc bằng 2 ... và bạn có cả số không khớp và số nguyên tố, do đó một lần nữa sự trở lại của phủ định của kết quả trận đấu.

bây giờ bạn có nhìn thấy nó không? Nó khó tin đến mức khó tin (và đắt tiền về mặt tính toán!) Nhưng nó cũng khá đơn giản cùng một lúc, khi bạn có được nó. :-)

Tôi có thể giải thích nếu bạn có thêm câu hỏi, như cách phân tích cú pháp regex thực sự hoạt động. Nhưng tôi đang cố gắng để giữ cho câu trả lời này đơn giản ngay bây giờ (hoặc đơn giản như nó có thể được).


10
Tôi đã thử logic này với JS trong bảng điều khiển chrome dev. trên trang web. và chỉ cần vượt qua 5 để kiểm tra. Trang bị sập!
Amogh Talpallikar

Các bình luận dưới đây cho lời giải thích tốt hơn. Xin vui lòng đọc nó trước khi bạn di chuyển trên!
Ivan Davidov

"Tốt hơn" là chủ quan - Tôi muốn nói rằng nó tiếp cận vấn đề từ một góc độ khác và là một bổ sung tuyệt vời cho câu trả lời này. :-)
Bạch kim Azure

1
Tôi thực sự đã viết một bài đăng trên blog giải thích điều này với nhiều chi tiết hơn: Làm sáng tỏ Biểu thức thường xuyên kiểm tra nếu một số là số nguyên tố .
Illya Gerasymchuk

73

Tôi sẽ giải thích phần regex bên ngoài kiểm tra tính nguyên thủy: regex sau đây, được đưa ra String sbao gồm lặp lại String t, tìm thấy t.

    System.out.println(
        "MamamiaMamamiaMamamia".replaceAll("^(.*)\\1+$", "$1")
    ); // prints "Mamamia"

Cách thức hoạt động của nó là regex thu hút (.*)vào \1, và sau đó xem nếu có \1+theo dõi nó. Sử dụng ^$đảm bảo rằng một trận đấu phải là của toàn bộ chuỗi.

Vì vậy, theo một cách nào đó, chúng ta được đưa ra String s, đó là một "bội số" String tvà regex sẽ tìm thấy như vậy t(lâu nhất có thể, vì \1là tham lam).

Khi bạn hiểu lý do tại sao regex này hoạt động, sau đó (bỏ qua thay thế đầu tiên trong regex của OP bây giờ) giải thích cách nó được sử dụng để kiểm tra tính nguyên thủy là đơn giản.

  • Để kiểm tra tính nguyên thủy của n, trước tiên hãy tạo một Stringđộ dài n(chứa đầy char)
  • Regex thu được một Stringkhoảng thời gian (nói k) vào \1và cố gắng khớp \1+với phần còn lại củaString
    • Nếu có một trận đấu, thì đó nlà bội số thích hợp k, và do đó nkhông phải là số nguyên tố.
    • Nếu không có kết quả trùng khớp, thì không ktồn tại sự phân chia như vậy n, và ndo đó là một nguyên tố

Làm thế nào để .?|(..+?)\1+khớp số nguyên tố?

Thật ra thì không! Nó phù hợp String với độ dài của nó là KHÔNG nguyên tố!

  • .?: Phần đầu tiên của các kết quả trùng khớp Stringcó độ dài 0hoặc 1(KHÔNG phải là số nguyên tố theo định nghĩa)
  • (..+?)\1+: Phần thứ hai của sự xen kẽ, một biến thể của biểu thức chính được giải thích ở trên, khớp với Stringđộ dài nlà "bội số" của một Stringđộ dài k >= 2(nghĩa nlà một hỗn hợp, KHÔNG phải là số nguyên tố).
    • Lưu ý rằng công cụ sửa đổi miễn cưỡng ?thực sự không cần thiết cho tính chính xác, nhưng nó có thể giúp tăng tốc quá trình bằng cách thử nhỏ hơn ktrước

Lưu ý ! booleantoán tử bổ sung trong returncâu lệnh: nó phủ định matches. Đó là khi regex KHÔNG khớp, nlà chính! Đó là logic hai mặt tiêu cực, vì vậy không có gì lạ khi nó khó hiểu !!


Đơn giản hóa

Đây là cách viết lại mã đơn giản để dễ đọc hơn:

public static boolean isPrime(int n) {
    String lengthN = new String(new char[n]);
    boolean isNotPrimeN = lengthN.matches(".?|(..+?)\\1+");
    return !isNotPrimeN;
}

Ở trên về cơ bản giống như mã Java ban đầu, nhưng được chia thành nhiều câu lệnh với các phép gán cho các biến cục bộ để làm cho logic dễ hiểu hơn.

Chúng ta cũng có thể đơn giản hóa regex, sử dụng sự lặp lại hữu hạn, như sau:

boolean isNotPrimeN = lengthN.matches(".{0,1}|(.{2,})\\1+");

Một lần nữa, đưa ra một Stringchiều dài n, chứa đầy char,

  • .{0,1}kiểm tra nếu n = 0,1, KHÔNG nguyên tố
  • (.{2,})\1+kiểm tra nếu nlà bội số thích hợp của k >= 2, KHÔNG phải là số nguyên tố

Ngoại trừ công cụ sửa đổi miễn cưỡng ?trên \1(được bỏ qua cho rõ ràng), regex ở trên giống hệt với bản gốc.


Regex vui hơn

Regex sau đây sử dụng kỹ thuật tương tự; nó nên mang tính giáo dục:

System.out.println(
    "OhMyGod=MyMyMyOhGodOhGodOhGod"
        .replaceAll("^(.+)(.+)(.+)=(\\1|\\2|\\3)+$", "$1! $2! $3!")
); // prints "Oh! My! God!"

Xem thêm


6
+1: Tôi nghĩ cách tiếp cận của bạn có lẽ tốt hơn tôi. Không có lý do tại sao tôi nhận được rất nhiều upvote hoặc dấu kiểm ... bạn xứng đáng với nó nhiều hơn, tôi nghĩ. :-( Xin lỗi
Platinum Azure

@Platinum: Wow, tôi chưa bao giờ nghĩ rằng bạn sẽ đi xung quanh nói điều đó một cách công khai! Cảm ơn về sự hỗ trợ. Có lẽ tôi sẽ nhận được một [Populist]ngày từ này.
đa gen

2
Chà, đó chỉ là sự thật (như tôi cảm nhận được) ... thực sự không phải là một vấn đề lớn. Tôi không ở đây để đại diện (mặc dù nó luôn luôn là một phần thưởng và một bất ngờ thú vị) ... Tôi ở đây để cố gắng trả lời các câu hỏi khi tôi có thể. Do đó, không có gì ngạc nhiên khi tôi có thể thừa nhận khi ai đó đã làm điều đó tốt hơn tôi trong một câu hỏi cụ thể.
Bạch kim Azure

25

Thủ thuật regex đẹp (mặc dù rất kém hiệu quả) ... :)

Regex định nghĩa các số nguyên tố như sau:

N không phải là số nguyên tố khi và chỉ khi N <= 1 HOẶC N chia hết cho một số K> 1.

Thay vì chuyển biểu diễn kỹ thuật số đơn giản của N cho công cụ regex, nó được cung cấp với một chuỗi có độ dài N, bao gồm một ký tự lặp lại. Phần đầu tiên của hàm phân tách kiểm tra N = 0 hoặc N = 1, và phần thứ hai tìm kiếm một ước số K> 1, sử dụng phản hồi. Nó buộc công cụ regex tìm một số chuỗi con không trống có thể được lặp lại ít nhất hai lần để tạo thành chuỗi. Nếu một chuỗi như vậy tồn tại, có nghĩa là chiều dài của nó chia N, do đó N không phải là số nguyên tố.


2
Thật kỳ lạ, thậm chí sau khi đọc nhiều lần những lời giải thích dài hơn và kỹ thuật hơn, tôi thấy lời giải thích này là lời giải thích khiến nó 'nhấp chuột' trong đầu.
tám bit

2
/^1?$|^(11+?)\1+$/

Áp dụng cho các số sau khi chuyển đổi sang cơ sở 1 (1 = 1, 2 = 11, 3 = 111, ...). Không phải số nguyên tố sẽ phù hợp với điều này. Nếu nó không khớp, nó là số nguyên tố.

Giải thích tại đây .

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.