Trop de pions sur un échiquier

10

É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

Agnishom Chattopadhyay
la source
2
Quel est le format de sortie?
Poignée de porte
2
Pour test: quelqu'un a-t-il une idée des chiffres impliqués? J'ai trouvé 3 solutions pour 2x2 et 28 solutions pour 4x4
edc65
1
@ edc65, je le fais 3, 30, 410. J'ai vérifié les 3 et 30 par une méthode alternative.
Peter Taylor
1
J'ai eu mon code générer les premiers: 3, 30, 410, 6148, 96120, 1526700. Bien que, je n'ai aucun moyen de vérifier. Quelqu'un obtient-il la même chose?
cmxu
1
Pour clarifier la priorité des opérateurs, lorsque vous dites 2n^2pions, est-ce que c'est (2n)^2ou 2(n^2)?
Reto Koradi

Réponses:

9

Java, n = 87 sur ma machine

Le résultat pour n = 87 est

62688341832480765224168252369740581641682638216282495398959252035334029997073369148728772291668336432168


import java.math.BigInteger;

public class NonattackingPawns {

    static BigInteger count(int n) {
        BigInteger[][] a0 = new BigInteger[n+1][n*n+1], a1 = new BigInteger[n+1][n*n+1], tm;

        for(int h = 0; h <= n; h++) a0[h][0] = h%2==0? BigInteger.ONE: BigInteger.ZERO;

        for(int c = 1; c <= 2*n; c++) {     
            int minp = 0;
            for(int h = 0; h <= n; h++) {
                java.util.Arrays.fill(a1[h], BigInteger.ZERO);
                if(h>0) minp += c >= 2*h-c%2 ? 2*h - c%2 : c;

                int maxp = Math.min(n*(c-1)+h, n*n);
                for(int p = minp; p <= maxp; p++) {
                    BigInteger sum = a0[h][p-h];

                    if(c%2==1 && h>0) 
                        sum = sum.add(a0[h-1][p-h]);
                    else if(c%2==0 && h<n) 
                        sum = sum.add(a0[h+1][p-h]);

                    a1[h][p] = sum;
                }
            }
            tm=a0; a0=a1; a1=tm;
        }
        BigInteger[] s = new BigInteger[n*n+1];
        for(int p = 0; p <= n*n; p++) {
            BigInteger sum = BigInteger.ZERO;
            for(int h = 0; h <= n; h++) sum = sum.add(a0[h][p]);
            s[p] = sum;

        }

        BigInteger ans = BigInteger.ZERO;
        for(int p = 0; p < n*n; p++) ans = ans.add(s[p].multiply(s[p]));
        return ans.shiftLeft(1).add(s[n*n].multiply(s[n*n]));
    }

    public static void main(String[] args) {
        for(int n = 0;; n++) {
            System.out.println(n + " " + count(n));
        }
    }

}

Ceci utilise actuellement un schéma de programmation dynamique prenant des opérations O (n ^ 4) pour calculer les façons de placer des ppions sur les carrés d'une couleur pour 0 <= 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:

ligne de pion

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 .

feersum
la source
Pouvez-vous partager le nombre pour n = 87? Ou au moins l'ordre de grandeur? Cela doit être un très grand nombre ...
Reto Koradi
Je suis un peu confus sur ce que vous faites ici, mais c'est très impressionnant!
cmxu
Je pense que je comprends l'essentiel de votre explication, sauf quand vous dites "La ligne peut également aller sur deux rangées imaginaires au-dessus du haut du tableau"
cmxu
@Changming, cela signifie seulement qu'il n'y a pas de pions dans cette colonne.
feersum
@feersum Je vois que cela a plus de sens, je vais voir si je peux travailler à travers la logique et voir si je peux trouver un moyen de l'implémenter encore plus rapidement.
cmxu
5

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

2x2:    3
4x4:    30
6x6:    410
8x8:    6148
10x10:  96120

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

