ECMAScript Regex, 733+ 690+ 158 119 118 (117🐌) byte
Ketertarikan saya pada regex telah dipicu dengan semangat baru setelah lebih dari 4½ tahun tidak aktif. Karena itu, saya mencari set angka dan fungsi yang lebih alami agar sesuai dengan regeks ECMAScript unary, melanjutkan meningkatkan engine regex saya, dan mulai menyikat PCRE juga.
Saya terpesona oleh keterasingan membangun fungsi matematika di ECMAScript regex. Masalah harus didekati dari perspektif yang sama sekali berbeda, dan sampai kedatangan wawasan kunci, tidak diketahui apakah mereka dapat dipecahkan sama sekali. Ini memaksa casting jaring yang jauh lebih luas dalam menemukan properti matematika mana yang mungkin dapat digunakan untuk membuat masalah tertentu dapat dipecahkan.
Mencocokkan angka faktorial adalah masalah yang bahkan tidak saya pertimbangkan untuk ditangani pada tahun 2014 - atau jika saya melakukannya, hanya sebentar, mengabaikannya sebagai hal yang tidak mungkin terjadi. Tetapi bulan lalu, saya menyadari bahwa itu bisa dilakukan.
Seperti dengan posting regex ECMA saya yang lain, saya akan memberikan peringatan: Saya sangat merekomendasikan belajar bagaimana memecahkan masalah matematika unary di ECMAScript regex. Ini merupakan perjalanan yang menarik bagi saya, dan saya tidak ingin merusaknya bagi siapa pun yang mungkin ingin mencobanya sendiri, terutama mereka yang tertarik pada teori bilangan. Lihat posting ini sebelumnya untuk daftar masalah yang direkomendasikan untuk ditandai dengan spoiler bertanda satu per satu.
Jadi jangan membaca lebih jauh jika Anda tidak ingin beberapa sihir regex unary canggih dimanjakan untuk Anda . Jika Anda ingin mencoba mencari tahu sendiri keajaiban ini, saya sangat menyarankan memulai dengan menyelesaikan beberapa masalah dalam ECMAScript regex sebagaimana diuraikan dalam pos yang ditautkan di atas.
Ini ideku:
Masalah dengan pencocokan set angka ini, seperti kebanyakan yang lain, adalah bahwa dalam ECMA biasanya tidak mungkin untuk melacak dua angka yang berubah dalam satu lingkaran. Kadang-kadang mereka dapat multiplexing (mis. Kekuatan dari basis yang sama dapat ditambahkan bersama-sama dengan jelas), tetapi itu tergantung pada propertinya. Jadi saya tidak bisa hanya mulai dengan nomor input, dan membaginya dengan dividen yang meningkat secara bertahap sampai mencapai 1 (atau jadi saya pikir, setidaknya).
Kemudian saya melakukan penelitian tentang banyaknya faktor utama dalam bilangan faktorial, dan mengetahui bahwa ada formula untuk ini - dan itu salah satu yang mungkin bisa saya implementasikan dalam regex ECMA!
Setelah mengunyahnya sebentar, dan membangun beberapa regex lain sementara itu, saya mengambil tugas untuk menulis regex faktorial. Butuh beberapa jam, tetapi akhirnya bekerja dengan baik. Sebagai bonus tambahan, algoritme dapat mengembalikan faktorial terbalik sebagai pertandingan. Tidak ada yang menghindarinya, bahkan; dengan sifat bagaimana itu harus diimplementasikan dalam ECMA, perlu untuk menebak apa faktor terbalik sebelum melakukan hal lain.
The downside adalah bahwa algoritma ini dibuat untuk regex yang sangat panjang ... tapi saya senang bahwa akhirnya membutuhkan teknik yang digunakan dalam regex multiplikasi 651 byte saya (yang akhirnya usang, karena metode yang berbeda dibuat untuk 50 byte regex). Saya berharap masalah akan muncul yang memerlukan trik ini: Beroperasi pada dua angka, yang keduanya kekuatan dari basis yang sama, dalam satu lingkaran, dengan menambahkan mereka bersama-sama dengan jelas dan memisahkan mereka di setiap iterasi.
Tetapi karena kesulitan dan panjangnya algoritma ini, saya menggunakan lookaheads molekuler (dari bentuk (?*...)
) untuk mengimplementasikannya. Itu adalah fitur yang tidak ada dalam ECMAScript atau mesin regex utama lainnya, tetapi yang saya miliki implementasikan di mesin saya . Tanpa tangkapan apa pun di dalam molekul lookahead, secara fungsional setara dengan lookahead atom, tetapi dengan tangkapan itu bisa sangat kuat. Mesin akan mundur ke lookahead, dan ini dapat digunakan untuk memperkirakan nilai yang berputar melalui semua kemungkinan (untuk pengujian nanti) tanpa menggunakan karakter input. Menggunakannya dapat menghasilkan implementasi yang jauh lebih bersih. (Variable-length lookbehind setidaknya memiliki kekuatan yang sama dengan molekuler lookahead, tetapi yang terakhir cenderung membuat implementasi yang lebih mudah dan elegan.)
Jadi panjang 733 dan 690 byte sebenarnya tidak mewakili inkarnasi solusi yang kompatibel dengan ECMAScript - karenanya "+" setelahnya; tentu saja mungkin untuk mem-port algoritma itu ke ECMAScript murni (yang akan menambah panjangnya sedikit) tetapi saya tidak menyadarinya ... karena saya memikirkan algoritma yang jauh lebih sederhana dan lebih ringkas! Salah satu yang dapat dengan mudah diimplementasikan tanpa melihat molekul. Ini juga jauh lebih cepat.
Yang baru ini, seperti yang sebelumnya, harus menebak faktor terbalik, bersepeda melalui semua kemungkinan dan mengujinya untuk pertandingan. Ini membagi N dengan 2 untuk memberikan ruang bagi pekerjaan yang perlu dilakukan, dan kemudian membuat lingkaran di mana ia akan berulang kali membagi input dengan pembagi yang dimulai pada 3 dan bertambah setiap kali. (Dengan demikian, 1! Dan 2! Tidak dapat dicocokkan dengan algoritme utama, dan harus ditangani secara terpisah.) Pembagi ini terus dilacak dengan menambahkannya ke hasil bagi; dua angka ini dapat dipisahkan secara jelas karena, dengan asumsi M! == N, hasil bagi berjalan akan terus dibagi oleh M sampai sama dengan M.
Regex ini melakukan pembagian-oleh-variabel di bagian paling dalam dari loop. Algoritma pembagian sama dengan di regex saya yang lain (dan mirip dengan algoritma multiplikasi): untuk A≤B, A * B = C jika ada hanya jika C% A = 0 dan B adalah angka terbesar yang memenuhi B≤C dan C% B = 0 dan (CB- (A-1))% (B-1) = 0, di mana C adalah dividen, A adalah pembagi, dan B adalah hasil bagi. (Algoritma yang sama dapat digunakan untuk kasus yang A≥B, dan jika tidak diketahui bagaimana A dibandingkan dengan B, satu tes keterbagian tambahan adalah semua yang diperlukan.)
Jadi saya suka bahwa masalahnya dapat dikurangi menjadi bahkan lebih sedikit kompleksitas daripada regex Fibonacci dioptimalkan golf saya , tapi saya mengeluh dengan kekecewaan bahwa teknik multiplexing-power-of-the-same-base saya harus menunggu masalah lain yang sebenarnya membutuhkannya, karena yang ini tidak. Ini adalah kisah tentang algoritma multiplikasi 651 byte saya yang digantikan oleh 50 byte, sekali lagi!
Sunting: Saya dapat menjatuhkan 1 byte (119 → 118) menggunakan trik yang ditemukan oleh Grimy yang dapat memperpendek pembagian dalam kasus bahwa hasil bagi dijamin lebih besar dari atau sama dengan pembagi.
Tanpa basa-basi lagi, inilah regex:
Versi benar / salah (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?$
Cobalah online!
Kembalikan faktorial terbalik atau tidak cocok (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?$
Cobalah online!
Kembalikan faktorial terbalik atau tidak cocok, dalam 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?$
Dan versi bebas spasi dengan komentar:
^
(?= # 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
Sejarah lengkap optimalisasi golf saya di regex ini ada di github:
regex untuk mencocokkan nomor faktorial - metode perbandingan multiplisitas, dengan molekuler lookahead.txt
regex untuk mencocokkan nomor faktorial.txt (yang ditunjukkan di atas)
((x*)x*)
((x*)+)
((x+)+)
n=3!\2
3−3=0
Mesin .NET regex tidak meniru perilaku ini dalam mode ECMAScript-nya, dan dengan demikian, 117 byte regex berfungsi:
Cobalah online! (versi eksponensial-perlambatan, dengan mesin .NET regex + emulasi ECMAScript)
1
?