Regex (hương vị ECMAScript), 392 358 328 224 206 165 byte
Các kỹ thuật cần phải được sử dụng để khớp với các số Fibonacci với biểu thức chính ECMAScript (đơn nhất) khác xa so với cách nó được thực hiện tốt nhất trong hầu hết các hương vị regex khác. Việc thiếu các phản hồi ngược hoặc lồng nhau hoặc đệ quy có nghĩa là không thể trực tiếp đếm hoặc giữ tổng cộng mọi thứ đang chạy. Việc thiếu cái nhìn khiến nó thường là một thách thức thậm chí để có đủ không gian để làm việc.
Nhiều vấn đề phải được tiếp cận từ một quan điểm hoàn toàn khác, và dường như không thể giải quyết được cho đến khi có một số hiểu biết quan trọng. Nó buộc bạn phải tạo 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ủa các số bạn làm việc cùng có thể có thể được sử dụng để làm cho một vấn đề cụ thể có thể giải quyết được.
Vào tháng 3 năm 2014, đây là những gì đã xảy ra đối với các số Fibonacci. Nhìn vào trang Wikipedia, ban đầu tôi không thể tìm ra cách nào, mặc dù một tài sản cụ thể dường như rất gần gũi. Sau đó, nhà toán học teukon đã phác thảo một phương pháp làm cho nó khá rõ ràng có thể thực hiện được, sử dụng tính chất đó cùng với một phương pháp khác. Ông miễn cưỡng thực sự xây dựng regex. Phản ứng của anh ấy khi tôi đi trước và làm điều đó:
Bạn điên rồi! ... Tôi nghĩ bạn có thể làm điều này.
Cũng như các bài đăng regex toán học đơn nhất ECMAScript khác của tôi, tôi sẽ đưa ra một cảnh báo: Tôi 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 thức 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 đăng đó để 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 đơn phương hư 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.
Thách thức ban đầu tôi gặp phải: Một số nguyên dương x là một số Fibonacci khi và chỉ khi 5x 2 + 4 và / hoặc 5x 2 - 4 là một hình vuông hoàn hảo. Nhưng không có chỗ để tính toán điều này trong một regex. Không gian duy nhất chúng ta phải làm việc là số chính nó. Chúng tôi thậm chí không có đủ chỗ để nhân 5 hoặc lấy hình vuông, nói gì đến cả hai.
Ý tưởng của teukon về cách giải quyết nó ( ban đầu được đăng ở đây ):
Regex được trình bày với một chuỗi có dạng ^x*$
, gọi z là độ dài của nó. Kiểm tra xem z có phải là một trong số ít các số Fibonacci đầu tiên hay không (tối đa 21 nên làm). Nếu không phải vậy:
- Đọc một vài số, a <b, sao cho b không lớn hơn 2a.
- Sử dụng các hướng nhìn về phía trước để xây dựng 2 , ab và b 2 .
- Khẳng định rằng 5a 2 + 4 hoặc 5a 2 - 4 là một hình vuông hoàn hảo (vì vậy phải là F n - 1 cho một số n).
- Khẳng định rằng 5b 2 + 4 hoặc 5b 2 + 4 là một hình vuông hoàn hảo (vì vậy b phải là F n ).
- Kiểm tra xem z = F 2n + 3 hoặc z = F 2n + 4 bằng cách sử dụng 2 , ab và b 2 được xây dựng trước đó và các danh tính:
- F 2n-1 = F n 2 + F n-1 2
- F 2n = (2F n-1 + F n ) F n
Tóm lại: những bản sắc cho phép chúng ta làm giảm các vấn đề về kiểm tra rằng một số lượng nhất định là Fibonacci để kiểm tra xem một cặp nhiều số nhỏ hơn là Fibonacci. Một đại số nhỏ sẽ chỉ ra rằng với n đủ lớn (n = 3 nên làm), F 2n + 3 > F n + 5F n 2 + 4 vì vậy phải luôn có đủ không gian.
Và đây là một mockup của thuật toán trong C mà tôi đã viết dưới dạng thử nghiệm trước khi thực hiện nó trong regex.
Vì vậy, không có thêm ado, đây là regex:
^((?=(x*).*(?=x{4}(x{5}(\2{5}))(?=\3*$)\4+$)(|x{4})(?=xx(x*)(\6x?))\5(x(x*))(?=(\8*)\9+$)(?=\8*$\10)\8*(?=(x\2\9+$))(x*)\12)\7\11(\6\11|\12)|x{0,3}|x{5}|x{8}|x{21})$
Hãy thử trực tuyến!
Và phiên bản đẹp, được bình luận:
^(
(?=
(x*) # \2+1 = potential number for which 5*(\2+1)^2 ± 4
# is a perfect square; this is true iff \2+1 is a Fibonacci
# number. Outside the surrounding lookahead block, \2+1 is
# guaranteed to be the largest number for which this is true
# such that \2 + 5*(\2+1)^2 + 4 fits into the main number.
.*
(?= # tail = (\2+1) * (\2+1) * 5 + 4
x{4}
( # \3 = (\2+1) * 5
x{5}
(\2{5}) # \4 = \2 * 5
)
(?=\3*$)
\4+$
)
(|x{4}) # \5 = parity - determined by whether the index of Fibonacci
# number \2+1 is odd or even
(?=xx (x*)(\6 x?)) # \6 = arithmetic mean of (\2+1) * (\2+1) * 5 and \8 * \8,
# divided by 2
# \7 = the other half, including remainder
\5
# require that the current tail is a perfect square
(x(x*)) # \8 = potential square root, which will be the square root
# outside the surrounding lookahead; \9 = \8-1
(?=(\8*)\9+$) # \10 = must be zero for \8 to be a valid square root
(?=\8*$\10)
\8*
(?=(x\2\9+$)) # \11 = result of multiplying \8 * (\2+1), where \8 is larger
(x*)\12 # \12 = \11 / 2; the remainder will always be the same as it
# is in \7, because \8 is odd iff \2+1 is odd
)
\7\11
(
\6\11
|
\12
)
|
x{0,3}|x{5}|x{8}|x{21} # The Fibonacci numbers 0, 1, 2, 3, 5, 8, 21 cannot be handled
# by our main algorithm, so match them here; note, as it so
# happens the main algorithm does match 13, so that doesn't
# need to be handled here.
)$
Thuật toán nhân không được giải thích trong các bình luận đó, nhưng được giải thích ngắn gọn trong một đoạn của bài đăng regex số dồi dào của tôi .
Tôi đã duy trì sáu phiên bản khác nhau của biểu thức Fibonacci: bốn phiên bản từ độ dài ngắn nhất đến tốc độ nhanh nhất và sử dụng thuật toán được giải thích ở trên, và hai phiên bản khác sử dụng thuật toán khác, nhanh hơn nhưng dài hơn nhiều, mà tôi thấy thực sự có thể quay trở lại chỉ số Fibonacci là một kết quả khớp (giải thích rằng thuật toán ở đây nằm ngoài phạm vi của bài đăng này, nhưng nó được giải thích trong thảo luận ban đầu Gist ). Tôi không nghĩ rằng tôi sẽ duy trì nhiều phiên bản regex rất giống nhau một lần nữa, bởi vì tại thời điểm đó tôi đang thực hiện tất cả các thử nghiệm của mình trong PCRE và Perl, nhưng công cụ regex của tôi đủ nhanh để các mối quan tâm về tốc độ không còn quan trọng nữa (và nếu một cấu trúc cụ thể gây ra tắc nghẽn, tôi có thể thêm tối ưu hóa cho nó) - mặc dù tôi có thể lại duy trì một phiên bản nhanh nhất và một phiên bản ngắn nhất, nếu sự khác biệt trong tốc độ là đủ lớn.
Phiên bản "trả về chỉ số Fibonacci trừ 1 như một trận đấu" (không được đánh gôn nhiều):
Hãy thử trực tuyến!
Tất cả các phiên bản đều có trên github với lịch sử cam kết đầy đủ về tối ưu hóa golf:
regex để khớp các số Fibonacci - ngắn, tốc độ 0.txt (ngắn nhất nhưng chậm nhất, như trong bài đăng này)
regex để khớp các số Fibonacci - ngắn, tốc độ 1.txt
regex để khớp với các số Fibonacci - ngắn, tốc độ 2.txt
regex cho khớp các số Fibonacci - ngắn, tốc độ 3.txt
regex để khớp với các số Fibonacci - regex quick.txt để khớp với các số
Fibonacci - return index.txt