public class Chess {
    public static void main(String[] args){
        System.out.println(solve(1));
        System.out.println(solve(2));
        System.out.println(solve(3));
        System.out.println(solve(4));
        System.out.println(solve(5));
    }
    static int solve(int n){
        int m =2*n;
        int[][] b = new int[m][m];
        for(int i = 0; i < m; i++){
            for(int j = 0; j < m; j++){
                b[i][j]=0;
            }
        }
        return count(m,m*m,m*m/2,m*m/2,0,b);
    }
    static int count(int n,int sqLeft, int bLeft, int wLeft, int count, int[][] b){
        if(sqLeft == 0){
            /*for(int i = 0; i < n; i++){
                for(int j = 0; j < n; j++){
                    System.out.print(b[i][j]);
                }
                System.out.println();
            }
            System.out.println();*/
            return count+1;
        }
        int x=(sqLeft-1)%n;
        int y=(sqLeft-1)/n;
        if(wLeft==0){
            if(y!=0){
                if ((x==0?true:b[x-1][y-1]!=1)&&(x==n-1?true:b[x+1][y-1]!= 1)) {
                    b[x][y] = 2;
                    return count(n, sqLeft-1, bLeft-1, wLeft, count, b);
                } else {
                    return 0;
                }
            } else {
                b[x][y]=2;
                return count(n,sqLeft-1,bLeft-1,wLeft,count,b);
            }
        } else if(bLeft==0){
            if(y!=n-1){
                if((x==0?true:b[x-1][y+1]!=2)&&(x==n-1?true:b[x+1][y+1]!=2)){
                    b[x][y]=1;
                    return count(n,sqLeft-1,bLeft,wLeft-1,count,b);
                } else {
                    return 0;
                }
            } else {
                b[x][y]=1;
                return count(n,sqLeft-1,bLeft,wLeft-1,count,b);
            }
        } else{
            if(y==0){
                if((x==0?true:b[x-1][y+1]!=2)&&(x==n-1?true:b[x+1][y+1]!=2)){
                    int[][] c=new int[n][n];
                    for(int i = 0; i < n; i++){
                        System.arraycopy(b[i], 0, c[i], 0, n);
                    }
                    b[x][y]=2;
                    c[x][y]=1;
                    return count(n,sqLeft-1,bLeft,wLeft-1,count,c)+count(n,sqLeft-1,bLeft-1,wLeft,count,b);
                } else {
                    b[x][y]=2;
                    return count(n,sqLeft-1,bLeft-1,wLeft,count,b);
                }
            }else if(y==n-1){
                if((x==0?true:b[x-1][y-1]!=1)&&(x==n-1?true:b[x+1][y-1]!=1)){
                    int[][] c=new int[n][n];
                    for(int i = 0; i < n; i++){
                        System.arraycopy(b[i], 0, c[i], 0, n);
                    }
                    b[x][y]=2;
                    c[x][y]=1;
                    return count(n,sqLeft-1,bLeft,wLeft-1,count,c)+count(n,sqLeft-1,bLeft-1,wLeft,count,b);
                } else {
                    b[x][y]=1;
                    return count(n,sqLeft-1,bLeft,wLeft-1,count,b);
                }
            }else{
                if(((x==0?true:b[x-1][y-1]!=1)&&(x==n-1?true:b[x+1][y-1]!=1))&&((x==0?true:b[x-1][y+1]!=2)&&(x==n-1?true:b[x+1][y+1]!=2))){
                    int[][] c=new int[n][n];
                    for(int i = 0; i < n; i++){
                        System.arraycopy(b[i], 0, c[i], 0, n);
                    }
                    b[x][y]=2;
                    c[x][y]=1;
                    return count(n,sqLeft-1,bLeft,wLeft-1,count,c)+count(n,sqLeft-1,bLeft-1,wLeft,count,b);
                } else if ((x==0?true:b[x-1][y-1]!=1)&&(x==n-1?true:b[x+1][y-1]!=1)){
                    b[x][y]=2;
                    return count(n,sqLeft-1,bLeft-1,wLeft,count,b);
                } else if ((x==0?true:b[x-1][y+1]!=2)&&(x==n-1?true:b[x+1][y+1]!=2)){
                    b[x][y]=1;
                    return count(n,sqLeft-1,bLeft,wLeft-1,count,b);
                } else {
                    return 0;
                }
            }
        }
    }
}

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!)

