Ich habe meinen Schädel schon seit einiger Zeit auf dieses Problem geschlagen und es beginnt mich wirklich zu frustrieren. Das Problem ist:
Ich habe eine Reihe von Zeichen, A
, B
, C
, und D
. Ich muss sagen, auf wie viele Arten eine Zeichenfolge aus diesen Zeichen erstellt werden kann, wann die Länge ist n
und jedes Zeichen gerade mal vorkommen muss.
Zum Beispiel n = 2
lautet die Antwort für 4:
AA
BB
CC
DD
Die Antwort für n = 4
ist 40. Einige dieser gültigen Zeichenfolgen sind:
AAAA
AABB
CACA
DAAD
BCCB
Ich bin fest entschlossen, eine Logik zu entwickeln. Ich denke, es könnte eine DP-Lösung dafür geben. Es kommt nicht in Frage, mich brutal durchzusetzen: Die Anzahl der Lösungen wächst schnell zu einer großen Anzahl.
Ich habe versucht, alle möglichen Ideen auf Papier zu zeichnen, ohne Erfolg. Fast alle diese Ideen musste ich verwerfen, da ihre Komplexität zu groß war. Die Lösung sollte effizient sein für n = 10^4
.
Eine meiner Ideen war es, niemals die tatsächlichen Saiten im Auge zu behalten, sondern nur, ob jeder Charakter gerade oder ungerade Male aufgetaucht ist. Ich konnte mir keine Möglichkeit einfallen lassen, diese Logik anzuwenden.
Kann mir jemand helfen?
quelle
Antworten:
Richten Sie dies
f(n,d)
als Funktion ein, die die Anzahl der Permutationen mit (gerader) Länge untern
Verwendungd
unterschiedlicher Zeichen angibt (dhd=4
in Ihrem Fall).Klar
f(0,d) = 1
undf(n,1) = 1
da es nur eine Anordnung gibt, wenn Sie nur ein Zeichen oder keine Leerzeichen haben.Nun der Induktionsschritt:
Um eine gültige Zeichenfolge mit
d
Zeichen zu erstellen, nehmen Sie eine kürzere Zeichenfolge mit gerader Länged-1
und verwenden Sie die Länge, indem Sie ein gerades Vielfaches dieses neuen Zeichens hinzufügen. Die Anzahl der Arrangements ist darauf zurückzuführen,choose(n,n_newdigits)
dass Sien_newdigit
Orte aus der Gesamtlänge der Zeichenfolge auswählen können, um die neue Ziffer zu erhalten, und der Rest wird der Reihe nach mit der ursprünglichen Zeichenfolge gefüllt.Um dies mit naiver Rekursion in R zu implementieren, habe ich:
Für die Art von Zahlen, die Sie interessieren, hätte ich gedacht, dass es effizienter wäre, Zahlen in einem zweidimensionalen Array zu speichern und über das zunehmende d zu iterieren, aber dies kann von Ihrer Wahl der Sprache abhängen.
quelle
Miffs Antwort ist definitiv elegant. Da ich meine sowieso fast fertig hatte, stelle ich sie trotzdem zur Verfügung. Das Gute ist, dass ich für n = 500 das gleiche Ergebnis bekomme :-)
Sei d die Anzahl der verschiedenen Zeichen, die erlaubt sind, d = 4 in Ihrem Fall.
Sei n die Länge des Strings, letztendlich sehen Sie gerade Werte von n.
Sei u die Anzahl der ungepaarten Zeichen in einer Zeichenfolge.
Sei N (n, d, u) die Anzahl der Zeichenketten der Länge n, die aus d verschiedenen Zeichen aufgebaut sind und u ungepaarte Zeichen haben. Versuchen wir, N zu berechnen.
Es gibt einige Eckfälle zu beobachten:
u> d oder u> n => N = 0
u <0 => N = 0
n% 2! = u% 2 => N = 0.
Wenn Sie von n nach n + 1 gehen, muss u entweder um 1 zunehmen oder um 1 abnehmen, sodass wir eine Rekursion gemäß haben
N (n, d, u) = f (N (n-1, d, u-1), N (n-1, d, u + 1))
Wie viele Möglichkeiten gibt es, dich um eins zu reduzieren? Dieser ist einfach, weil wir einen der u ungepaarten Charaktere koppeln müssen, was es nur zu u macht. Der 2. Teil von f lautet also (u + 1) * N (n-1, d, u + 1), mit der Einschränkung, dass wir N = 0 beachten müssen, wenn u + 1> n-1 oder u +1> d.
Sobald wir dies verstanden haben, ist es leicht zu erkennen, was der erste Teil von f ist: Auf wie viele Arten können wir u erhöhen, wenn es ungepaarte u-1-Zeichen gibt. Nun, wir müssen eines der (k- (u-1)) Zeichen auswählen, die gepaart sind.
Unter Berücksichtigung aller Eckfälle lautet die rekursive Formel für N.
N (n, d, u) = (d- (u-1)) · N (n-1, d, u-1) + (u + 1) · N (n-1, d, u + 1)
Ich werde in http://en.wikipedia.org/wiki/Concrete_Mathematics nicht nachlesen, wie man die Rekursion löst.
Stattdessen habe ich Java-Code geschrieben. Wieder etwas ungeschickter, da Java aufgrund seiner Ausführlichkeit sowieso ist. Aber ich hatte die Motivation, keine Rekursion zu verwenden, da sie zumindest in Java viel zu früh bricht, wenn der Stapel bei 500 oder 1000 Verschachtelungsebenen überläuft.
Das Ergebnis für n = 500, d = 4 und u = 0 ist:
N (500, 4, 0) = 1339385758982834151185531311325002263201756014631917009304687985462938813906170153116497973519619822659493341146941433531483931607115392554498072196838958545795769042788035468026048125208904713757765805163872455056995809556627183222337328039422584942896842901774597806462162357229520744881314972303360
berechnet in 0,2 Sekunden aufgrund des Speicherns von Zwischenergebnissen. N (40000,4,0) berechnet in weniger als 5 Sekunden. Code auch hier: http://ideone.com/KvB5Jv
quelle
Ich habe versucht, eine Lösung zu finden, bin jedoch gescheitert und habe dieselbe Frage zu Mathematics.StackExchange gestellt . Dank Rus May gibt es hier eine Lösung in Common Lisp:
Dies gibt immer 0 für ungerade Werte von zurück
n
. Dennn = 500
hier ist die Ausgabe mit SBCL :quelle