Zählen Sie Reimschemata auf

26

Ein "Reimschema" ist eine Folge von Buchstaben abis z, so dass die ersten Vorkommen der Zeichen in aufsteigender Reihenfolge (ohne Lücken) beginnen a. Zum Beispiel (mit markiertem erstem Vorkommen):

abccdbebdcfa
^^^ ^ ^   ^

Die Anzahl der Reimlängenschemata Nergibt sich aus den Bell-Zahlen B(N) . ( OEIS A000110 )

Die Herausforderung

Ihre Aufgabe ist es, eine Aufzählung dieser Reimschemata zu implementieren, dh eine bijektive Abbildung von ganzen Zahlen auf Reimschemata. Sie erhalten eine positive Ganzzahl N <= 26sowie eine nicht negative Ganzzahl 0 <= i < B(N). Alternativ können Sie den Bereich verwenden 1 <= i <= B(N). Sie sollten ein Reimschema der Länge ausgeben N, sodass jeder ieine andere Zeichenfolge ergibt.

Sie können ein Programm oder eine Funktion schreiben, indem Sie eine Eingabe über STDIN (oder die nächstgelegene Alternative), ein Befehlszeilenargument oder ein Funktionsargument vornehmen und das Ergebnis über STDOUT (oder die nächstgelegene Alternative), einen Funktionsrückgabewert oder einen Funktionsparameter (out) ausgeben.

Sie können entweder Groß- oder Kleinbuchstaben verwenden (konsistent).

Ihr Code muss in der Lage sein, jede gültige Eingabe in angemessener Zeit zu verarbeiten (z. B. nicht länger als ein paar Stunden für den N = 26schlimmsten Fall i). Dies sollte Lösungen ermöglichen, die exponentiell mit skalieren N(für kleine Basen), auch in langsamen Sprachen, aber Lösungen, die linear mit skalieren i(dh B(N)) , verbieten . Dies bedeutet insbesondere, dass Sie nicht alle gültigen Reimschemata mit einer Länge durchlaufen können, Nbis Sie die iSchemata verworfen haben.

Es gelten die Standardregeln für .

Beispiele

Die genaue Zuordnung der izu Schemata (dh die Reihenfolge der Schemata für eine gegebene N) liegt bei Ihnen. Angenommen, Sie haben die lexikografische Reihenfolge gewählt. Ihre Lösung sollte der folgenden Tabelle entsprechen ( -wobei ungültige Eingaben angegeben werden):

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

Hier ist ein kurzes CJam-Skript, das alle gültigen Reimschemata für eine bestimmte Länge generiert (aber versuchen Sie nicht mehr als 10, sonst warten Sie eine Weile).

Verwandte Herausforderungen

Martin Ender
quelle
5
Ich könnte ein Kopfgeld für eine (gut eingespielte) Polynom-Time-Lösung (in N) ausgeben , vorausgesetzt, das ist nicht ganz einfach und ich war einfach zu dumm, um es zu finden.
Martin Ender
Obwohl das Kopfgeld für polynomielle Zeitlösungen ausreicht, würde ich mir immer noch Exponentialzeitlösungen wünschen, die das Zeitlimit einhalten. (Meine eigene Mathematica-Referenzimplementierung würde die Herausforderung derzeit noch gewinnen.)
Martin Ender,
B (26) ist die kleinste Bellsche Zahl, die nicht in eine 64-Bit-Ganzzahl passt. Meanie. :-(
Anders Kaseorg

Antworten:

3

CJam, 68 66 Bytes

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

Probieren Sie es online aus.

Dies ist mein erstes CJam-Programm. Es war ursprünglich ein Port meiner Perl-Lösung und anfangs mehr als 130 Byte lang. Weitere Golfvorschläge sind willkommen.

Wie bei meinem Perl-Programm besteht es aus zwei Teilen.

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)

