Enumera gli schemi di rima


26

Uno "schema di rime" è una stringa di lettere ain modo ztale che le prime occorrenze dei personaggi sono in ordine crescente (senza spazi vuoti), a partire da a. Ad esempio (con le prime occorrenze contrassegnate):

abccdbebdcfa
^^^ ^ ^   ^

Il numero di schemi di rima di lunghezza Nè dato dai numeri di Bell B(N) . ( OEIS A000110 )

La sfida

Il tuo compito è implementare un elenco di questi schemi di rima, ovvero una mappatura biiettiva da numeri interi a schemi di rima. Ti viene dato un numero intero positivo N <= 26, nonché un numero intero non negativo 0 <= i < B(N). In alternativa, è possibile utilizzare l'intervallo 1 <= i <= B(N). Dovresti produrre uno schema di rima di lunghezza N, in modo tale che ognuno iproduca una stringa diversa.

È possibile scrivere un programma o una funzione, prendendo l'input tramite STDIN (o l'alternativa più vicina), l'argomento della riga di comando o l'argomento della funzione e producendo il risultato tramite STDOUT (o l'alternativa più vicina), il valore di ritorno della funzione o il parametro della funzione (out).

Puoi usare lettere maiuscole o minuscole (in modo coerente).

Il codice deve essere in grado di gestire qualsiasi input valido ragionevole lasso di tempo (ad esempio , non più di un paio d'ore per N = 26, caso peggiore i). Ciò dovrebbe consentire soluzioni che si adattano in modo esponenziale con N(per basi di piccole dimensioni), anche in linguaggi lenti ma che vietano soluzioni che si adattano in modo lineare con i(ovvero B(N)). In particolare, ciò significa che non puoi semplicemente scorrere tutti gli schemi di rima validi Nfino a quando non hai scartato gli ischemi.

Si applicano le regole standard del .

Esempi

L'esatta assegnazione degli ischemi a (cioè l'ordine degli schemi per un dato N) dipende da te. Ma supponiamo che tu abbia scelto l'ordinamento lessicografico, la tua soluzione dovrebbe corrispondere alla seguente tabella (con -input non valido):

N\i 1    2    3    4    5    6    7    8    9    10   11   12   13   14   15
1   a    -    -    -    -    -    -    -    -    -    -    -    -    -    -
2   aa   ab   -    -    -    -    -    -    -    -    -    -    -    -    -
3   aaa  aab  aba  abb  abc  -    -    -    -    -    -    -    -    -    -
4   aaaa aaab aaba aabb aabc abaa abab abac abba abbb abbc abca abcb abcc abcd

Ecco uno script CJam breve che genera tutti gli schemi di rima validi per una data lunghezza (ma non provare più di 10 o aspetteresti un po ').

Sfide correlate


5
Potrei mettere una taglia su una soluzione (ben giocata) di tempo polinomiale (in N), a condizione che non si riveli abbastanza banale ed ero troppo stupido per trovarla.
Martin Ender,

Sebbene la generosità sia destinata a soluzioni temporali polinomiali, mi piacerebbe comunque vedere soluzioni a tempo esponenziale che soddisfino il limite di tempo. (La mia implementazione di riferimento di Mathematica attualmente vincerebbe ancora la sfida.)
Martin Ender

B (26) è il numero Bell più piccolo che non rientra in un numero intero a 64 bit. Meanie. :-(
Anders Kaseorg,

Risposte:


3

CJam, 68 66 byte

r~:W)1a*{__(;\);_,,.*.+}W(*r~{X@\=_2$\/:CX<!{X:C):X;}&C*-C'a+o}W*;

Provalo online.

Questo è il mio primo programma CJam. È nato come porta della mia soluzione Perl ed era inizialmente lungo oltre 130 byte. Ulteriori suggerimenti sul golf sono i benvenuti.

Come per il mio programma Perl, è diviso in due parti.

Part 1:
r~:W                                         | Read the first input (n) and store it in W
    )1a*                                     | Create an array of n+1 1s
        {              }W(*                  | Repeat n-1 times:
         __                                  | Duplicate array twice
           (;\);                             | Remove first element of 1st array. Swap
                                             | arrays. Remove last element of 2nd array
                _,,                          | Duplicate array. Count items. Create range
                   .*.+                      | Multiply arrays. Add 1st array to result

Part 2:
r~                                           | Read the second input (i)
   {                                  }W*    | Repeat n times:
    X@                                       | Push y (initially 1). Bring item 2 (last array) to top
     \=                                      | Swap top two items. Pop array[y] (v)
       _2$                                   | Duplicate v. Copy item 2 (i) to top
          \/:CX                              | Swap i & v. i/v. Store in C (c). Push y
               <!{       }&                  | If !(i/v < c):
                  X:C):X;                    | c = y. ++y (store in X)
                           C*-C'a+o          | i -= c * v. Push y. Push "a". Add c. Print
                                         ;   | Discard top item (integer 0)

Per eseguire il debug gli array creati dalla Parte 1 add ]_`o~tra le parti 1 e 2. Se n è 5, gli array sarà simile a questo: [[1 1 1 1 1 1] [1 2 3 4 5] [2 5 10 17] [5 15 37] [15 52]]. Gli indici 0 di ciascun array non vengono utilizzati, ma rendono più semplice non dover calcolare gli offset. Le matrici sono calcolate in questo modo:

[2 5 10 17] [2 5 10 17] [2 5 10 17]        | Duplicate twice
[2 5 10 17] [2 5 10 17] [5 10 17]          | Discard first item of array
[2 5 10 17] [5 10 17] [2 5 10 17]          | Swap last two arrays
[2 5 10 17] [5 10 17] [2 5 10]             | Discard last item of array
[2 5 10 17] [5 10 17] [2 5 10] [2 5 10]    | Duplicate array
[2 5 10 17] [5 10 17] [2 5 10] 3           | Count items in array
[2 5 10 17] [5 10 17] [2 5 10] [0 1 2]     | Integer to range 0 - n-1
[2 5 10 17] [5 10 17] [0 5 20]             | Multiply arrays [2*0 5*1 10*2]
[2 5 10 17] [5 15 37]                      | Add arrays [5+0 10+5 17+20]

Mantiene una copia del vecchio array mentre calcola quello successivo. Le matrici vengono lette e scartate in ordine inverso dalla Parte 2.


13

Python 2, 153

u=[1]*999;i=60;exec"u[i]=i%30*u[i-30]+u[i-29];i+=1;"*900
def x(l,n,a=0):m=u[30*l+a];c=n>=a*m;return'.'*l and chr(65+min(n/m,a))+x(l-1,[n%m,n-m*a][c],a+c)

Utilizza l'ordine alfabetico e l'indicizzazione basata su 0.

Puoi lindicare la lunghezza di un suffisso delle lettere e aindicano il numero di lettere distinte che sono stati utilizzati nella parte precedente. Quindi una funzione p(l,a)che calcola il numero di modi per selezionare le lettere rimanenti potrebbe essere 40 byte:

p=lambda l,a:l<1or a*p(l-1,a)+p(l-1,a+1)

Tuttavia, questo è troppo lento per la sfida, quindi i valori necessari vengono precalcolati e archiviati unell'array. In ogni fase del calcolo, se la lettera successiva è quella agià utilizzata, n = k * p (l - 1, a) + n ' dove k è la lettera dell'alfabeto con indice 0 e n' è il valore di nper la chiamata di funzione successiva, che contiene le informazioni sulle lettere rimanenti. Se viene utilizzata una nuova lettera, allora n = a * p (l - 1, a) + n ' .


1
Quanto tempo impiega l'input nel caso peggiore?
Michael Klein,

1
@MichaelKlein Una quantità trascurabile di tempo.
feersum

Questo è esattamente quello che stavo pensando di fare (tranne che l'avrei fatto con JS). Bel lavoro! +1
ETHproductions

11

Haskell (GHC 7.10), 150 byte

s=(1,\_->[]):s
k!((y,b):l@((x,a):_))|let h i|i<x=k:a i|(p,q)<-divMod(i-x)y=p:b q=(x+k*y,h):(k+1)!l
n#i=(['a'..]!!).fromEnum<$>snd(iterate(0!)s!!n!!0)i

L'operatore n # icalcola lo ischema della rima th (zero indicizzato) di lunghezza n. Funziona con operazioni O (n²) (big-integer), sfruttando le pigre liste infinite di Haskell per la memorizzazione automatica. Esecuzioni campione:

*Main> 26 # 0
"abcdefghijklmnopqrstuvwxyz"
*Main> 26 # 1
"abcdefghijklmnopqrstuvwxya"
*Main> 26 # 2
"abcdefghijklmnopqrstuvwxyb"
*Main> 26 # 49631246523618756271
"aaaaaaaaaaaaaaaaaaaaaaaabb"
*Main> 26 # 49631246523618756272
"aaaaaaaaaaaaaaaaaaaaaaaaab"
*Main> 26 # 49631246523618756273
"aaaaaaaaaaaaaaaaaaaaaaaaaa"
*Main> [1 # i | i <- [0..0]]
["a"]
*Main> [2 # i | i <- [0..1]]
["ab","aa"]
*Main> [3 # i | i <- [0..4]]
["abc","aba","abb","aab","aaa"]
*Main> [4 # i | i <- [0..14]]
["abcd","abca","abcb","abcc","abac","abaa","abab","abbc","abba","abbb","aabc","aaba","aabb","aaab","aaaa"]

(Se il massimo N fosse 25 invece di 26, .fromEnumpotrebbe essere rimosso, perché B (25) si inserisce in un 64-bit Int.)


1
Sembra fantastico. Ti dispiacerebbe aggiungere una versione meno giocata a golf per facilitare il trekking?
Michael Klein,

4

Perl 257 + 1 (-p flag) = 258

Perl 182 + 10 (-pMbignum flags) = 192

($n,$i)=split;@m=[@a=(1)x($n+1)];while($a[2]){push@m,[@a=map{$a[$_]*$_+$a[$_+1]}0..$#a-1]}$_='';$y=1;while($w=pop@m){$c=int($i/($v=$$w[$y]));$c=$y++if($c>=$y);$i-=$c*$v;$_.=chr$c+65}

Grazie a dev-nul per aver salvato molti byte! Ora l'ho riscritto in base a ciò che ho imparato facendo la versione di CJam.

Calcola la rima in ordine alfabetico crescente, 0 indicizzato.

Due parti: la parte 1 è 128 90 byte e calcola una matrice per la parte 2. La parte 2 è 129 92 byte e fa alcuni semplici calcoli per calcolare ogni lettera. Se potessi liberarmi della matrice e sostituirla con due semplici numeri, potrei calcolare un singolo percorso attraverso la matrice per ciascun numero e salvare molti byte! Apparentemente, quell'idea non funziona!

Sfortunatamente, non igenera le rime giuste per valori superiori a 9007199254740992, ma funziona magnificamente per valori bassi! Ho aggiunto la libreria Bignum al costo di 11 byte. Viene eseguito dalla riga di comando con perl -pMbignum bell-rhyme.pl. -pMbignum = 10 byte. È anche molto veloce per qualsiasi valore di input.


2

Oracle SQL 11.2, 412 284 283 byte

WITH a AS(SELECT CHR(96+LEVEL)d,LEVEL b FROM DUAL CONNECT BY LEVEL<=:i),v(s,c,n)AS(SELECT d,1,1 FROM a WHERE b=1 UNION ALL SELECT s||d,b,LENGTH(REGEXP_REPLACE(s||d,'([a-z])\1+','\1'))FROM v,a WHERE(b<=n OR b=c+1)AND LENGTH(s)<:n)SELECT s FROM v WHERE:n=LENGTH(s)AND:i<=:n ORDER BY 1;

Purtroppo ha una lunghezza massima di 8. Qualsiasi valore maggiore risulta in: ORA-01489: il risultato della concatenazione di stringhe è troppo lungo

Un-golfed

WITH a AS(SELECT CHR(96+LEVEL)d,LEVEL b FROM DUAL CONNECT BY LEVEL<=:i),
v(s,c,n) AS
(
  SELECT d,1,1 FROM a WHERE b=1
  UNION ALL
  SELECT s||d,b,LENGTH(REGEXP_REPLACE(s||d,'([a-z])\1+','\1')) 
  FROM v,a 
  WHERE (b<=n OR b=c+1) AND LENGTH(s)<:n
)
SELECT s FROM v WHERE LENGTH(s)=:n AND :i<=:n ORDER BY 1;

La vista a genera: i lettere nella colonna a e il loro valore in b.

La vista ricorsiva v accetta la stringa che viene costruita come parametro v, il valore dell'ultima lettera utilizzata in c e il valore della lettera più grande utilizzata in n. Il parametro n è uguale alla lunghezza della stringa senza alcuna lettera duplicata, ecco a cosa serve la regex.

Una lettera è valida se il suo valore è <= il valore della lettera più grande già utilizzata o è la lettera successiva da utilizzare.

In qualche modo la query ha bisogno della parte LENGTH (s) <: n per funzionare, mi manca qualcosa nel modo in cui funziona la query.

Il SELECT principale si occupa di filtrare gli input non validi e le stringhe più brevi costruite prima che venga raggiunta la lunghezza target.

Versione da 412 byte

WITH a AS(SELECT * FROM(SELECT d,b,ROW_NUMBER()OVER(PARTITION BY b ORDER BY d)l FROM(SELECT CHR(64+DECODE(MOD(LEVEL,:i),0,:i,MOD(LEVEL,:i)))d,CEIL(LEVEL/:i)b FROM DUAL CONNECT BY LEVEL<=:i*:n))WHERE l<=b),v(s,c,p)AS(SELECT d,1,l FROM a WHERE b=1 UNION ALL SELECT s||d,c+1,l FROM v,a WHERE c+1=b AND(l<=LENGTH(REGEXP_REPLACE(s,'([A-Z])\1+','\1'))OR l=p+1))SELECT s FROM v WHERE LENGTH(s)=:n AND :i<=:n ORDER BY 1;

Non provare la query 412 byte con 26. Mette il database in modalità riservata, almeno sulla mia versione xe in esecuzione in un contenitore finestra mobile su un macbook. Potrei provare gli exadata al lavoro, ma purtroppo ho ancora bisogno di lavorare per vivere.


0

Mathematica, 136 byte

(For[j=2^#-1;t=#2,c=1;m=0;x=t;r=If[#>0,++m,c*=m;d=x~Mod~m+1;x=⌊x/m⌋;d]&/@j~IntegerDigits~2;;c<=t,t-=c;--j];FromCharacterCode[r+64])&

Per completezza, ecco la mia implementazione di riferimento golfizzata. A differenza delle risposte esistenti, ciò non viene eseguito nel tempo polinomiale (è esponenziale Ncon la base 2) ma soddisfa il vincolo temporale (il caso peggiore continuerebbe comunque in meno di mezz'ora).

L'idea è questa:

  • Per ogni schema di rima possiamo identificare le posizioni in cui il carattere massimo finora aumenta:

    ABCDEFGHDIJDEKBBIJEIKHDFII
    ^^^^^^^^ ^^  ^
    

    Possiamo considerare quei segni come un numero binario che semplifica l'iterazione di tutte queste strutture. Dobbiamo iterare da 2 n-1 a 2 n (o viceversa) da dove proviene la complessità temporale esponenziale.

  • Per ciascuna di tali strutture è facile determinare quante stringhe ci siano: solo gli spazi tra i segni possono essere scelti liberamente e il massimo davanti allo spazio ci dice quanti caratteri diversi sono validi in ciascuna posizione. Questo è un prodotto semplice Se questo numero è inferiore a i, lo sottraggiamo da i. Altrimenti, abbiamo trovato la struttura dello schema di rima richiesto.
  • Per enumerare gli schemi nella struttura data, rappresentiamo semplicemente i(o ciò che ne rimane) come un numero a base mista, in cui i pesi delle cifre sono determinati dal numero di caratteri consentiti nelle posizioni rimanenti.

Mi chiedo se ciò consentirebbe una soluzione più breve in alcune delle altre lingue presentate poiché non richiede alcuna memoisation o precomputazione.

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.