ECMAScript Regex, 733+ 690+ 158 119 118 (117🐌) byte
Sự quan tâm của tôi đối với regex đã được khơi dậy với sức sống mới sau hơn 4 năm không hoạt động. Do đó, tôi đã tìm kiếm các bộ số và hàm số tự nhiên hơn để phù hợp với các biểu thức ECMAScript đơn nhất, tiếp tục cải thiện công cụ regex của mình và cũng bắt đầu cải tiến trên PCRE.
Tôi bị mê hoặc bởi sự xa lạ của việc xây dựng các hàm toán học trong biểu thức ECMAScript. Các vấn đề phải được tiếp cận từ một quan điểm hoàn toàn khác, và cho đến khi có một cái nhìn sâu sắc quan trọng, không biết liệu chúng có thể giải quyết được không. Nó buộc phải đúc một mạng lưới rộng hơn nhiều trong việc tìm ra các thuộc tính toán học nào có thể được sử dụng để làm cho một vấn đề cụ thể có thể giải quyết được.
Kết hợp các số yếu tố là một vấn đề tôi thậm chí không cân nhắc việc giải quyết trong năm 2014 - hoặc nếu tôi đã làm, chỉ trong giây lát, loại bỏ nó là điều quá khó có thể xảy ra. Nhưng tháng trước, tôi nhận ra rằng nó có thể được thực hiện.
Cũng như các bài đăng regex ECMA khác của tôi, tôi sẽ đưa ra một cảnh báo: Tôi đặc biệt khuyên bạn nên học cách giải quyết các vấn đề toán học đơn phương trong biểu thức chính của ECMAScript. Đó là một hành trình hấp dẫn đối với tôi và tôi không muốn làm hỏng nó cho bất kỳ ai có khả năng muốn thử nó, đặc biệt là những người quan tâm đến lý thuyết số. Xem bài này sớm hơn để biết danh sách các vấn đề được đề xuất liên quan đến spoiler để giải quyết từng vấn đề một.
Vì vậy, đừng đọc thêm nữa nếu bạn không muốn một số phép thuật regex unary tiên tiến được làm hỏng cho bạn . Nếu bạn thực sự muốn tự mình tìm ra phép thuật này, tôi khuyên bạn nên bắt đầu bằng cách giải quyết một số vấn đề trong biểu thức ECMAScript như được nêu trong bài đăng được liên kết ở trên.
Đây là ý tưởng của tôi:
Vấn đề với việc khớp số này, như với hầu hết các số khác, là trong ECMA, thường không thể theo dõi hai số thay đổi trong một vòng lặp. Đôi khi chúng có thể được ghép kênh (ví dụ: các quyền hạn của cùng một cơ sở có thể được thêm vào một cách rõ ràng), nhưng nó phụ thuộc vào thuộc tính của chúng. Vì vậy, tôi không thể chỉ bắt đầu với số đầu vào và chia nó cho cổ tức tăng dần cho đến khi đạt 1 (ít nhất là tôi nghĩ vậy).
Sau đó, tôi đã thực hiện một số nghiên cứu về tính đa dạng của các thừa số nguyên tố trong các số nhân tử và biết rằng có một công thức cho điều này - và đó là một công thức mà tôi có thể thực hiện trong một biểu thức ECMA!
Sau khi hầm nó một lúc, và xây dựng một số regex khác trong thời gian đó, tôi nhận nhiệm vụ viết regex giai thừa. Phải mất một số giờ, nhưng cuối cùng đã làm việc tốt. Là một phần thưởng bổ sung, thuật toán có thể trả về yếu tố nghịch đảo như một trận đấu. Không có cách nào để tránh nó, thậm chí; bởi bản chất của cách nó phải được thực hiện trong ECMA, cần phải đoán xem yếu tố nghịch đảo là gì trước khi làm bất cứ điều gì khác.
Nhược điểm là thuật toán này tạo ra một regex rất dài ... nhưng tôi hài lòng rằng nó đã kết thúc đòi hỏi một kỹ thuật được sử dụng trong regex phép nhân 651 byte của tôi (một kết thúc bị lỗi thời, bởi vì một phương thức khác được tạo ra cho 50 regex byte). Tôi đã hy vọng một vấn đề sẽ xuất hiện đòi hỏi thủ thuật này: Hoạt động trên hai số, cả hai sức mạnh của cùng một cơ sở, trong một vòng lặp, bằng cách thêm chúng lại với nhau một cách rõ ràng và tách chúng ra ở mỗi lần lặp.
Nhưng vì độ khó và độ dài của thuật toán này, tôi đã sử dụng các bảng tìm phân tử (có dạng (?*...)
) để thực hiện nó. Đó là một tính năng không có trong ECMAScript hoặc bất kỳ công cụ regex chính thống nào khác, mà là một tính năng mà tôi đã triển khai trong công cụ của mình . Không có bất kỳ ảnh chụp nào bên trong một cái nhìn phân tử, nó có chức năng tương đương với một cái nhìn nguyên tử, nhưng với các ảnh chụp thì nó có thể rất mạnh. Công cụ sẽ quay trở lại vào giao diện và điều này có thể được sử dụng để phỏng đoán một giá trị xoay quanh tất cả các khả năng (để kiểm tra sau) mà không tiêu tốn các ký tự của đầu vào. Sử dụng chúng có thể làm cho việc thực hiện sạch hơn nhiều. (Cái nhìn có chiều dài thay đổi có sức mạnh tối thiểu bằng với cái nhìn phân tử, nhưng cái sau có xu hướng tạo ra những triển khai đơn giản và thanh lịch hơn.)
Vì vậy, độ dài 733 và 690 byte không thực sự đại diện cho các hóa thân tương thích ECMAScript của giải pháp - do đó "+" sau chúng; chắc chắn có thể chuyển thuật toán đó sang ECMAScript thuần túy (sẽ tăng độ dài của nó lên một chút) nhưng tôi đã không tìm hiểu về nó ... bởi vì tôi nghĩ về một thuật toán đơn giản và gọn nhẹ hơn nhiều! Một trong đó có thể dễ dàng được thực hiện mà không cần nhìn phân tử. Nó cũng nhanh hơn đáng kể.
Cái mới này, giống như cái trước, phải đoán ở giai đoạn nghịch đảo, đạp xe qua tất cả các khả năng và kiểm tra chúng cho một trận đấu. Nó chia N cho 2 để nhường chỗ cho công việc cần thực hiện và sau đó gieo một vòng lặp trong đó nó sẽ liên tục chia đầu vào cho một ước số bắt đầu từ 3 và tăng mỗi lần. (Như vậy, 1! Và 2! Không thể khớp với thuật toán chính và phải được xử lý riêng.) Bộ chia được theo dõi bằng cách thêm nó vào thương số đang chạy; hai số này có thể được phân tách rõ ràng bởi vì, giả sử M! == N, thương số đang chạy sẽ tiếp tục chia hết cho M cho đến khi bằng M.
Regex này thực hiện chia từng biến trong phần trong cùng của vòng lặp. Thuật toán chia giống như trong các biểu thức chính khác của tôi (và tương tự như thuật toán nhân): với A≤B, A * B = C nếu có chỉ khi C% A = 0 và B là số lớn nhất thỏa mãn B≤C và C% B = 0 và (CB- (A-1))% (B-1) = 0, trong đó C là cổ tức, A là ước số và B là thương số. (Một thuật toán tương tự có thể được sử dụng cho trường hợp A≥B và nếu không biết A so với B như thế nào, thì một bài kiểm tra phân chia bổ sung là tất cả những gì cần thiết.)
Vì vậy, tôi thích rằng vấn đề có thể được giảm đến mức độ phức tạp thậm chí ít hơn so với biểu thức Fibonacci được tối ưu hóa cho môn đánh gôn của tôi , nhưng tôi thở dài với sự thất vọng rằng kỹ thuật ghép kênh đa năng của tôi sẽ phải chờ một vấn đề khác Điều đó thực sự đòi hỏi nó, bởi vì cái này không. Đó là câu chuyện về thuật toán nhân 651 byte của tôi được thay thế bằng một byte 50 byte, một lần nữa!
Chỉnh sửa: Tôi đã có thể giảm 1 byte (119 → 118) bằng cách sử dụng một mẹo được tìm thấy bởi Grimy có thể rút ngắn phân chia trong trường hợp thương số được đảm bảo lớn hơn hoặc bằng số chia.
Không có thêm rắc rối, đây là regex:
Phiên bản đúng / sai (118 byte):
^((x*)x*)(?=\1$)(?=(xxx\2)+$)((?=\2\3*(x(?!\3)xx(x*)))\6(?=\5+$)(?=((x*)(?=\5(\8*$))x)\7*$)x\9(?=x\6\3+$))*\2\3$|^xx?$
Hãy thử trực tuyến!
Trả về giai thừa nghịch đảo hoặc không khớp (124 byte):
^(?=((x*)x*)(?=\1$)(?=(xxx\2)+$)((?=\2\3*(x(?!\3)xx(x*)))\6(?=\5+$)(?=((x*)(?=\5(\8*$))x)\7*$)x\9(?=x\6\3+$))*\2\3$)\3|^xx?$
Hãy thử trực tuyến!
Trả về giai thừa ngược hoặc không khớp, trong ECMAScript +\K
(120 byte):
^((x*)x*)(?=\1$)(?=(xxx\2)+$)((?=\2\3*(x(?!\3)xx(x*)))\6(?=\5+$)(?=((x*)(?=\5(\8*$))x)\7*$)x\9(?=x\6\3+$))*\2\K\3$|^xx?$
Và phiên bản miễn phí với các bình luận:
^
(?= # Remove this lookahead and the \3 following it, while
# preserving its contents unchanged, to get a 119 byte
# regex that only returns match / no-match.
((x*)x*)(?=\1$) # Assert that tail is even; \1 = tail / 2;
# \2 = (conjectured N for which tail == N!)-3; tail = \1
(?=(xxx\2)+$) # \3 = \2+3 == N; Assert that tail is divisible by \3
# The loop is seeded: X = \1; I = 3; tail = X + I-3
(
(?=\2\3*(x(?!\3)xx(x*))) # \5 = I; \6 = I-3; Assert that \5 <= \3
\6 # tail = X
(?=\5+$) # Assert that tail is divisible by \5
(?=
( # \7 = tail / \5
(x*) # \8 = \7-1
(?=\5(\8*$)) # \9 = tool for making tail = \5\8
x
)
\7*$
)
x\9 # Prepare the next iteration of the loop: X = \7; I += 1;
# tail = X + I-3
(?=x\6\3+$) # Assert that \7 is divisible by \3
)*
\2\3$
)
\3 # Return N, the inverse factorial, as a match
|
^xx?$ # Match 1 and 2, which the main algorithm can't handle
Lịch sử đầy đủ về tối ưu hóa golf của tôi về các biểu thức này là trên github:
regex để khớp các số giai thừa - phương pháp so sánh bội số, với
regex lookahead.txt phân tử để khớp các số giai thừa (một cách hiển thị ở trên)
((x*)x*)
((x*)+)
((x+)+)
n=3!\2
3−3=0
Công cụ regex .NET không mô phỏng hành vi này trong chế độ ECMAScript của nó và do đó regex 117 byte hoạt động:
Hãy thử trực tuyến! (phiên bản làm chậm theo cấp số nhân, với công cụ regex .NET + mô phỏng ECMAScript)
1
không?