cmxu
la source
Je suis d'accord jusqu'à 6148, et je n'ai pas encore produit de valeurs au-delà.
Peter Taylor
@PeterTaylor Eh bien Agnishom dit que ce devrait être 3, 28, 408, donc je doute que 6148 ait raison. Je me demande ce que nous faisons tous les deux mal?
cmxu
Bien plus rapide que le mien. +1 même si je ne suis pas d'accord sur les résultats
edc65
salut! Je n'ai jamais dit que c'était 28, 408 La séquence correcte est 3,30,410, ...
Agnishom Chattopadhyay
Vous avez dit, @ edc65 avait les bonnes valeurs, et ses valeurs sont 28, 408?
cmxu
4

C ++ avec pthreads, n = 147 156

Le 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:

7858103688882482349696225090648142317093426691269441606893544257091315906431773702676266198643058148987365151560565922891852481847049321541347582728793175114543240164406674

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:

  • Étant donné que les pions sur les carrés noirs ne peuvent attaquer que les pions sur d'autres carrés noirs, et il en va de même pour les carrés blancs, les carrés noirs et blancs sont indépendants et peuvent être traités séparément. Et comme ils sont équivalents, il suffit de traiter l'un des deux.
  • Le problème devient beaucoup plus facile lors du traitement de la carte diagonale par diagonale.

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:

  • Nombre de pions noirs dans la rangée (ou en d'autres termes, la position dans la diagonale qui sépare les pions noirs des pions blancs).
  • Nombre total de pions noirs utilisés. Nous devons suivre le tout par nombre de pions, car nous n'avons besoin que du nombre égal de pions noirs et de pions blancs à la fin. Lors du traitement des diagonales, les décomptes peuvent être différents et aboutir à des solutions valides au final.

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:

  • Bien qu'il puisse y avoir jusqu'à 2 * n ^ 2 pions noirs sur les carrés noirs, je ne calcule que les nombres de configuration jusqu'à n ^ 2 pions noirs. Puisqu'il y a une symétrie entre les pions noirs et blancs, le nombre de configurations pour k et 2 * n ^ 2-k est le même.
  • Le nombre de solutions est calculé à la fin à partir des décomptes de configuration sur les carrés noirs sur la base d'une symétrie similaire. Le nombre total de solutions (qui doivent avoir 2 * n ^ 2 pions de chaque couleur) est le nombre de configurations pour k pions noirs sur une couleur de carrés multiplié par le nombre de configurations pour 2 * n ^ 2-k pions noirs sur l'autre couleur des carrés, sommée sur tout k.
  • En plus de stocker uniquement le nombre de configurations par position diagonale et le nombre de pions, je stocke également la plage de nombres de pions qui ont des configurations valides par position. Cela permet de réduire sensiblement la portée de la boucle intérieure. Sans cela, j'ai constaté que beaucoup de zéros étaient ajoutés. J'ai obtenu une amélioration des performances très substantielle de cela.
  • L'algorithme se parallélise assez bien, en particulier pour les grandes tailles. Les diagonales doivent être séquentielles, il y a donc une barrière à la fin de chaque diagonale. Mais les positions à l'intérieur de la diagonale peuvent être traitées en parallèle.
  • Le profilage montre que le goulot d'étranglement réside clairement dans l'ajout de valeurs bigint. J'ai joué avec quelques variantes du code, mais il n'est pas fortement optimisé. Je soupçonne qu'il pourrait y avoir une amélioration significative de l'assemblage en ligne pour utiliser des ajouts 64 bits avec carry.

Code d'algorithme principal. THREADScontrôle le nombre de threads utilisés, où le nombre de cœurs de processeur doit être un point de départ raisonnable:

#ifndef THREADS
#define THREADS 2
#endif

#if THREADS > 1
#include <pthread.h>
#endif

#include <vector>
#include <iostream>
#include <sstream>

#include "BigUint.h"

typedef std::vector<BigUint> BigUintVec;
typedef std::vector<int> IntVec;

static int N;
static int NPawn;
static int NPos;

static BigUintVec PawnC[2];
static IntVec PawnMinC[2];
static IntVec PawnMaxC[2];

#if THREADS > 1
static pthread_mutex_t ThreadMutex;
static pthread_cond_t ThreadCond;
static int BarrierCount;
#endif

#if THREADS > 1
static void ThreadBarrier()
{
    pthread_mutex_lock(&ThreadMutex);

    --BarrierCount;
    if (BarrierCount)
    {
        pthread_cond_wait(&ThreadCond, &ThreadMutex);
    }
    else
    {
        pthread_cond_broadcast(&ThreadCond);
        BarrierCount = THREADS;
    }

    pthread_mutex_unlock(&ThreadMutex);
}
#endif

static void* countThread(void* pData)
{
    int* pThreadIdx = static_cast<int*>(pData);
    int threadIdx = *pThreadIdx;

    int prevDiagMin = N - 1;
    int prevDiagMax = N;

    for (int iDiag = 1; iDiag < 2 * N; ++iDiag)
    {
        BigUintVec& rSrcC = PawnC[1 - iDiag % 2];
        BigUintVec& rDstC = PawnC[iDiag % 2];

        IntVec& rSrcMinC = PawnMinC[1 - iDiag % 2];
        IntVec& rDstMinC = PawnMinC[iDiag % 2];

        IntVec& rSrcMaxC = PawnMaxC[1 - iDiag % 2];
        IntVec& rDstMaxC = PawnMaxC[iDiag % 2];

        int diagMin = prevDiagMin;
        int diagMax = prevDiagMax;;
        if (iDiag < N)
        {
            --diagMin;
            ++diagMax;
        }
        else if (iDiag > N)
        {
            ++diagMin;
            --diagMax;
        }

        int iLastPos = diagMax;
        if (prevDiagMax < diagMax)
        {
            iLastPos = prevDiagMax;
        }

        for (int iPos = diagMin + threadIdx; iPos <= iLastPos; iPos += THREADS)
        {
            int nAdd = iPos - diagMin;

            for (int iPawn = nAdd; iPawn < NPawn; ++iPawn)
            {
                rDstC[iPos * NPawn + iPawn] = 0;
            }

            rDstMinC[iPos] = NPawn;
            rDstMaxC[iPos] = -1;

            int iFirstPrevPos = iPos;
            if (!nAdd)
            {
                iFirstPrevPos = prevDiagMin;
            }

            for (int iPrevPos = iFirstPrevPos;
                 iPrevPos <= prevDiagMax; ++iPrevPos)
            {
                int iLastPawn = rSrcMaxC[iPrevPos];
                if (iLastPawn + nAdd >= NPawn)
                {
                    iLastPawn = NPawn - 1 - nAdd;
                }

                if (rSrcMinC[iPrevPos] > iLastPawn)
                {
                    continue;
                }

                if (rSrcMinC[iPrevPos] < rDstMinC[iPos])
                {
                    rDstMinC[iPos] = rSrcMinC[iPrevPos];
                }

                if (iLastPawn > rDstMaxC[iPos])
                {
                    rDstMaxC[iPos] = iLastPawn;
                }

                for (int iPawn = rSrcMinC[iPrevPos];
                     iPawn <= iLastPawn; ++iPawn)
                {
                    rDstC[iPos * NPawn + iPawn + nAdd] += rSrcC[iPrevPos * NPawn + iPawn];
                }
            }

            if (rDstMinC[iPos] <= rDstMaxC[iPos])
            {
                rDstMinC[iPos] += nAdd;
                rDstMaxC[iPos] += nAdd;
            }
        }

        if (threadIdx == THREADS - 1 && diagMax > prevDiagMax)
        {
            int pawnFull = (iDiag + 1) * (iDiag + 1);
            rDstC[diagMax * NPawn + pawnFull] = 1;
            rDstMinC[diagMax] = pawnFull;
            rDstMaxC[diagMax] = pawnFull;
        }

        prevDiagMin = diagMin;
        prevDiagMax = diagMax;

#if THREADS > 1
        ThreadBarrier();
#endif
    }

    return 0;
}

static void countPawns(BigUint& rRes)
{
    NPawn = N * N + 1;
    NPos = 2 * N;

    PawnC[0].resize(NPos * NPawn);
    PawnC[1].resize(NPos * NPawn);

    PawnMinC[0].assign(NPos, NPawn);
    PawnMinC[1].assign(NPos, NPawn);

    PawnMaxC[0].assign(NPos, -1);
    PawnMaxC[1].assign(NPos, -1);

    PawnC[0][(N - 1) * NPawn + 0] = 1;
    PawnMinC[0][N - 1] = 0;
    PawnMaxC[0][N - 1] = 0;

    PawnC[0][N * NPawn + 1] = 1;
    PawnMinC[0][N] = 1;
    PawnMaxC[0][N] = 1;

#if THREADS > 1
    pthread_mutex_init(&ThreadMutex, 0);
    pthread_cond_init(&ThreadCond, 0);

    BarrierCount = THREADS;

    int threadIdxA[THREADS] = {0};
    pthread_t threadA[THREADS] = {0};
    for (int iThread = 0; iThread < THREADS; ++iThread)
    {
        threadIdxA[iThread] = iThread;
        pthread_create(threadA + iThread, 0, countThread, threadIdxA + iThread);
    }

    for (int iThread = 0; iThread < THREADS; ++iThread)
    {
        pthread_join(threadA[iThread], 0);
    }

    pthread_cond_destroy(&ThreadCond);
    pthread_mutex_destroy(&ThreadMutex);
#else
    int threadIdx = 0;
    countThread(&threadIdx);
#endif

    BigUint solCount;
    BigUintVec& rResC = PawnC[1];
    for (int iPawn = 0; iPawn < NPawn; ++iPawn)
    {
        BigUint nComb = rResC[(N - 1) * NPawn + iPawn];

        nComb *= nComb;
        if (iPawn < NPawn - 1)
        {
            nComb *= 2;
        }

        solCount += nComb;
    }

    std::string solStr;
    solCount.toDecString(solStr);
    std::cout << solStr << std::endl;
}

int main(int argc, char* argv[])
{
    std::istringstream strm(argv[1]);
    strm >> N;

    BigUint res;
    countPawns(res);

    return 0;
}

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:

#ifndef BIG_UINT_H
#define BIG_UINT_H

#include <cstdint>
#include <string>
#include <vector>

class BigUint
{
public:
    BigUint()
      : m_size(1),
        m_cap(MIN_CAP),
        m_valA(m_fixedValA)
    {
        m_valA[0] = 0;
    }

    BigUint(uint32_t val)
      : m_size(1),
        m_cap(MIN_CAP),
        m_valA(m_fixedValA)
    {
        m_valA[0] = val;
    }

    BigUint(const BigUint& rhs)
      : m_size(rhs.m_size),
        m_cap(MIN_CAP),
        m_valA(m_fixedValA)
    {
        if (m_size > MIN_CAP)
        {
            m_cap = m_size;
            m_valA = new uint32_t[m_cap];
        }

        for (int iVal = 0; iVal < m_size; ++iVal)
        {
            m_valA[iVal] = rhs.m_valA[iVal];
        }
    }

    ~BigUint()
    {
        if (m_cap > MIN_CAP)
        {
            delete[] m_valA;
        }
    }

    BigUint& operator=(uint32_t val)
    {
        m_size = 1;
        m_valA[0] = val;

        return *this;
    }

    BigUint& operator=(const BigUint& rhs)
    {
        if (rhs.m_size > m_cap)
        {
            if (m_cap > MIN_CAP)
            {
                delete[] m_valA;
            }

            m_cap = rhs.m_size;
            m_valA = new uint32_t[m_cap];
        }

        m_size = rhs.m_size;

        for (int iVal = 0; iVal < m_size; ++iVal)
        {
            m_valA[iVal] = rhs.m_valA[iVal];
        }

        return *this;
    }

    BigUint& operator+=(const BigUint& rhs)
    {
        if (rhs.m_size > m_size)
        {
            resize(rhs.m_size);
        }

        uint64_t sum = 0;
        for (int iVal = 0; iVal < m_size; ++iVal)
        {
            sum += m_valA[iVal];
            if (iVal < rhs.m_size)
            {
                sum += rhs.m_valA[iVal];
            }
            m_valA[iVal] = sum;
            sum >>= 32u;
        }

        if (sum)
        {
            resize(m_size + 1);
            m_valA[m_size - 1] = sum;
        }

        return *this;
    }

    BigUint& operator*=(const BigUint& rhs)
    {
        int resSize = m_size + rhs.m_size - 1;
        uint32_t* resValA = new uint32_t[resSize];

        uint64_t sum = 0;

        for (int iResVal = 0; iResVal < resSize; ++iResVal)
        {
            uint64_t carry = 0;

            for (int iLhsVal = 0;
                 iLhsVal <= iResVal && iLhsVal < m_size; ++iLhsVal)
            {
                int iRhsVal = iResVal - iLhsVal;
                if (iRhsVal < rhs.m_size)
                {
                    uint64_t prod = m_valA[iLhsVal];
                    prod *= rhs.m_valA[iRhsVal];
                    uint64_t newSum = sum + prod;
                    if (newSum < sum)
                    {
                        ++carry;
                    }
                    sum = newSum;
                }
            }

            resValA[iResVal] = sum & UINT64_C(0xFFFFFFFF);
            sum >>= 32u;
            sum += carry << 32u;
        }

        if (resSize > m_cap)
        {
            if (m_cap > MIN_CAP)
            {
                delete[] m_valA;
            }

            m_cap = resSize;
            m_valA = resValA;
        }
        else
        {
            for (int iVal = 0; iVal < resSize; ++iVal)
            {
                m_valA[iVal] = resValA[iVal];
            }

            delete[] resValA;
        }

        m_size = resSize;

        if (sum)
        {
            resize(m_size + 1);
            m_valA[m_size - 1] = sum;
        }

        return *this;
    }

    void divMod(uint32_t rhs, uint32_t& rMod)
    {
        uint64_t div = 0;
        for (int iVal = m_size - 1; iVal >= 0; --iVal)
        {
            div <<= 32u;
            div += m_valA[iVal];

            uint64_t val = div / rhs;
            div -= val * rhs;

            if (val || iVal == 0 || iVal < m_size - 1)
            {
                m_valA[iVal] = val;
            }
            else
            {
                --m_size;
            }
        }

        rMod = div;
    }

    void toDecString(std::string& rStr) const
    {
        std::vector<char> digits;

        BigUint rem(*this);
        while (rem.m_size > 1 || rem.m_valA[0])
        {
            uint32_t digit = 0;
            rem.divMod(10, digit);
            digits.push_back(digit);
        }

        if (digits.empty())
        {
            rStr = "0";
        }
        else
        {
            rStr.clear();
            rStr.reserve(digits.size());

            for (int iDigit = digits.size() - 1; iDigit >= 0; --iDigit)
            {
                rStr.append(1, '0' + digits[iDigit]);
            }
        }
    }

private:
    static const int MIN_CAP = 8;

    void resize(int newSize)
    {
        if (newSize > m_cap)
        {
            uint32_t* newValA = new uint32_t[newSize];

            for (int iVal = 0; iVal < m_size; ++iVal)
            {
                newValA[iVal] = m_valA[iVal];
            }

            if (m_cap > MIN_CAP)
            {
                delete[] m_valA;
            }

            m_cap = newSize;
            m_valA = newValA;
        }

        for (int iVal = m_size; iVal < newSize; ++iVal)
        {
            m_valA[iVal] = 0;
        }

        m_size = newSize;
    }

    int m_size;
    int m_cap;

    uint32_t* m_valA;
    uint32_t m_fixedValA[MIN_CAP];
};

#endif // BIG_UINT_H
Reto Koradi
la source
0

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

class main
{
  public  Void main(){

    echo(calculate(1))
    echo(calculate(2))
    echo(calculate(3))
    echo(calculate(4))
    echo(calculate(5))

  }

  public static  Int calculate(Int n){

    n *= 2
    //Initialize the array -  Definitely a weakpoint, but only runs once
    Bool[][] white := [,]
    n.times{ 
      row := [,]
      n.times{ row.add(false) }
      white.add(row)
    }

    return recurse(white, -1, 0, n, n*n/2)
  }

  private static  Int recurse(Bool[][] white, Int lastPlacement, Int numWhites, Int n, Int totalWhite){
    if(totalWhite - numWhites > n*n - 1 - lastPlacement) return 0
    lastPlacement++
    Int row := lastPlacement / n
    Int col := lastPlacement % n
    if(white[row][col]){ return recurse(white, lastPlacement, numWhites, n, totalWhite)}
    Bool[][] whiteCopy := copy(white)
    whiteCopy[row][col] = true
    Int result := fillIn(whiteCopy, numWhites + 1, totalWhite)
    if(result == -1){
      return recurse(white, lastPlacement, numWhites,n, totalWhite);
    }
    else if(result == totalWhite){
      //echo("Found solution")
      //echo("WhiteCopy = $whiteCopy")
      return recurse(white, lastPlacement, numWhites,n, totalWhite) + 1;
    }
    else return recurse(whiteCopy, lastPlacement, result,n, totalWhite) + recurse(white, lastPlacement, numWhites,n, totalWhite)


  }

  //Every white must be attacking other whites, so fill in the grid with all necessary points
  //Stop if number of whites used goes too high
  private static Int fillIn(Bool[][] white, Int count, Int n){
    white[0..-2].eachWhile |Bool[] row, Int rowIndex -> Bool?| {
      return row.eachWhile |Bool isWhite, Int colIndex -> Bool?|{
        if(isWhite){
          //Catching index out of bounds is faster than checking index every time
          try{
            if(colIndex > 0 && !white[rowIndex + 1][colIndex - 1]){
              white[rowIndex + 1][colIndex - 1] = true
              count++
            }
            if(!white[rowIndex + 1][colIndex + 1]){
              white[rowIndex + 1][colIndex + 1] = true
              count++
            }
          } catch {}
        }
        if(count > n){ count = -1; return true}
        return null
      }//End row.each
    }//End white.each
    return count
  }

  private static Bool[][] copy(Bool[][] orig){
    Bool[][] copy := [,]
    orig.each{
      copy.add(it.dup)
    }
    return copy
  }

}

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.

3
30
410
6148
96120

Tester

Caïn
la source
C'est aussi ma stratégie, mais cela semble beaucoup trop lent par rapport aux autres solutions publiées ici.
edc65
@ edc65 Les approches qui énumèrent les solutions n'auront aucune chance. S'il y avait des doutes, le simple nombre produit par l'algorithme de feersum en est la preuve. Une sorte d'algorithme de programmation dynamique qui calcule le nombre de solutions sans les énumérer est la voie à suivre ici.
Reto Koradi