Uzunluğu dördüncü bir güç olan dizeleri eşleştir


28

Bu soru kapsamında, sadece xrasgele defalarca tekrarlanan karakterden oluşan dizeleri göz önüne alalım .

Örneğin:

<empty>
x
xx
xxxxxxxxxxxxxxxx

(Aslında, olması gerekmiyor x- karakterin tamamı sadece 1 karaktere sahip olduğu sürece herhangi bir karakter gayet iyi)

Negatif olmayan bir tamsayı n (n> = 0) için uzunluğu n 4 olan tüm dizgileri eşleştirmek için seçtiğiniz herhangi bir regex lezzetine bir regex yazın . Örneğin, 0, 1, 16, 81 vb. Uzunluktaki dizeler geçerlidir; Gerisi geçersiz.

Teknik sınırlama nedeniyle, 128'den büyük n değerlerinin test edilmesi zordur. Ancak, regex'in ne olursa olsun mantıklı bir şekilde çalışması gerekir.

Regex'inizde (Perl kullanıcıları için) keyfi kod çalıştırmanıza izin verilmediğini unutmayın. Başka herhangi bir sözdizimine (etrafa bakma, geriye referans vb.) İzin verilir.

Lütfen soruna yaklaşımınızla ilgili kısa bir açıklama da ekleyin.

(Lütfen otomatik oluşturulan regex sözdizimi açıklamasını yapıştırmayın, çünkü bunlar işe yaramaz)


"xx" geçerli değil, değil mi?
Kendall Frey

@KendallFrey: Hayır. Geçerli değil.
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

@ Nhahtdh bunun olası bir cevabı olduğunu düşünüyor musunuz?
Xem

1
@Timwi: Evet. Java, PCRE (muhtemelen Perl, ancak test edemez), .NET. Mine, Ruby / JS'de çalışmıyor.
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

1
Bu soru, "Gelişmiş Regex-Fu" altındaki Yığın Taşması Düzenli İfade SSS bölümüne eklendi .
aliteralmind

Yanıtlar:


21

Bu (ir) düzenli ifade işe yaramış gibi görünüyor.

^((?(1)((?(2)\2((?(3)\3((?(4)\4x{24}|x{60}))|x{50}))|x{15}))|x))*$

Bu regex PCRE, Perl, .NET lezzetleri ile uyumludur.

Bu, temel olarak, bir sonraki dördüncü güç için kaç tane daha x'in eşleştirileceğini söyleyen bir "fark ağacını" (uygun bir isim olup olmadığından emin değil) izler:

1     16    81    256   625   1296  2401 ...
   15    65    175   369   671   1105 ...
      50    110   194   302   434 ...
         60    84    108   132 ...
            24    24    24 ...  # the differences level out to 24 on the 4th iteration

\2, \3, \4Mağaza ve sırasıyla 2, 3. ve 4. satırlarda gösterilen fark güncellemeleri.

Bu yapı daha yüksek güçler için kolayca genişletilebilir.

Kesinlikle zarif bir çözüm değil, işe yarıyor.


+1. Mükemmel cevap. Bu cevap benimkinden farklı olsa da (koşullu regex kullanıyor, benimki olmasa da), benim çözümümle aynı ruha sahip (fark ağacını kullanarak ve bazı regex motorlarının ileri sürülen geri referanslarını kullanıyor).
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

temiz fikir yeniden fark ağacı. Kareler için ağaç 1 4 9 16 ... 3 5 7 ... 2 2 2, değil mi?
Sparr

@Sparr teşekkürler ve evet
Volatility

24

Başka bir çözüm

Bu, bence sitedeki en ilginç problemlerden biri. Zirveye geri döndüğüm için deadcode'a teşekkür etmeliyim .

^((^|xx)(^|\3\4\4)(^|\4x{12})(^x|\1))*$

39 bayt , herhangi bir şartlandırma veya iddia olmadan ... tür. Değişimler, kullanıldıkları gibi ( ^|), "ilk yineleme" ve "ilk yineleme değil" arasında seçim yapmak için bir şekilde koşullu bir türdür.

