Étant donné un entier 2n, trouvez le nombre de façons possibles dont 2n ^ 2 pions noirs et 2n ^ 2 pions blancs peuvent être disposés sur un échiquier de 2n par 2n de sorte qu'aucun pion n'attaque un autre.
- Un pion noir ne peut attaquer qu'un pion blanc et vice versa.
- Les règles d'échecs habituelles d'attaque suivent, c'est-à-dire que les pions blancs attaquent les carrés immédiatement en diagonale devant, et les pions noirs attaquent les carrés immédiatement en diagonale vers l'arrière (comme vu par l'observateur blanc).
- Toute rotation, les réflexions comptent comme distinctes.
Le programme qui peut sortir toutes les configurations possibles pour la valeur la plus élevée de 2n en 120 secondes l'emporte. (Cependant, tous les programmes sont les bienvenus)
Par exemple, le programme d'Alice peut gérer jusqu'à n = 16 dans les 120 secondes tandis que Bob peut gérer jusqu'à n = 20 dans le même temps. Bob gagne.
Plateforme: Linux 2,7 GHz @ 4 processeurs
fastest-code
combinatorics
chess
Agnishom Chattopadhyay
la source
la source
2n^2
pions, est-ce que c'est(2n)^2
ou2(n^2)
?Réponses:
Java, n = 87 sur ma machine
Le résultat pour n = 87 est
Ceci utilise actuellement un schéma de programmation dynamique prenant des opérations O (n ^ 4) pour calculer les façons de placer des
p
pions sur les carrés d'une couleur pour0 <= p <= n^2
. Je pense qu'il devrait être possible de le faire beaucoup plus efficacement.Vérifie les résultats ici.
Explication
Dans une solution valide, les pions blancs les plus bas de chaque colonne doivent former une ligne en zigzag comme ceci:
Autrement dit, la hauteur de la ligne dans la colonne c doit être de +/- 1 par rapport à sa position dans la colonne c - 1 . La ligne peut également aller sur deux rangées imaginaires au-dessus du haut de la planche.
Nous pouvons utiliser la programmation dynamique pour trouver le nombre de façons de tracer une ligne sur les premières c colonnes qui comprend p pions sur ces colonnes, est à la hauteur h sur la c e colonne, en utilisant les résultats de la colonne c - 1 , hauteurs h + / - 1 , et nombre de pions p - h .
la source
Java
Actuellement, mon code est très long et fastidieux, je travaille à le rendre plus rapide. J'utilise une méthode récursive afin de trouver les valeurs. Il calcule les 5 premiers en 2 ou 3 secondes, mais il devient beaucoup plus lent par la suite. De plus, je ne suis pas encore sûr que les chiffres soient corrects, mais les premiers semblent correspondre aux commentaires. Toutes suggestions sont les bienvenues.
Production
Explication
L'idée de base est la récursivité. Essentiellement, vous commencez avec un tableau vide, un tableau avec tous les zéros. La méthode récursive vérifie simplement si elle peut mettre un pion noir ou blanc dans la position suivante, si elle ne peut mettre qu'une seule couleur, elle le place là et s'appelle. S'il peut mettre les deux couleurs, il s'appelle deux fois, un avec chaque couleur. Chaque fois qu'il s'appelle, il diminue les carrés à gauche et la couleur appropriée à gauche. Quand il a rempli le plateau entier, il retourne le compte actuel + 1. S'il découvre qu'il n'y a aucun moyen de mettre un pion noir ou blanc dans la position suivante, il retourne 0, ce qui signifie que c'est un chemin mort.
Code
Essayez-le ici (ne fonctionne pas assez rapidement pour Ideone donc la dernière valeur ne s'imprime pas, ma solution n'est pas très bonne!)
la source
C ++ avec pthreads, n =
147156Le dernier résultat est d'exécuter le même code que précédemment sur une machine plus robuste. Celui-ci était désormais exécuté sur un ordinateur de bureau équipé d'un quadricœur i7 (Core i7-4770), qui atteignait n = 156 en 120 secondes. Le résultat est:
Cela utilise un algorithme de programmation dynamique. J'ai d'abord réfléchi aux approches où le résultat serait construit ligne par ligne, mais je n'ai jamais pu trouver un moyen d'étendre la solution sans suivre une tonne d'état.
Les informations clés qui ont permis une solution raisonnablement efficace étaient les suivantes:
Si vous regardez une diagonale d'une configuration valide, elle consiste toujours en une séquence de pions noirs suivie d'une séquence de pions blancs (où l'une ou l'autre séquence peut également être vide). En d'autres termes, chaque diagonale peut être entièrement caractérisée par son nombre de pions noirs.
Par conséquent, l'état suivi pour chaque diagonale est le nombre de configurations de pions valides pour chaque combinaison de:
Lorsque vous passez d'une diagonale à la suivante, il existe une autre contrainte pour construire des solutions valides: la position qui sépare les pions noirs des pions blancs ne peut pas augmenter. Le nombre de configurations valides est donc calculé comme la somme des configurations valides de la diagonale précédente pour des positions égales ou supérieures.
L'étape DP de base est alors très simple. Chaque valeur dans une diagonale est juste une somme de valeurs de la diagonale précédente. La seule partie quelque peu pénible consiste à calculer correctement les indices et les plages de boucles. Puisque nous travaillons sur des diagonales, la longueur augmente pendant la première moitié du calcul et diminue pour la seconde moitié, ce qui rend le calcul des plages de boucle plus lourd. Il y a aussi quelques considérations pour les valeurs à la limite de la carte, car elles n'ont que des voisins diagonaux d'un côté lors du passage de la diagonale à la diagonale.
La quantité de mémoire utilisée est O (n ^ 3). Je garde deux copies des données d'état et je fais du ping-pong entre elles. Je pense qu'il serait possible de fonctionner avec une seule instance des données d'état. Mais vous devez faire très attention à ce qu'aucune valeur ne soit mise à jour avant que les anciennes valeurs ne soient entièrement consommées. De plus, cela ne fonctionnerait pas bien pour le traitement parallèle que j'ai introduit.
La complexité d'exécution est ... polynomiale. Il y a 4 boucles imbriquées dans l'algorithme, donc à première vue il ressemblerait à O (n ^ 4). Mais vous avez évidemment besoin de bigints à ces tailles, et les chiffres eux-mêmes s'allongent également à des tailles plus grandes. Le nombre de chiffres dans le résultat semble augmenter à peu près proportionnellement à n, ce qui rendrait le tout O (n ^ 5). D'un autre côté, j'ai trouvé des améliorations de performances, ce qui évite de parcourir toute la gamme de toutes les boucles.
Ainsi, bien qu'il s'agisse encore d'un algorithme assez coûteux, il va beaucoup plus loin que les algorithmes qui énumèrent les solutions, qui sont tous exponentiels.
Quelques notes sur la mise en œuvre:
Code d'algorithme principal.
THREADS
contrôle le nombre de threads utilisés, où le nombre de cœurs de processeur doit être un point de départ raisonnable:Cela nécessite également une classe bigint que j'ai écrite à cet effet. Notez que ce n'est pas une classe bigint à usage général. Il fait juste assez pour prendre en charge les opérations utilisées par cet algorithme spécifique:
la source
Fantom
Voici un premier article qui définit le cadre. Je pense que la procédure est relativement bonne, mais la mise en œuvre en ce moment est un peu nul. Je dois probablement essayer de minimiser le nombre de calculs que je fais, et passer à la place plus de constantes.
Stratégie
Fondamentalement, chaque pion blanc doit attaquer d'autres pions blancs. Je commence donc par placer un pion blanc, en plaçant des pions à chaque endroit qu'il attaque, et en remplissant essentiellement le plateau avec tous les endroits où un pion blanc doit aller. Je m'arrête si j'ai déjà ajouté trop de pions blancs. Si, à la fin de cela, j'ai exactement 2n ^ 2 pions, c'est une solution. Si moins que cela, ajoutez un autre pion blanc quelque part, remplissez toutes ses places requises et comptez à nouveau. Je divise récursivement chaque fois qu'un remplissage avec moins de 2n ^ 2 est trouvé, et calcule le nombre de solutions avec et sans le dernier pion que j'ai ajouté.
Code
Production
Il ne fait que 5 pour le moment, mais je pense que la plupart du problème est en cours de mise en œuvre.
Tester
la source