Abbina le stringhe la cui lunghezza è una quarta potenza


28

Nell'ambito di questa domanda, consideriamo solo le stringhe che consistono nel carattere xripetuto in numero arbitrario di volte.

Per esempio:

<empty>
x
xx
xxxxxxxxxxxxxxxx

(Beh, in realtà non deve essere x- qualsiasi personaggio va bene purché l'intera stringa abbia solo 1 tipo di carattere)

Scrivi una regex in qualsiasi tipo di regex di tua scelta in modo che corrisponda a tutte le stringhe la cui lunghezza è n 4 per un numero intero non negativo n (n> = 0). Ad esempio, le stringhe di lunghezza 0, 1, 16, 81, ecc. Sono valide; il resto non è valido.

A causa delle limitazioni tecniche, è difficile testare valori di n maggiori di 128. Tuttavia, il tuo regex dovrebbe logicamente funzionare correttamente, indipendentemente.

Nota che non ti è permesso eseguire codice arbitrario nella tua regex (per gli utenti Perl). È consentita qualsiasi altra sintassi (look-around, back-reference, ecc.).

Includi anche una breve spiegazione del tuo approccio al problema.

(Non incollare la spiegazione della sintassi della regex generata automaticamente, poiché sono inutili)


"xx" non è valido, vero?
Kendall Frey,

@KendallFrey: No. Non è valido
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

@nhahtdh pensi che ci sia una possibile risposta a questo?
XX

1
@ Timwi: Sì. Java, PCRE (probabilmente anche Perl, ma non posso testarlo), .NET. Il mio non funziona in Ruby / JS, però.
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

1
Questa domanda è stata aggiunta alle FAQ sulle espressioni regolari di Stack Overflow , in "Advanced Regex-Fu".
aliteralmind

Risposte:


21

Questa (ir) espressione regolare sembra funzionare.

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

Questo regex è compatibile con PCRE, Perl, .NET.

Questo in pratica segue un "albero delle differenze" (non sono sicuro che ci sia un nome proprio per esso), che dice alla regex quante più x corrispondono per la prossima quarta potenza:

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, \4I negozi e gli aggiornamenti la differenza come indicato al 2 °, 3 ° e 4 ° fila, rispettivamente.

Questo costrutto può essere facilmente esteso per poteri superiori.

Certamente non è una soluzione elegante, ma funziona.


+1. Bella risposta. Sebbene questa risposta sia diversa dalla mia (utilizza regex condizionale, mentre la mia no), ha lo stesso spirito della mia soluzione (sfruttando l'albero delle differenze e facendo uso del riferimento posteriore dichiarato in avanti di alcuni motori regex).
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

idea ordinata per quanto riguarda l'albero delle differenze. per i quadrati l'albero è 1 4 9 16 ... 3 5 7 ... 2 2 2, giusto?
Sparr,

@Sparr grazie, e sì
Volatilità,

24

Un'altra soluzione

Questo è, a mio avviso, uno dei problemi più interessanti sul sito. Devo ringraziare deadcode per averlo riportato in cima.

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

39 byte , senza condizionali o asserzioni ... sorta di. Le alternanze, mentre vengono utilizzate ( ^|), sono un tipo di condizionale in un certo senso, per selezionare tra "prima iterazione" e "non prima iterazione".

Questo regex può essere visto funzionare qui: http://regex101.com/r/qA5pK3/1

Sia PCRE che Python interpretano correttamente il regex, ed è stato anche testato in Perl fino a n = 128 , inclusi n 4 -1 e n 4 +1 .


definizioni

La tecnica generale è la stessa delle altre soluzioni già pubblicate: definire un'espressione autoreferenziale che su ciascuna iterazione successiva corrisponde a una lunghezza pari al termine successivo della funzione di differenza diretta, D f , con un quantificatore illimitato ( *). Una definizione formale della funzione di differenza in avanti:

Definizione 1: funzione di differenza in avanti

Inoltre, è possibile definire anche funzioni di differenza ordine superiore:

Definizione 2: seconda funzione di differenza in avanti

O, più in generale:

Definizione 3: kth funzione di differenza in avanti

La funzione di differenza in avanti ha molte proprietà interessanti; è alle sequenze ciò che la derivata è alle funzioni continue. Ad esempio, D f di un polinomio di n ° ordine sarà sempre un polinomio di n-1 ° ordine, e per qualsiasi i , se D f i = D f i + 1 , allora la funzione f è esponenziale, più o meno allo stesso modo che la derivata di e x è uguale a se stessa. La funzione discreta più semplice per cui f = D f è 2 n .


f (n) = n 2

Prima di esaminare la soluzione di cui sopra, iniziamo con qualcosa di un po 'più semplice: una regex che corrisponde a stringhe le cui lunghezze sono un quadrato perfetto. Esame della funzione di differenza in avanti:

DFF: n ^ 2

Significato, la prima iterazione dovrebbe corrispondere a una stringa di lunghezza 1 , la seconda a una stringa di lunghezza 3 , la terza a una stringa di lunghezza 5 , ecc. E, in generale, ogni iterazione dovrebbe corrispondere a una stringa due più lunga della precedente. Il regex corrispondente segue quasi direttamente da questa affermazione:

^(^x|\1xx)*$

Si può vedere che la prima iterazione corrisponderà a una sola xe ogni successiva iterazione corrisponderà a una stringa due più lunga della precedente, esattamente come specificato. Ciò implica anche un test quadrato perfetto sorprendentemente breve in perl:

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

Questo regex può essere ulteriormente generalizzato per adattarsi a qualsiasi lunghezza n -gonale:

Numeri triangolari:
^(^x|\1x{1})*$

Numeri quadrati:
^(^x|\1x{2})*$

Numeri pentagonali:
^(^x|\1x{3})*$

Numeri esagonali:
^(^x|\1x{4})*$

eccetera.


f (n) = n 3

Passando a n 3 , esaminando ancora una volta la funzione di differenza in avanti:

DFF: n ^ 3

Potrebbe non essere immediatamente chiaro come implementarlo, quindi esaminiamo anche la seconda funzione di differenza:

DFF ^ 2: n ^ 3

Pertanto, la funzione di differenza in avanti non aumenta di un valore costante, ma piuttosto di un valore lineare. È bello che il valore iniziale (' -1 °') di D f 2 sia zero, il che salva un'inizializzazione sulla seconda iterazione. La regex risultante è la seguente:

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

La prima iterazione corrisponderà a 1 , come prima, la seconda corrisponderà a una stringa 6 più lunga ( 7 ), la terza corrisponderà a una stringa 12 più lunga ( 19 ), ecc.


f (n) = n 4

La funzione di differenza in avanti per n 4 :

DFF: n ^ 4

La seconda funzione di differenza diretta:

DFF ^ 2: n ^ 4

La terza funzione di differenza in avanti:

DFF ^ 3: n ^ 4

Ora è brutto. I valori iniziali per D f 2 e D f 3 sono entrambi diversi da zero, 2 e 12 rispettivamente, che dovranno essere presi in considerazione. Probabilmente hai già capito che il regex seguirà questo schema:

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

Poiché D f 3 deve corrispondere a una lunghezza di 12 nella seconda iterazione, a è necessariamente 12 . Ma poiché aumenta di 24 ogni termine, il successivo annidamento più profondo deve utilizzare due volte il valore precedente, implicando b = 2 . L'ultima cosa da fare è inizializzare D f 2 . Poiché D f 2 influenza direttamente D f , che è in definitiva ciò che vogliamo abbinare, il suo valore può essere inizializzato inserendo l'atomo appropriato direttamente nella regex, in questo caso (^|xx). La regex finale diventa quindi:

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

Ordini superiori

Un polinomio di quinto ordine può essere abbinato alla seguente regex:
^((^|\2\3{c})(^|\3\4{b})(^|\4x{a})(^x|\1))*$

f (n) = n 5 è un esercizio abbastanza semplice, poiché i valori iniziali per la seconda e la quarta funzione di differenza in avanti sono zero:

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

Per polinomi a sei ordini:
^((^|\2\3{d})(^|\3\4{c})(^|\4\5{b})(^|\5x{a})(^x|\1))*$

Per i polinomi del settimo ordine:
^((^|\2\3{e})(^|\3\4{d})(^|\4\5{c})(^|\5\6{b})(^|\6x{a})(^x|\1))*$

eccetera.

Si noti che non tutti i polinomi possono essere abbinati esattamente in questo modo, se uno qualsiasi dei coefficienti necessari è non intero. Ad esempio, n 6 richiede che a = 60 , b = 8 e c = 3/2 . Questo può essere risolto, in questo caso:

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

Qui ho cambiato b in 6 e c in 2 , che hanno lo stesso prodotto dei valori sopra indicati. È importante che il prodotto non cambi, poiché a · b · c ·… controlla la funzione di differenza costante, che per un polinomio del sesto ordine è D f 6 . Sono presenti due atomi di inizializzazione: uno per inizializzare D f a 2 , come con n 4 , e l'altro per inizializzare la quinta funzione di differenza su 360 , aggiungendo contemporaneamente i due mancanti da b .


Su quali motori hai provato questo?
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳,

Finalmente capisco cosa sta succedendo. In effetti, l'unica cosa necessaria è il supporto per il riferimento futuro. +1
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

@nhahtdh ahh, hai ragione. I riferimenti diretti non sono necessariamente una caratteristica universale.
primo

1
Eccellente! Adoro quanto sia breve, semplice e facile da capire. Con il suo annidamento superficiale, è facile calcolare a mano come si comporterà. Inoltre, è altrettanto veloce delle soluzioni di Volatility e di nhahtdh . E adoro la tua spiegazione dettagliata, inclusa la dimostrazione che questo può anche essere esteso ai polinomi. Darei punti bonus se potessi.
Deadcode,

@Lynn grazie! Non mi aspettavo che ...
primo

13

Ecco una soluzione che non utilizza condizionali, backreferenze dichiarate in avanti o nidificate, lookbehind, gruppi di bilanciamento o ricorsione regex. Utilizza solo lookahead e backreferenze standard, ampiamente supportate. Sono stato ispirato a operare con queste limitazioni a causa di Regex Golf , che utilizza il motore regex ECMAScript.

Il modo in cui funziona questa regex a 50 byte è concettualmente piuttosto semplice e completamente diverso rispetto a tutte le altre soluzioni presentate a questo puzzle. È stato sorprendente scoprire che questo tipo di logica matematica era espressibile in una regex.

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

(I gruppi di acquisizione sono etichettati sopra la regex)

La regex può essere generalizzato a un potere semplicemente sostituendo la 4in{4} con la potenza desiderata.

Provalo online!

Funziona dividendo ripetutamente la quarta potenza più piccola di un numero primo per cui il valore corrente è divisibile. Come tale, il quoziente di ogni passaggio è sempre una quarta potenza, se il valore originale era una quarta potenza. Un quoziente finale di 1 indica che il valore originale era effettivamente una quarta potenza; questo completa la partita. Anche Zero è abbinato.

Innanzitutto utilizza un gruppo \2di acquisizione pigro per acquisire il fattore più piccolo del numero maggiore di 1. In quanto tale, questo fattore è garantito per essere primo. Ad esempio, con 1296 (6 ^ 4) inizialmente catturerà \2= 2.

Quindi, all'inizio di un ciclo che viene ripetuto 4 volte, verifica se il numero corrente è divisibile per \2, con (?=\2+$). La prima volta in questo ciclo, questo test è inutile, ma il suo scopo diventerà evidente in seguito.

Successiva all'interno di questo ciclo interno, esso utilizza il gruppo di acquisizione greedy \4per catturare il fattore più grande del numero piccolo di se stesso: (?=(x+)(\4+)$). In effetti, questo divide il numero per il suo fattore primo più piccolo \2,; ad esempio, 1296 verrà inizialmente catturato come \4= 1296/2 = 648. Notare che la divisione del numero corrente per\2 è implicita. Mentre è possibile dividere esplicitamente il numero corrente per un numero contenuto in un gruppo di acquisizione (che ho scoperto solo quattro giorni dopo aver pubblicato questa risposta), fare questo renderebbe una regex più lenta e più difficile da capire, e non lo è necessario, poiché il fattore più piccolo di un numero maggiore di 1 corrisponderà sempre al suo fattore più grande più piccolo di se stesso (in modo tale che il loro prodotto sia uguale al numero stesso).

Dato che questo tipo di regex può solo "mangiare via" dalla stringa (rendendola più piccola) lasciando un risultato alla fine della stringa, dobbiamo "spostare" il risultato della divisione alla fine della stringa. Questo viene fatto catturando il risultato della sottrazione (il numero corrente meno \4), nel gruppo di cattura \5, e quindi, al di fuori del lookahead, facendo corrispondere una porzione dell'inizio del numero corrente corrispondente \5. Ciò lascia la stringa non elaborata rimanente alla corrispondenza finale\4 in lunghezza.

Ora ritorna all'inizio del ciclo interno, dove diventa evidente il motivo per cui esiste un test di divisibilità per il fattore primo. Abbiamo appena diviso per il più piccolo fattore primo del numero; se il numero è ancora divisibile per quel fattore, significa che il numero originale potrebbe essere divisibile per la quarta potenza di quel fattore. La prima volta che questo test viene eseguito è inutile, ma le successive 3 volte determina se il risultato della divisione implicita per \2è ancora divisibile per \2. Se è ancora divisibile \2all'inizio di ogni iterazione del ciclo, questo dimostra che ogni iterazione ha diviso il numero per \2.

Nel nostro esempio, con un input di 1296, questo verrà ripetuto come segue:

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

Ora il regex può tornare al primo passo; questo è ciò che fa la finale *. In questo esempio, 81 diventerà il nuovo numero; il ciclo successivo sarà il seguente:

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

Ora tornerà di nuovo al primo passaggio, con 1 come nuovo numero.

Il numero 1 non può essere diviso per nessun numero primo, il che renderebbe una non corrispondenza per (?=(xx+?)\2+$), quindi esce dal ciclo di livello superiore (quello con *alla fine). Ora raggiunge ilx?$ . Questo può corrispondere solo a zero o uno. Il numero corrente a questo punto sarà 0 o 1 se e solo se il numero originale era una quarta potenza perfetta; se è 0 a questo punto, significa che il loop di livello superiore non ha mai eguagliato nulla, e se è 1, significa che il loop di livello superiore ha diviso una quarta potenza perfetta fino a quando non è più divisibile per nulla (o era 1 in primo luogo, il che significa che il loop di livello superiore non corrispondeva mai a nulla).

È anche possibile risolverlo in 49 byte eseguendo una divisione esplicita ripetuta (che è anche generalizzata per tutti i poteri - sostituisci la potenza desiderata meno una nella {3}), ma questo metodo è molto, molto più lento, e una spiegazione dell'algoritmo che utilizza va oltre lo scopo di questa risposta:

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

Provalo online!


Dai miei test (fino alla lunghezza 1024), sembra che sia corretto. Tuttavia, il regex è troppo lento - ci vuole molto tempo solo per abbinare la lunghezza 16 ^ 4, quindi è molto difficile verificare un numero elevato. Ma dal momento che non sono richieste prestazioni, voterò quando comprenderò la tua regex.
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

1
Il tuo regex e la volatilità sono fantastici. La loro velocità e brevità mi stupiscono, entrambi corrispondenti a 100000000 in 7,5 secondi sul mio i7-2600k, molto più velocemente di quanto mi sarei aspettato che fosse una regex. La mia soluzione qui è su un ordine di grandezza totalmente diverso, poiché ci vogliono 12 secondi per abbinare 50625. Ma l'obiettivo con il mio non era la velocità, ma piuttosto, realizzare il lavoro con una lunghezza minima del codice usando un set di operazioni molto più limitato.
Deadcode

Le nostre risposte sono veloci, poiché fanno a malapena qualche backtracking. I tuoi fanno un sacco di passi indietro ((((x+)\5+)\4+)\3+)\2+$. Il tuo è anche sorprendente a modo suo, dal momento che non riesco nemmeno a pensare a come abbinare un numero quadrato senza backreference dichiarato in avanti.
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

A proposito, questa domanda non è code-golf, ma un puzzle. Non giudico la soluzione in base alla lunghezza del codice.
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳,

Oh. Questo spiega perché l'hai usato (?:). Quindi dovrei modificare la mia risposta per rendere la versione ottimizzata quella primaria?
Deadcode

8

Soluzione

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

Questo regex è compatibile con Java, Perl, PCRE e .NET. Questo regex utilizza una vasta gamma di funzionalità: look-ahead, look-behind e back-reference dichiarato in avanti. I tipi di riferimenti secondari dichiarati in avanti limitano la compatibilità di questa regex con alcuni motori.

Spiegazione

Questa soluzione utilizza la seguente derivazione.

Espandendo completamente la somma, possiamo dimostrare la seguente uguaglianza:

\ sum \ limits_ {i = 1} ^ n (i + 1) ^ 4 - \ sum \ limits_ {i = 1} ^ ni ^ 4 = (n + 1) ^ 4 - 1
\ sum \ limits_ {i = 1} ^ ni ^ 4 - \ sum \ limits_ {i = 1} ^ n (i-1) ^ 4 = n ^ 4

Uniamo la somma sul lato sinistro:

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

Sottrarre le 2 equazioni (equazione superiore meno equazione inferiore) e quindi combinare le sommazioni sul lato sinistro, quindi semplificarle:

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

Otteniamo la differenza tra la quarta potenza consecutiva come somma di potenza:

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

Ciò significa che la differenza tra le quarte potenze consecutive aumenterà di (12n 2 + 2).

Per facilitare il pensiero, facendo riferimento all'albero delle differenze nella risposta della volatilità :

  • Il lato destro dell'equazione finale è la seconda riga nell'albero delle differenze.
  • L'incremento (12n 2 + 2) è la terza riga nell'albero delle differenze.

Abbastanza matematica. Torna alla soluzione sopra:

  • Il primo gruppo di acquisizione mantiene una serie di numeri dispari per calcolare i 2 come visto nell'equazione.

    Precisamente, la lunghezza del 1 ° gruppo di acquisizione sarà 0 (non utilizzata), 1, 3, 5, 7, ... mentre il ciclo scorre.

    (?<=^x)ximposta il valore iniziale per la serie di numeri dispari. È ^lì per consentire alla prospettiva di essere soddisfatta nella prima iterazione.

    xx\1 aggiunge 2 e passa al numero dispari successivo.

  • Il secondo gruppo di acquisizione mantiene la serie di numeri quadrati per i 2 .

    Precisamente, la lunghezza del secondo gruppo di acquisizione sarà 0, 1, 4, 9, ... mentre il ciclo scorre.

    ^in (^|\1\2)imposta il valore iniziale per la serie di numeri quadrati. E \1\2aggiunge il numero dispari al numero quadrato corrente per avanzare al numero quadrato successivo.

  • Il terzo gruppo di acquisizione (al di fuori di qualsiasi aspetto e consuma effettivamente del testo) corrisponde all'intero lato destro dell'equazione che abbiamo derivato sopra.

    ^xin (^x|\3\2{12}xx)imposta il valore iniziale, che è + 1il lato destro dell'equazione.

    \3\2{12}xxaggiunge l'aumento della differenza (12n 2 + 2) usando n 2 dal gruppo di acquisizione 2 e abbina la differenza contemporaneamente.

Questa disposizione è possibile perché la quantità di testo corrispondente in ciascuna iterazione è maggiore o uguale alla quantità di testo necessaria per eseguire il look-ahead per costruire n 2 .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.