ECMAScript Regex, 733+ 690+ 158 119 118 (117🐌) bayt
Regex'e olan ilgim 4½ yıldan fazla bir süredir kullanılmadığı zaman yenilenen canlılığa neden oldu. Böylece, ECMAScript düzenli regex'lerle eşleşecek daha doğal sayı kümeleri ve işlevleri aramaya başladım, regex motorumu geliştirmeye devam ettim ve PCRE'yi de geliştirmeye başladım.
ECMAScript regex'te matematiksel fonksiyonlar oluşturma konusundaki sıkıntıdan etkilendim. Sorunlara tamamen farklı bir perspektiften yaklaşılmalıdır ve kilit bir iç görü gelişine kadar, çözülebilir olup olmadıkları bilinmemektedir. Belirli bir problemi çözülebilir hale getirmek için hangi matematiksel özelliklerin kullanılabileceğini bulmakta çok daha geniş bir ağ yaratmaya zorlar.
Faktör sayıları eşleştirmek, 2014'te uğraşmayı bile düşünmediğim bir problemdi - ya da eğer öyleysem, ancak bir an için mümkün olamayacak kadar azdı. Ancak geçen ay yapılabileceğini anladım.
Diğer ECMA regex yayınlarımda olduğu gibi, bir uyarı vereceğim : ECMAScript regex'teki sıradışı matematiksel problemlerin nasıl çözüleceğini öğrenmenizi tavsiye ederim. Benim için büyüleyici bir yolculuk oldu ve bunu denemek isteyebilecek herhangi biri için, özellikle de sayı teorisine ilgi duyanlar için onu mahvetmek istemiyorum. Birbiri ardına çözmek için art arda spoiler etiketli önerilen sorunların bir listesi için bu önceki yazıya bakın .
Bu yüzden , sizin için şımarık bazı düzensiz regex sihrini istemiyorsanız, daha fazla okumayın . Bu sihri kendiniz bulmaya bir göz atmak istiyorsanız, yukarıda belirtilen bağlantıda belirtildiği gibi ECMAScript regex'teki bazı sorunları çözerek başlamanızı şiddetle tavsiye ederim.
Bu benim fikrimdi:
Bu sayı kümesini eşleştirmedeki sorun, diğerlerinin çoğunda olduğu gibi, ECMA'da genellikle iki değişen sayıyı bir döngüde takip etmenin mümkün olmamasıdır. Bazen çoğaltılabilirler (örneğin, aynı tabanın güçleri açık bir şekilde birbirine eklenebilir), ancak özelliklerine bağlıdır. Bu yüzden giriş numarasıyla yeni başlayamadım ve 1'e ulaşana kadar artan şekilde artan bir temettü ile bölemedim (ya da en azından düşündüm).
Sonra, asal faktörlerin çokluğunu faktör sayıları üzerinde araştırdım ve bunun için bir formül olduğunu öğrendim - ve muhtemelen bir ECMA regex'inde uygulayabileceğim bir tane!
Bir süre üzerine bastırdıktan ve diğer bazı regex'leri yaptıktan sonra, faktoring regex'i yazma görevini üstlendim. Birkaç saat sürdü, ancak iyi çalıştı. Ek bir avantaj olarak, algoritma ters faktörü eşleşme olarak geri getirebilir. Bundan kaçınmak bile yoktu; ECMA'da nasıl uygulanması gerektiğine bağlı olarak, başka bir şey yapmadan önce ters faktörün ne olduğunu tahmin etmek gerekir.
Dezavantajı, bu algoritmanın çok uzun bir regex için yapılmış olmasıydı ... ama 651 bayt çarpım regex'imde kullanılan bir teknik gerektirdiği için memnun oldum (artık bitmiş olan, çünkü 50 bayt regex). Bu numarayı gerektiren bir problemin ortaya çıkacağını umuyordum: Aynı bazın her ikisinin de gücü olan iki sayı üzerinde, bir döngü içinde, her birini bir arada ekleyerek ve bunları birbirinden ayırarak çalıştırarak.
Ancak bu algoritmanın zorluğundan ve uzunluğundan dolayı, onu uygulamak için moleküler bakış açıları (formun (?*...)
) kullandım. Bu, ECMAScript'te veya diğer ana akım regex motorlarında değil, motorumda uyguladığım özelliktir . Moleküler bir görünüşün içinde herhangi bir yakalama olmadan, işlevsel olarak atomik bir görünüşe eşdeğerdir, ancak yakalamalarla çok güçlü olabilir. Motor, geriye doğru geriye doğru ilerler ve bu, girişin karakterlerini tüketmeden tüm olasılıkları (daha sonra test etmek için) dolaşan bir değeri tahmin etmek için kullanılabilir. Bunları kullanmak daha temiz bir uygulama için yapabilir. (Değişken uzunluklu gözbebeği, moleküler gözle güç bakımından en az eşittir, ancak ikincisi daha basit ve zarif uygulamalar yapmaya meyillidir.)
Dolayısıyla, 733 ve 690 bayt uzunlukları aslında çözümün ECMAScript uyumlu enkarnasyonlarını temsil etmiyor - bu nedenle onlardan sonra "+"; Bu algoritmayı saf ECMAScript'e (uzunluğunu biraz artıracaktır) taşımak elbette mümkün ama ben bunu çözemedim ... çünkü çok daha basit ve daha kompakt bir algoritma düşündüm! Moleküler bakış açıları olmadan kolayca uygulanabilir. Aynı zamanda önemli ölçüde daha hızlı.
Bu yeni olan, önceki gibi, ters faktörü tahmin etmek, tüm olasılıklar arasında dolaşmak ve onları bir eşleşme için test etmek zorundadır. Yapması gereken işe yer açmak için N'yi 2'ye böler ve daha sonra girişi tekrar tekrar 3'te başlayan ve her seferinde artan bir bölen ile böleceği bir döngü oluşturur. (Dolayısıyla, 1! Ve 2! Ana algoritma ile eşleştirilemez ve ayrı olarak ele alınmalıdır.) Bölen, çalışmakta olan bölüme eklenerek takip edilir; Bu iki sayı açıkça ayrılabilir, çünkü M! == N, çalışan bölüm M'ye eşit olana kadar M tarafından bölünebilir olmaya devam edecek
Bu regex, döngünün en iç kısmında bir değişkene bölünür. Bölünme algoritması benim diğer regex'lerimdekiyle aynıdır (ve çarpma algoritmasına benzemektedir): A≤B, A * B = C için, yalnızca eğer C% A = 0 ve B ise B≤C'yi sağlayan en büyük sayıysa ve C% B = 0 ve (CB- (A-1))% (B-1) = 0, burada C, temettü, A, bölen, ve B, bölümdür. (A≥B'nin durumu için benzer bir algoritma kullanılabilir ve A'nın B ile karşılaştırıldığı bilinmiyorsa, gereken tek ekstra bölünebilirlik testi gerekir.)
Sorunun benim golf optimize daha az karmaşıklık indirgenemez başardı seviyorum Yani Fibonacci regex , ama benim çoğullama-güçler--aynı-baz tekniği başka sorun için beklemek zorunda olacağı hayal kırıklığı ile iç çekiyorum yok Bu aslında onu gerektirir, çünkü buna gerek yok. 651 bayt çarpım algoritmamın öyküsü, 50 baytlık bir sayı ile tekrar tekrar destekleniyor!
Düzenleme: Grimy tarafından bulunan ve bölümün bölenden büyük veya eşit olması garanti edildiğinde bölünmeyi daha da kısaltabilecek bir hile kullanarak 1 baytı (119 → 118) bırakmayı başardım .
Daha fazla uzatmadan, işte regex:
Doğru / yanlış sürüm (118 bayt):
^((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?$
Çevrimiçi deneyin!
Faktör veya eşleşmeyen ters dön (124 bayt):
^(?=((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?$
Çevrimiçi deneyin!
ECMAScript + 'de\K
(120 bayt) ters faktoringi veya eşleşmeyen değeri döndür :
^((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?$
Ve yorum içeren serbest aralıklı sürüm:
^
(?= # 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
Bu regex'lerin golf optimizasyonlarının tam tarihi github'da:
faktörel sayıları eşleştirmek için regex - çokluklu karşılaştırma yöntemi, moleküler lookahead.txt ile eşleştirilen faktorel sayıları.txt ile
regex (yukarıda gösterilen)
((x*)x*)
((x*)+)
((x+)+)
n = 3 !\2
3 - 3 = 0
.NET regex motoru, ECMAScript modunda bu davranışı öykünmez ve bu nedenle 117 bayt regex çalışır:
Çevrimiçi deneyin! (üstel yavaşlama sürümü, .NET regex engine + ECMAScript öykünmesi ile)
1
?