Retina , 66 63 45 43 36 byte
^()(\1(?<1>.\1))+(\1(.(?(4).\4)))*$
Terlepas dari judul yang mengatakan Retina, ini hanyalah .NET biasa yang menerima representasi nomor Loeschian yang unary .
Input 999 dan 1000 baik dalam waktu satu detik.
Cobalah online! (Baris pertama memungkinkan suite tes yang dipisahkan dengan linefeed, dan dua baris berikutnya mengurus konversi menjadi unary untuk kenyamanan.)
Penjelasan
Solusi didasarkan pada klasifikasi bahwa input dapat ditulis sebagai i*i + j*(i + j)
positif i
dan non-negatif j
(karena kita tidak harus menangani input 0
), dan itu n*n
hanya jumlah dari n
bilangan bulat ganjil pertama . Bermain golf ini adalah latihan yang menarik dalam referensi ke depan.
"Referensi ke depan" adalah ketika Anda meletakkan referensi di dalam grup yang dimaksud. Tentu saja itu tidak berfungsi ketika grup digunakan pertama kali, karena tidak ada yang harus direferensikan lagi, tetapi jika Anda menempatkan ini dalam satu lingkaran, maka backreference mendapatkan tangkapan iterasi sebelumnya setiap kali. Ini pada gilirannya, mari kita membangun tangkapan yang lebih besar dengan setiap iterasi. Ini dapat digunakan untuk membuat pola yang sangat ringkas untuk hal-hal seperti angka segitiga, kuadrat, dan angka Fibonacci.
Sebagai contoh, menggunakan fakta bahwa kuadrat hanya jumlah dari n
bilangan bulat ganjil pertama , kita dapat mencocokkan input persegi seperti ini:
(^.|..\1)+$
Pada iterasi pertama, ..\1
tidak dapat bekerja, karena \1
belum memiliki nilai. Jadi kita mulai dengan ^.
, menangkap satu karakter ke dalam grup 1
. Pada iterasi berikutnya, ^.
tidak lagi cocok karena jangkar, tetapi sekarang ..\1
valid. Ini cocok dengan dua karakter lebih banyak dari iterasi sebelumnya dan memperbarui penangkapan. Dengan cara ini kami mencocokkan peningkatan angka ganjil, mendapatkan kotak setelah setiap iterasi.
Sayangnya, kita tidak bisa menggunakan teknik ini apa adanya. Setelah mencocokkan i*i
, kita perlu mendapatkan i
juga, sehingga kita dapat melipatgandakannya dengan j
. Cara sederhana (tapi panjang) untuk melakukan ini adalah dengan menggunakan fakta bahwa pencocokan i*i
membutuhkan i
iterasi, sehingga kami telah menangkap i
hal-hal dalam kelompok 1
. Kita sekarang bisa menggunakan kelompok penyeimbang untuk mengekstrak ini i
, tapi seperti saya katakan itu mahal.
Sebagai gantinya, saya menemukan cara yang berbeda untuk menulis "jumlah bilangan bulat ganjil berturut-turut" ini yang juga menghasilkan i
kelompok penangkap pada akhirnya. Tentu saja angka i
ganjilnya adalah adil 2i-1
. Ini memberi kita cara untuk menambah referensi maju hanya dengan 1 pada setiap iterasi. Itu bagian ini:
^()(\1(?<1>.\1))+
Ini ()
hanya mendorong tangkapan kosong ke grup 1
(menginisialisasii
ke 0
). Ini cukup setara dengan ^.|
solusi sederhana di atas, tetapi menggunakan |
dalam kasus ini akan sedikit rumit.
Kemudian kita memiliki loop utama (\1(?<1>.\1))
. \1
cocok dengan yang sebelumnya i
, (?<1>.\1)
lalu perbarui grup 1
dengan i+1
. Dari segi yang baru i
, kami baru saja mencocokkan 2i-1
karakter. Tepat seperti yang kita butuhkan.
Setelah selesai, kami telah mencocokkan beberapa kotak i*i
dan grup 1
masih menyimpan i
karakter.
Bagian kedua lebih dekat dengan pencocokan kotak sederhana yang saya tunjukkan di atas. Mari kita abaikan backreference 1
untuk saat ini:
(.(?(4).\1))*
Ini pada dasarnya sama dengan (^.|..\4)*
, kecuali bahwa kita tidak dapat memanfaatkannya^
karena kita tidak di awal string. Alih-alih, kami menggunakan persyaratan, untuk mencocokkan tambahan .\1
hanya ketika kami sudah menggunakan grup 4
. Tetapi pada dasarnya ini persis sama. Ini memberi kita j*j
.
Satu-satunya hal yang hilang adalah j*i
istilah itu. Kami menggabungkan ini dengan j*j
menggunakan fakta bahwa j*j
perhitungan masih membutuhkan j
iterasi. Jadi untuk setiap iterasi, kami juga memajukan kursor i
dengan \1
. Kita hanya perlu memastikan untuk tidak menuliskannya ke dalam grup 4
, karena itu akan mengacaukan dengan pencocokan angka ganjil berturut-turut. Begitulah cara kami tiba di:
(\1(.(?(4).\1)))*