Bu regex burada çalışmak için görülebilir: http://regex101.com/r/qA5pK3/1

PCRE'nin ve Python Hem doğru düzenli ifade yorumlamak ve bu da kadar Perl test edilmiştir n = 128 de dahil olmak üzere, n, 4 -1 ve n, 4 + 1 .


Tanımlar

Genel teknik daha önce yayınlanan diğer çözümlerde aynıdır: ileri fark fonksiyonu, bir sonraki terime eşit sonraki her tekrarında bir uzunluğa uyan bir kendi kendine referans ifade tanımlayan D f sınırsız nicelik ile, ( *). İleri fark fonksiyonunun resmi bir tanımı:

Tanım 1: ileri fark fonksiyonu

Ek olarak, daha yüksek dereceli fark fonksiyonları da tanımlanabilir:

Tanım 2: ikinci ileri fark fonksiyonu

Veya daha genel olarak:

Tanım 3: ileri ileri fark fonksiyonu

İleri fark fonksiyonunun birçok ilginç özelliği vardır; türevin ne olduğunu sürekli fonksiyonlara göre sıralar. Örneğin, D f bir bölgesinin N inci polinom her zaman olacaktır , n-1 inci polinom ve herhangi i , eğer D f i = D f i + 1 , işlev f de aynı şekilde, üstel türevi olduğu e x kendi eşittir. En basit ayrık fonksiyonu olan f = D f olan 2 , n .


f (n) = n- 2

Yukarıdaki çözümü incelemeden önce, biraz daha kolay bir şeyle başlayalım: uzunlukları mükemmel bir kare olan dizelerle eşleşen bir regex. İleri fark fonksiyonunun incelenmesi:

FDF: n ^ 2

Yani, birinci yineleme uzunluğu 1 olan bir dize, ikincisi uzunluk 3 olan bir dize, üçüncü uzunluk 5 olan bir dize vb. İle eşleşmelidir ve genel olarak, her yineleme öncekinden iki daha uzun olan bir dizeyle eşleşmelidir. Karşılık gelen regex hemen hemen bu ifadeden izler:

^(^x|\1xx)*$

İlk yinelemenin yalnızca biriyle eşleşeceği xve sonraki her yinelemenin, tam olarak belirtildiği gibi öncekinden iki daha uzun bir dizeyle eşleşeceği görülebilir . Bu aynı zamanda perl'de şaşırtıcı derecede kısa, mükemmel bir kare testi anlamına gelir:

(1x$_)=~/^(^1|11\1)*$/

Bu normal ifade ayrıca, herhangi bir maç için genelleştirilebilir N -gonal uzunluğu:

Üçgen sayılar:
^(^x|\1x{1})*$

Kare sayılar:
^(^x|\1x{2})*$

Beşgen sayılar:
^(^x|\1x{3})*$

Altıgen sayılar:
^(^x|\1x{4})*$

vb.


f (n) = n- 3

İçin taşıma n 3 kez daha ileri fark fonksiyonunu inceleyerek,:

FDF: n ^ 3

Bunun nasıl uygulanacağı hemen belli olmayabilir, bu yüzden ikinci fark fonksiyonunu da inceleriz:

FDF ^ 2: n ^ 3

Dolayısıyla, ileri doğru fark fonksiyonu sabit bir şekilde artmaz, aksine doğrusal bir değer artar. It 'güzel ilk ( 'o -1 inci') değeri D f 2 ikinci tekrarında bir başlatma kaydeder sıfırdır. Ortaya çıkan regex şudur:

^((^|\2x{6})(^x|\1))*$

İlk yineleme , eskisi gibi 1 ile eşleşir , ikincisi daha uzun bir dizeyle 6 ( 7 ), üçüncü ise daha uzun bir dizeyle 12 ( 19 ), vb. Eşleşir.


f (n) = n- 4

N 4 için ileri fark fonksiyonu :

FDF: n ^ 4

İkinci ileri fark fonksiyonu:

FDF ^ 2: n ^ 4

Üçüncü ileri fark fonksiyonu:

FDF ^ 3: n ^ 4

