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
import java.math.BigInteger;
public class EvenPairedString2 {
private final int nChars; // d above, number of different chars to use
private int count = 0;
private Map<Task,BigInteger> memo = new HashMap<>();
public EvenPairedString2(int nChars) {
this.nChars = nChars;
}
/*+******************************************************************/
// encodes for a fixed d the task to compute N(strlen,d,unpaired).
private static class Task {
public final int strlen;
public final int unpaired;
Task(int strlen, int unpaired) {
this.strlen = strlen;
this.unpaired = unpaired;
}
@Override
public int hashCode() {
return strlen*117 ^ unpaired;
}
@Override
public boolean equals(Object other) {
if (!(other instanceof Task)) {
return false;
}
Task t2 = (Task)other;
return strlen==t2.strlen && unpaired==t2.unpaired;
}
@Override
public String toString() {
return "("+strlen+","+unpaired+")";
}
}
/*+******************************************************************/
// return corner case or memorized result or null
private BigInteger getMemoed(Task t) {
if (t.strlen==0 || t.unpaired<0 || t.unpaired>t.strlen || t.unpaired>nChars
|| t.strlen%2 != t.unpaired%2) {
return BigInteger.valueOf(0);
}
if (t.strlen==1) {
return BigInteger.valueOf(nChars);
}
return memo.get(t);
}
public int getCount() {
return count;
}
public BigInteger computeNDeep(Task t) {
List<Task> stack = new ArrayList<Task>();
BigInteger result = null;
stack.add(t);
while (stack.size()>0) {
count += 1;
t = stack.remove(stack.size()-1);
result = getMemoed(t);
if (result!=null) {
continue;
}
Task t1 = new Task(t.strlen-1, t.unpaired+1);
BigInteger r1 = getMemoed(t1);
Task t2 = new Task(t.strlen-1, t.unpaired-1);
BigInteger r2 = getMemoed(t2);
if (r1==null) {
stack.add(t);
stack.add(t1);
if (r2==null) {
stack.add(t2);
}
continue;
}
if (r2==null) {
stack.add(t);
stack.add(t2);
continue;
}
result = compute(t1.unpaired, r1, nChars-t2.unpaired, r2);
memo.put(t, result);
}
return result;
}
private BigInteger compute(int u1, BigInteger r1, int u2, BigInteger r2) {
r1 = r1.multiply(BigInteger.valueOf(u1));
r2 = r2.multiply(BigInteger.valueOf(u2));
return r1.add(r2);
}
public static void main(String[] argv) {
int strlen = Integer.parseInt(argv[0]);
int nChars = Integer.parseInt(argv[1]);
EvenPairedString2 eps = new EvenPairedString2(nChars);
BigInteger result = eps.computeNDeep(new Task(strlen, 0));
System.out.printf("%d: N(%d, %d, 0) = %d%n",
eps.getCount(), strlen, nChars,
result);
}
}