Thật dễ dàng để mô tả một máy trạng thái hữu hạn có thể nhận ra bội số của 9: theo dõi tổng số chữ số (mod 9) và thêm bất kỳ chữ số nào được chấp nhận tiếp theo. Một FSM như vậy chỉ có 9 tiểu bang, rất đơn giản! Bằng sự tương đương giữa khả năng nhận biết của FSM và các ngôn ngữ thông thường, có một biểu thức chính quy cho bội số 9. Tuy nhiên, bất kỳ biểu thức chính quy nào như vậy có khả năng ... rất ... dài. Như trong, có khả năng theo thứ tự của một gigabyte.
Có một ví dụ hoạt động tại https://www.quaxio.com/triple/ cho bội số của 3. Ở cuối trang, tác giả cung cấp giải pháp hơi "tối ưu hóa bằng tay" ngắn hơn một chút so với chuyển đổi ngây thơ từ FSM để regex.
Các thách thức:
Bạn phải tạo một regex để phát hiện bội số của 9. Vì một regex như vậy dự kiến sẽ rất dài, tôi yêu cầu bạn cung cấp một chương trình có thể in ra regex của bạn. (Nếu bạn thực sự muốn cung cấp toàn bộ regex, có thể lưu trữ nó ở nơi khác và liên kết nó ở đây!)
Bạn phải có thể cho chúng tôi biết số lượng ký tự chính xác của đầu ra chương trình của bạn - vì vậy, có một chương trình chỉ đơn giản là thử tất cả các biểu thức có độ dài nhất định, cho đến khi tìm thấy một hoạt động, không thể chấp nhận được trừ khi nó chạy đủ nhanh để bạn có thể chạy nó để hoàn thành và cung cấp cho chúng tôi độ dài regex kết quả!
Điểm là để có regex đầu ra ngắn nhất, tất nhiên không dựa trên thời lượng chương trình. Vì regex là "chương trình" mà tôi đang yêu cầu và nó quá dài để truyền tải thuận tiện ở đây, nên tôi vẫn gắn thẻ môn đánh gôn này.
Quy tắc:
- Đầu vào sẽ chỉ bao gồm các ký tự khớp
[0-9]*
. - Regex của bạn phải phù hợp với bội số của 9, nhưng không phải bất cứ điều gì khác. Các trường hợp không được tạo hoàn toàn bằng các chữ số 0-9 và là đầu vào không hợp lệ có thể khớp hoặc thất bại như bạn muốn.
- Với động lực mà DFA dễ dàng nhận ra, regex kết quả thực sự phải là biểu thức chính quy theo thuật ngữ lý thuyết hơn, nghĩa là chỉ các toán tử theo đó các ngôn ngữ thông thường được đóng. Nói chính xác, những điều duy nhất được phép:
- Literals, dãy ký tự (
[ab]
,[a-f]
,[^k]
), ngôi sao Kleene (*
), neo (^
và$
), nhóm thông qua dấu ngoặc đơn, thay đổi luân phiên (|
), điều khoản bắt buộc (?
), một-hay-hơn các điều khoản (+
), lookaheads ((?=)
), lookaheads tiêu cực ((?!)
), lookbehinds ((?<=)
), lookbehinds ((?<!)
), điều kiện tiêu cực (như trong https://www.THER-expressions.info/conditable.html -(?(?=test)then|else)
) và phản hồi có độ dài giới hạn (xem bên dưới).
- Literals, dãy ký tự (
- Ví dụ về những điều không được phép:
- Phản hồi về độ dài tùy ý, tham chiếu chuyển tiếp, đệ quy, chương trình con, cấu trúc lặp, mã thực thi, bất kỳ biến thể nào của 'eval' hoặc cấu trúc dựng sẵn để truyền chuỗi thành giá trị số học.
- Backreferences có thể được hiển thị để có một chuỗi ràng buộc có độ dài giới hạn có thể được chấp nhận, vì chúng có thể được lưu trữ ở trạng thái hữu hạn và không làm thay đổi tính thường xuyên của ngôn ngữ. Ví dụ, regex
(..2.[3-5])4\1.\1
có thể chấp nhận được, vì có độ dài ràng buộc trên nhóm chụp\1
. Đây là một công trình thường xuyên. Một cấu trúc như(2*)0\1
không được chấp nhận, vì nhóm bị bắt không thể được lưu trữ ở trạng thái hữu hạn. - Regex của bạn được tự do chấp nhận hoặc từ chối các số nguyên với các số 0 đứng đầu ngoài ý muốn. Tuy nhiên, chuỗi
"0"
phải được chấp nhận.
^(0|9|(?<c>1|(?<c>2|(?<c>3|(?<c>4|(?<c>5|(?<c>6|(?<c>7|(?<c>8))))))))((?<-c>){9})?)*$(?(c).)