Şimdi bu çirkin. İçin başlangıç değerleri, D f 2 ve D f 3 , her iki sigara sıfır, 2 ve 12 gerekecektir sırasıyla muhasebeleşmesine. Muhtemelen şimdiye kadar regex'in bu modeli takip edeceğini öğrendiniz:

^((^|\2\3{b})(^|\3x{a})(^x|\1))*$

Çünkü D f 3 arasında bir uzunluğa eşleşmelidir 12 ikinci tekrarında, bir zorunlu olan 12 . Ancak , her terim 24 arttığı için , bir sonraki derin yuvalama, b = 2 anlamına gelecek şekilde önceki değerini iki kez kullanmalıdır . Yapılacak son şey, D f'yi başlatmaktır. 2 . Çünkü D f 2 etkiler D f maç için istiyoruz, ne de olsa, doğrudan, değeri, bu durumda, düzenli ifade doğrudan uygun atomu sokulmasıyla başlatıldı edilebilir(^|xx). Son regex sonra olur:

^((^|xx)(^|\3\4{2})(^|\4x{12})(^x|\1))*$

Daha Yüksek Siparişler

Beşinci dereceden bir polinom aşağıdaki regex ile eşleştirilebilir:
^((^|\2\3{c})(^|\3\4{b})(^|\4x{a})(^x|\1))*$

f (n) = n 5 , hem ikinci hem de dördüncü ileri fark fonksiyonlarının başlangıç ​​değerleri sıfır olduğundan, oldukça kolay bir egzersizdir:

^((^|\2\3)(^|\3\4{4})(^|\4x{30})(^x|\1))*$

Altı dereceli polinomlar için:
^((^|\2\3{d})(^|\3\4{c})(^|\4\5{b})(^|\5x{a})(^x|\1))*$

Yedinci dereceden polinomlar için:
^((^|\2\3{e})(^|\3\4{d})(^|\4\5{c})(^|\5\6{b})(^|\6x{a})(^x|\1))*$

vb.

Gerekli katsayıların herhangi biri tamsayı değilse, tüm polinomların tam olarak bu şekilde eşleştirilemeyeceğini unutmayın. Örneğin, n, 6 gerektirir , a = 60 , b = 8 olduğu ve C = 3/2 . Bu, bu durumda, bu konuda çalışılabilir:

^((^|xx)(^|\3\6\7{2})(^|\4\5)(^|\5\6{2})(^|\6\7{6})(^|\7x{60})(^x|\1))*$

Burada değiştirdik b için , 6 ve C için 2 yukarıda belirtilen değerleri aynı ürün olması. Bu ürün olarak, değişmez önemlidir bir · b · c · ... kontroller altıncı dereceden bir polinom içindir sabit fark fonksiyonu, D f 6 . Bir başlatmak için: Bu iki başlatma atomu vardır D f için 2 ile olduğu gibi, n- 4 ve diğer beşinci fark fonksiyonu başlatmak için 360 aynı zamanda eksik iki eklenirken, b .


Bunu hangi motorlarda test ettiniz?
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

Sonunda ne olduğunu anladım. Aslında ihtiyaç duyulan tek şey ileriye dönük referans desteğidir. +1
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

@ nhahtdh ahh, haklısın. İleriye dönük referanslar da mutlaka evrensel bir özellik değildir.
primo

1
Mükemmel! Bunun ne kadar kısa, basit ve anlaşılması kolay olduğunu seviyorum. Sığ yuvalarıyla, nasıl davranacağını elle hesaplamak kolaydır. Ayrıca, Volatility ve nhahtdh'ın çözümleri kadar hızlı . Ve bunun polinomlara bile uzatılabileceğini gösteren gösteriyi içeren ayrıntılı açıklamayı seviyorum. Yapabilseydim bonus puan verirdim.
Deadcode

@ Lynn teşekkürler! Bunu beklemiyordum ...
primo

13

