Je frappe mon crâne à ce problème depuis un certain temps maintenant, et ça commence vraiment à me frustrer. Le problème est:
J'ai un ensemble de caractères, A
, B
, C
et D
. Je dois dire de combien de façons une chaîne peut être construite à partir de ces caractères, quand la longueur est n
et que chaque caractère doit se produire même des fois.
Par exemple, la réponse n = 2
est 4:
AA
BB
CC
DD
La réponse pour n = 4
est 40. Certaines de ces chaînes valides sont:
AAAA
AABB
CACA
DAAD
BCCB
Je suis coincé à trouver une logique. J'ai l'impression qu'il pourrait y avoir une solution DP pour cela. Il est hors de question de forcer brutalement mon chemin: le nombre de solutions croît rapidement en très grand nombre.
J'ai essayé de dessiner toutes sortes d'idées sur papier, en vain. Presque toutes ces idées que j'ai dû abandonner car leur complexité était trop grande. La solution doit être efficace pour n = 10^4
.
L'une de mes idées était de ne jamais garder une trace des chaînes réelles, mais seulement de savoir si chaque personnage était apparu à des moments pairs ou impairs. Je ne pouvais pas trouver un moyen d'appliquer cette logique.
Quelqu'un peut-il m'aider?
la source
Réponses:
Configuré
f(n,d)
comme une fonction donnant le nombre de permutations de longueur (paire) enn
utilisantd
des caractères distincts (c'est-d=4
à- dire dans votre cas).En clair
f(0,d) = 1
etf(n,1) = 1
comme il n'y a qu'un seul arrangement quand vous n'avez qu'un seul caractère, ou zéro espace.Maintenant, l'étape d'induction:
Pour créer une chaîne valide à l'aide de
d
caractères, prenez une chaîne de longueur paire plus courte à l'aide ded-1
caractères et complétez-la en ajoutant un multiple pair de ce nouveau caractère. Le nombre d'arrangements estchoose(n,n_newdigits)
dû au fait que vous pouvez choisir desn_newdigit
emplacements dans la longueur totale de la chaîne pour avoir le nouveau chiffre, et le reste est rempli par la chaîne d'origine dans l'ordre.Pour implémenter cela en utilisant la récursivité naïve dans R, j'ai fait:
Pour le type de nombres qui vous intéresse, j'aurais pensé qu'il serait plus efficace de stocker des nombres dans un tableau à deux dimensions et d'itérer sur l'augmentation de d, mais cela peut dépendre de votre choix de langue.
la source
La réponse de Miff est définitivement élégante. Comme j'avais presque fini le mien, je le fournis quand même. La bonne chose est que j'obtiens le même résultat pour n = 500 :-)
Soit d le nombre de caractères différents autorisés, d = 4 dans votre cas.
Soit n la longueur de la chaîne, en fin de compte, vous regarderez les valeurs paires de n.
Soit u le nombre de caractères non appariés dans une chaîne.
Soit N (n, d, u) le nombre de chaînes de longueur n, construites à partir de d caractères différents et ayant u caractères non appariés. Essayons de calculer N.
Il y a quelques cas d'angle à observer:
u> d ou u> n => N = 0
u <0 => N = 0
n% 2! = u% 2 => N = 0.
Lorsque vous passez de n à n + 1, u doit soit augmenter de 1, soit diminuer de 1, nous avons donc une récursion selon
N (n, d, u) = f (N (n-1, d, u-1), N (n-1, d, u + 1))
Combien de façons existe-t-il de réduire u d'une unité. Celui-ci est facile, car nous devons associer l'un des u caractères non appariés, ce qui le rend juste u. Donc, la 2e partie de f se lira (u + 1) * N (n-1, d, u + 1), avec la mise en garde bien sûr que nous devons observer que N = 0 si u + 1> n-1 ou u +1> d.
Une fois que nous avons compris cela, il est facile de voir quelle est la première partie de f: de combien de façons pouvons-nous augmenter u quand il y a u-1 caractères non appariés. Eh bien, nous devons choisir l'un des (k- (u-1)) caractères qui sont appariés.
Donc, en tenant compte de tous les cas d'angle, la formule récursive pour N est
N (n, d, u) = (d- (u-1)) * N (n-1, d, u-1) + (u + 1) * N (n-1, d, u + 1)
Je ne vais pas lire dans http://en.wikipedia.org/wiki/Concrete_Mathematics comment résoudre la récursivité.
Au lieu de cela, j'ai écrit du code Java. Encore un peu plus maladroit, tout comme Java en raison de sa verbosité. Mais j'ai eu la motivation de ne pas utiliser la récursivité, car elle se casse bien au début, du moins en Java, lorsque la pile déborde à 500 ou 1000 niveaux d'imbrication.
Le résultat pour n = 500, d = 4 et u = 0 est:
N (500, 4, 0) = 1339385758982834151185531311325002263201756014631917009304687985462938813906170153116497973519619822659493341146941433531483931607115392554498072196838958545795769042788035468026048125208904713757765805163872455056995809556627183222337328039422584942896842901774597806462162357229520744881314972303360
calculé en 0,2 seconde, en raison de la mémorisation des résultats intermédiaires. N (40000,4,0) calcule en moins de 5 secondes. Code également ici: http://ideone.com/KvB5Jv
la source
J'ai essayé de trouver une solution mais j'ai échoué et posé la même question sur Mathematics.StackExchange . Merci à Rus May , voici une solution, en Common Lisp:
Ce retour toujours 0 pour les valeurs impaires de
n
. Pourn = 500
, voici la sortie avec SBCL :la source