Um die Arrays von Teil 1 Add erstellt debuggen ]_`o~zwischen Parts 1 & 2. Ist n 5, werden die Felder wie folgt aussehen: [[1 1 1 1 1 1] [1 2 3 4 5] [2 5 10 17] [5 15 37] [15 52]]. Die 0-Indizes jedes Arrays werden nicht verwendet. Sie erleichtern lediglich die Berechnung von Offsets. Die Arrays werden folgendermaßen berechnet:

[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]

Es behält eine Kopie des alten Arrays bei, während das nächste berechnet wird. Die Arrays werden in Teil 2 in umgekehrter Reihenfolge gelesen und verworfen.

CJ Dennis
quelle
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)

Es verwendet alphabetische Reihenfolge und 0-basierte Indexierung.

lBezeichnen wir die Länge eines Suffixes der Buchstaben und adie Anzahl der unterschiedlichen Buchstaben, die im vorhergehenden Teil verwendet wurden. Dann könnte eine Funktion p(l,a), die die Anzahl der Möglichkeiten zur Auswahl der verbleibenden Buchstaben berechnet, 40 Bytes umfassen:

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

Dies ist jedoch für die Abfrage zu langsam, sodass stattdessen die erforderlichen Werte vorberechnet und im uArray gespeichert werden . Wenn in jeder Stufe der Berechnung der nächste Buchstabe der abereits verwendete ist, ist n = k * p (l - 1, a) + n ', wobei k der mit 0 indizierte Buchstabe des Alphabets ist und n' ist Der Wert von nfür den nächsten Funktionsaufruf, der die Informationen zu den verbleibenden Buchstaben enthält. Wenn ein neuer Buchstabe verwendet wird, ist n = a * p (l - 1, a) + n ' .

Feersum
quelle
1
Wie lange dauert die Eingabe im ungünstigsten Fall?
Michael Klein
1
@MichaelKlein Eine vernachlässigbare Menge an Zeit.
Feersum
Dies ist genau das, was ich vorhatte (außer ich hätte es mit JS gemacht). Gute Arbeit! +1
ETHproductions
11

Haskell (GHC 7.10), 150 Bytes

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

Der Operator n # iberechnet das ith (null-indizierte) Reimlängenschema n. Es wird in O (n²) (Big Integer) -Operationen ausgeführt und nutzt die faulen unendlichen Listen von Haskell für die automatische Speicherung. Probeläufe:

*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"]

(Wenn das Maximum von N 25 statt 26 wäre, .fromEnumkönnte das entfernt werden, da B (25) in ein 64-Bit passt Int.)

Anders Kaseorg
quelle
1
Sieht großartig aus. Würde es Ihnen etwas ausmachen, eine weniger golfene Version hinzuzufügen, um das Grokking zu erleichtern?
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}

Vielen Dank an dev-nul für das Speichern vieler Bytes! Ich habe es jetzt neu geschrieben, basierend auf dem, was ich aus der CJam-Version gelernt habe.

Berechnet den Reim in aufsteigender alphabetischer Reihenfolge, 0 indiziert.

Zwei Teile: Teil 1 hat 128 bis 90 Bytes und berechnet eine Matrix für Teil 2. Teil 2 hat 129 bis 92 Bytes und führt einige einfache Berechnungen durch, um jeden Buchstaben zu berechnen. Wenn ich die Matrix loswerden und durch zwei einfache Zahlen ersetzen könnte, könnte ich für jede Zahl einen einzelnen Pfad durch die Matrix berechnen und eine Menge Bytes sparen! Anscheinend funktioniert diese Idee nicht!

Leider gibt es nicht die richtigen Reime für Werte iüber 9007199254740992 aus, aber es funktioniert wunderbar für niedrige Werte! Ich habe die Bignum-Bibliothek für 11 Byte hinzugefügt. Es wird von der Kommandozeile mit ausgeführt perl -pMbignum bell-rhyme.pl. -pMbignum = 10 Bytes. Es ist auch sehr schnell für jeden Eingabewert.

CJ Dennis
quelle
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;

Leider wird es nur bis zu einer Länge von 8 ausgeführt. Ein größerer Wert führt zu: ORA-01489: Ergebnis der Zeichenfolgenverkettung ist zu lang

Nicht golfen

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;

Die a-Ansicht generiert die: i-Buchstaben in Spalte a und ihren Wert in b.

In der rekursiven Sicht v wird die zu erstellende Zeichenfolge als Parameter v, der Wert des letzten in c verwendeten Buchstabens und der Wert des größten in n verwendeten Buchstabens verwendet. Der Parameter n entspricht der Länge der Zeichenfolge ohne doppelten Buchstaben. Dafür ist der reguläre Ausdruck vorgesehen.

Ein Buchstabe ist gültig, wenn sein Wert <= der Wert des größten bereits verwendeten Buchstabens ist oder der nächste zu verwendende Buchstabe ist.

Irgendwie benötigt die Abfrage den Teil LENGTH (s) <: n, damit sie ausgeführt werden kann. In der Funktionsweise der Abfrage muss etwas fehlen.

Das Haupt-SELECT filtert die ungültigen Eingaben und die kürzeren Zeichenfolgen heraus, die erstellt werden, bevor die gewünschte Länge erreicht ist.

412-Byte-Version

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;

Versuchen Sie nicht, die 412-Byte-Abfrage mit 26 auszuführen. Dadurch wird die Datenbank in den eingeschränkten Modus versetzt, zumindest in meiner xe-Version, die in einem Docker-Container auf einem MacBook ausgeführt wird. Ich könnte die Exadata bei der Arbeit anprobieren, aber leider muss ich immer noch arbeiten, um meinen Lebensunterhalt zu verdienen.

Jeto
quelle
0

Mathematica, 136 Bytes

(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])&

Der Vollständigkeit halber hier meine Golf-Referenzimplementierung. Im Gegensatz zu den vorhandenen Antworten läuft dies nicht in Polynomialzeit (es ist in Nmit Basis 2 exponentiell ), sondern erfüllt die Zeitbeschränkung (der schlimmste Fall würde immer noch in weniger als einer halben Stunde laufen).

Die Idee ist folgende:

  • Für jedes Reimschema können wir die Positionen identifizieren, an denen sich das maximale Zeichen bisher erhöht:

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

    Wir können diese Markierungen als Binärzahl behandeln, wodurch es einfach ist, alle derartigen Strukturen zu durchlaufen. Wir müssen von 2 n-1 bis 2 n (oder umgekehrt) iterieren, woraus die exponentielle Zeitkomplexität resultiert.

  • Für jede solche Struktur ist es einfach zu bestimmen, wie viele solcher Zeichenfolgen vorhanden sind: Nur die Lücken zwischen den Markierungen können frei gewählt werden, und das Maximum vor der Lücke gibt an, wie viele verschiedene Zeichen an jeder Position gültig sind. Dies ist ein einfaches Produkt. Ist diese Zahl kleiner als i, subtrahieren wir sie von i. Ansonsten haben wir die Struktur des angeforderten Reimschemas gefunden.
  • Um die Schemata in der gegebenen Struktur aufzulisten, stellen wir einfach i(oder was davon übrig bleibt) als eine gemischte Basiszahl dar, wobei die Gewichtung der Ziffern durch die Anzahl der zulässigen Zeichen in den verbleibenden Positionen bestimmt wird.

Ich frage mich, ob dies eine kürzere Lösung in einigen der anderen eingereichten Sprachen ermöglichen würde, da keine Memoisierung oder Vorberechnung erforderlich ist.

Martin Ender
quelle