İşte şartlı, ileriye dönük veya iç içe geçmiş geri referanslar, göz kamaştırıcı, dengeleme grupları veya regex özyinelemeyi kullanmayan bir çözüm. Yalnızca çok yaygın olarak desteklenen lookahead ve standart backreences kullanır. ECMAScript regex motorunu kullanan Regex Golf nedeniyle bu sınırlamalar altında çalışmak için ilham aldım .

Bu 50 bayt regex'in çalışma şekli kavramsal olarak oldukça basittir ve bu bulmacanın sunduğu diğer tüm çözümlerden tamamen farklıdır. Bu tür bir matematiksel mantığın bir regex'te anlaşılabilir olduğunu keşfetmek şaşırtıcıydı.

      \2                     \4  \5
^((?=(xx+?)\2+$)((?=\2+$)(?=(x+)(\4+)$)\5){4})*x?$

(Yakalama grupları regex'in üzerinde etiketlenir)

Regex sadece değiştirerek herhangi bir güce genellenebilir 4 girişi {4}istenen gç ile .

Çevrimiçi deneyin!

Geçerli değerin bölebildiği bir asalın en küçük dördüncü gücünü tekrar tekrar bölerek çalışır. Her adımdaki bölüm her zaman dördüncü bir güç olduğundan, orijinal değer dördüncü bir güç ise, 1'in son bölümü, orijinal değerin gerçekten de dördüncü bir güç olduğunu gösterir; bu maçı tamamlar. Sıfır da eşleştirilir.

Öncelikle \2, sayının 1'den büyük olan en küçük faktörünü yakalamak için tembel bir yakalama grubu kullanır . Örneğin, 1296 (6 ^ 4) ile başlangıçta \2= 2 yakalayacaktır .

Daha sonra, 4 kez tekrarlanır bir döngü başlangıcında, bu mevcut sayısı bölünebilir tarafından olup olmadığını görmek için test \2ile,(?=\2+$) . Bu döngü boyunca ilk kez, bu test işe yaramaz, ancak amacı daha sonra belirgin hale gelecektir.

Bu iç döngü içinde Sonra, o açgözlü yakalama grubunu kullanır \4kendisinden Buradaki sayı büyük faktör daha küçük yakalamak için: (?=(x+)(\4+)$). Aslında bu sayıyı en küçük asal çarpana böler \2; Örneğin, 1296 başlangıçta \4= 1296/2 = 648 olarak yakalanır . Geçerli sayının bölü\2 örtük olduğunu unutmayın. Geçerli sayıyı, bir yakalama grubundaki (bu cevabı gönderdikten sadece dört gün sonra keşfettiğim) bulunan bir numaraya açıkça bölmek mümkün olsa da, bunu yapmak daha yavaş ve anlaşılması zor bir regex için yapacaktır. gerekli olduğundan, bir sayının 1'den büyük olan en küçük faktörü her zaman kendisinden daha küçük olan en büyük faktörü ile eşleşecektir (öyle ki ürün sayının kendisine eşit olacak şekilde).

Bu tür regex, dizgenin sonunda bir sonuç bırakarak dizgiden yalnızca “yiyebiliyor” (daha küçük hale getirebildiğinden), bölmenin sonucunu dizginin sonuna kadar “hareket ettirmeliyiz”. Bu, çıkarma sonucunun (mevcut sayı eksi \4) yakalama grubuna alınması \5ve ardından, karşılık gelen geçerli sayının başlangıcının bir kısmıyla eşleşmesi için, görünüşün dışında yakalanmasıyla yapılır \5. Bu, kalan işlenmemiş dizgiyi sonunda eşleşen \4uzunlukta bırakır .

Şimdi iç döngünün başlangıcına geri döner, burada ana faktör tarafından bölünebilirlik için bir test yapılmasının neden belirgin olduğu ortaya çıkar. Numaranın en küçük asal çarpanına daha yeni bölündük; Sayı hala bu faktör tarafından bölünebilir ise, orijinal sayının o faktörün dördüncü gücü ile bölünebileceği anlamına gelir. Bu test ilk defa yapıldığında işe yaramaz, ancak sonraki 3 defa, dolaylı olarak bölmenin sonucunun \2hala bölünebilir olup olmadığını belirler \2. \2Döngünün her bir yinelemesinin başlangıcında hala bölünebilirse , bu her yinelemenin sayıyı böldüğünü kanıtlar \2.

Örneğimizde, 1296 girişi ile bu, aşağıdaki gibi dönecektir:

\2= 2
\4= 1296/2 = 648
\4= 648/2 = 324
\4= 324/2 = 162
\4= 162/2 = 81

Şimdi regex ilk adıma geri dönebilir; finalin *yaptığı şey bu . Bu örnekte, 81 yeni sayı olacaktır; sonraki döngü aşağıdaki gibi gidecektir:

\2= 3
\4= 81/3 = 27
\4= 27/3 = 9
\4= 9/3 = 3
\4= 3/3 = 1

Şimdi yeni numara olarak 1 ile tekrar ilk adıma geri döner.

1 sayısı, eşleşmemesini sağlayacak hiçbir asal sayıya bölünemez, bu (?=(xx+?)\2+$)yüzden en üst seviye döngüsünden çıkar ( *sonunda olan). Şimdi ulaşır x?$. Bu sadece sıfır veya bir ile eşleşebilir. Bu noktadaki mevcut sayı, yalnızca orijinal sayı mükemmel bir dördüncü güç ise ve eğer 0 ya da 1 olacaktır; bu noktada 0 ise, bu üst seviye döngüsünün hiçbir zaman bir şeyle eşleşmediği anlamına gelir ve 1 ise, üst düzey döngü artık hiçbir şey tarafından bölünemez hale gelinceye kadar mükemmel bir dördüncü gücü böldüğü anlamına gelir (veya ilk etapta 1 idi, yani en üst düzey döngü hiçbir şeyle eşleşmedi.

Bunu ayrıca 49 baytta tekrarlayan açık bölümleme yaparak da çözmek mümkündür (ki bu aynı zamanda tüm güçler için de geneldir - istenen güç eksi yerine {3}), ancak bu yöntem çok daha yavaştır ve kullandığı algoritmanın bir açıklamasıdır. bu Cevabın kapsamı dışında:

^((x+)((\2(x+))(?=(\4*)\2*$)\4*(?=\5$\6)){3})?x?$

Çevrimiçi deneyin!


Testlerimden (1024 uzunluğa kadar) doğru görünüyor. Ancak, regex çok yavaş - sadece uzunluğu 16 ^ 4 eşleştirmek için çok zaman alıyor, bu yüzden büyük sayı için doğrulamak çok zordur. Ancak performans gerekli olmadığından, regex'inizi anladığımda çok fazla oy atacağım.
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

1
Regex'iniz ve Volatiliteniz harika. Hızları ve kırılganlıkları beni şaşırtıyor, ikisi de i7-2600k'imde 7.5 saniyede 100000000'ü eşleştiriyorlardı, regex'in beklediğimden çok daha hızlıydı. Buradaki çözümüm, 50625 ile eşleşmesi 12 saniye sürdüğü için, tamamen farklı bir büyüklük düzeninde. Fakat benimki ile hedef hız yapmaktan ziyade, çok daha sınırlı bir işlem kümesi kullanarak işi en az kod uzunluğunda yapmaktı.
Deadcode

Cevaplarımız hızlı, çünkü herhangi bir geri izleme yapmıyorlar. Seninkiler çok fazla geri adım atıyor ((((x+)\5+)\4+)\3+)\2+$. Sizinki de kendi harikasıdır, çünkü ileriye dönük geri bildirimler olmadan kare sayıları eşleştirmeyi bile düşünemiyorum.
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

Bu arada, bu soru kod golf değil, bir bilmece. Çözümü kod uzunluğuna göre yargılamıyorum.
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

Ah. Bu neden kullandığını açıklıyor (?:). Öyleyse, optimize edilmiş sürümü birincil yapmak için cevabımı düzenlemeli miyim?
Deadcode

8

Çözüm

^(?:(?=(^|(?<=^x)x|xx\1))(?=(^|\1\2))(^x|\3\2{12}xx))*$

Bu regex Java, Perl, PCRE ve .NET lezzetleri ile uyumludur. Bu regex oldukça çeşitli özellikler kullanıyor: ileriye dönük, geriye dönük ve ileriye dönük geri referans. İleriye dönük beyan edilmiş geri referans türleri, bu regex'in birkaç motorla uyumluluğunu sınırlar.

açıklama

Bu çözelti, aşağıdaki türevlendirmeyi kullanır.

Toplamı tamamen genişleterek, aşağıdaki eşitliği kanıtlayabiliriz:

\ sum \ sınırlar_ {i = 1} ^ n (i + 1) ^ 4 - \ toplam \ sınırlar_ {i = 1} ^ ni ^ 4 = (n + 1) ^ 4 - 1
\ sum \ limi_ \ i = 1} ^ ni ^ 4 - \ sum \ limi_ \ i = 1} ^ n (i-1) ^ 4 = n ^ 4

Sol taraftaki toplamı birleştirelim:

\ sum \ sınırlar_ {i = 1} ^ n (4 (i + 1) ^ 3 - 6 (i + 1) ^ 2 + 4 (i + 1) - 1) = (n + 1) ^ 4 - 1
\ sum \ sınırlar_ {i = 1} ^ n (4i ^ 3 - 6i ^ 2 + 4i - 1) = n ^ 4

2 denklemi çıkarın (üst eşitlik eksi alt eşitlik) ve sonra sol taraftaki toplamları birleştirin, ardından sadeleştirin:

\ sum \ sınırlar_ {i = 1} ^ n (12i ^ 2 + 2) = (n + 1) ^ 4 - n ^ 4 - 1

Ardışık dördüncü güçler arasındaki farkı güç toplamı olarak elde ederiz:

(n + 1) ^ 4 - n ^ 4 = \ toplam \ limit_ {i = 1} ^ n (12i ^ 2 + 2) + 1

Bu demektir ki fark, birbirini takip eden dördüncü güçler arasında olacaktır artış (12n ile 2 + 2).

Düşünmeyi kolaylaştırmak için, Volatility'nin cevabındaki farklılık ağacına atıfta bulunarak :

  • Son denklemin sağ tarafı, fark ağacındaki 2. satırdır.
  • Artış (12n 2 + 2), fark ağacındaki 3. satırdır.

Yeterince matematik. Yukarıdaki çözüme geri dönün:

  • 1. yakalama grubu, i'yi hesaplamak için bir seri sayı tutar. 2 denkleminde görüldüğü gibi.

    Kesin konuşursak, birinci yakalama grubunun uzunluğu, döngü tekrarlanırken 0 (kullanılmamış), 1, 3, 5, 7, ... olacaktır.

    (?<=^x)xtek sayı serisinin başlangıç ​​değerini ayarlar. ^İleri bakma birinci tekrarda tatmin izin vermek sadece orada olduğunu.

    xx\1 2 ekler ve sonraki tek sayıya ilerler.

  • 2 yakalama grup i için kare sayısı serilerini sunar 2 .

    Tam olarak konuşursak, 2. yakalama grubunun uzunluğu, döngü tekrarlanırken 0, 1, 4, 9, ... olacaktır.

    ^in (^|\1\2)kare sayı serisinin başlangıç ​​değerini ayarlar. Ve bir \1\2sonraki kare sayıya ilerlemek için tek sayıyı geçerli kare sayıya ekler.

  • 3. yakalama grubu (ileriye dönük ve gerçekte metin tüketenlerin dışında), yukarıda türettiğimiz denklemin sağ tarafıyla eşleşiyor.

    ^xiçinde (^x|\3\2{12}xx)kümeler başlangıç değeri olan + 1denklemin sağ-taraf.

    \3\2{12}xxfarkı (12n artış ekler 2 , n + kullanarak 2) 2 grubu 2 yakalayan ve aynı zamanda fark eşleşir.

Bu düzenleme, her yinelemede eşleşen metin miktarının, n 2'yi oluşturmak için ileri doğru bakışı gerçekleştirmek için gereken metin miktarından daha fazla veya ona eşit olması nedeniyle mümkündür .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.