Satranç tahtasında çok fazla piyon


10

2n tamsayısı verildiğinde, 2n ^ 2 siyah piyon ve 2n ^ 2 beyaz piyonun 2n x 2n satranç tahtasında düzenlenebileceği olası yolların sayısını bulun, böylece piyon diğerine saldırmaz.

  • Siyah bir piyon sadece beyaz bir piyona saldırabilir, bunun tersi de geçerlidir.
  • Saldırının olağan satranç kuralları, yani beyaz piyonlar hemen çaprazlama olarak karelere saldırır ve siyah piyonlar kareleri hemen çapraz olarak (beyaz gözlemci tarafından görüldüğü gibi) saldırır.
  • Tüm döndürme, yansımalar ayrı olarak sayılır.

120 saniyede 2n'lik en yüksek değer için tüm olası konfigürasyonları verebilen program kazanır. (Yine de tüm programlar açıktır)

Örneğin Alice'in programı 120 saniye içinde n = 16 ile başa çıkabilirken Bob'lar aynı zamanda n = 20 ile başa çıkabilir. Bob kazanır.

Platform: Linux 2,7 GHz @ 4 CPU


2
Çıktı formatı nedir?
Kapı tokmağı

2
Test için: Birisi ilgili numaralar hakkında herhangi bir fikre sahip mi? 4x4 için 2x2 ve 28 çözüm için 3 çözüm buldum
edc65

1
@ edc65, 3, 30, 410 yapıyorum. 3 ve 30'u alternatif bir yöntemle kontrol ettim.
Peter Taylor

1
Kodumu ilk birkaç oluşturmak vardı: 3, 30, 410, 6148, 96120, 1526700. Bununla birlikte, kontrol etmenin bir yolu yok. Herkes aynı olsun?
cmxu

1
Operatör önceliğini açıklığa kavuşturmak için, 2n^2piyon dediğinizde bu (2n)^2ya da 2(n^2)?
Reto Koradi

Yanıtlar:


9

Java, makinemde n = 87

N = 87 için sonuç

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));
        }
    }

}

Bu halihazırda p, bir rengin karelerine piyon yerleştirme yollarını hesaplamak için O (n ^ 4) işlemlerini alan dinamik bir programlama şeması kullanır 0 <= p <= n^2. Bence bunu çok daha verimli bir şekilde yapmak mümkün olmalı.

Sonuçları buradan kontrol edin.

açıklama

Geçerli bir çözümde, her sütundaki en alt beyaz piyonlar aşağıdaki gibi zikzaklı bir çizgi oluşturmalıdır:

piyon hattı

Yani, c sütunundaki çizginin yüksekliği c - 1 sütunundaki konumundan +/- 1 olmalıdır . Çizgi ayrıca tahtanın üstünden iki hayali sıraya da gidebilir.

İlk bir çizgi çizmek için yollar sayısını bulmak için dinamik programlama kullanabilir C içeren sütun p yüksekliği olan, bu sütunlarda piyon h üzerinde c kolon sonuçları kullanılarak, sütun inci c - 1 , yükseklikleri h + / - 1 ve piyon sayısı p - h .


N = 87'nin numarasını paylaşabilir misiniz? Veya en azından büyüklük sırası? Bu çok büyük bir sayı olmalı ...
Reto Koradi

Burada ne yaptığınız konusunda biraz kafam karıştı, ama çok etkileyici!
cmxu

Sanırım açıklamanızın çoğunu alıyorum, "Çizgi de tahtanın üst kısmındaki iki hayali satıra gidebilir" derseniz hariç
cmxu

@Changming, bu sadece o sütunda piyon olmadığı anlamına gelir.
feersum

@feersum Daha mantıklı olduğunu görüyorum, mantık üzerinde çalışıp çalışamayacağımı ve daha hızlı uygulamak için bir yol bulup bulamayacağımı göreceğim.
cmxu

5

Java

Şu anda, kodum çok uzun ve sıkıcı, daha hızlı yapmak için çalışıyorum. Değerleri bulmak için özyinelemeli bir yöntem kullanıyorum. İlk 5'i 2 veya 3 saniye içinde hesaplar, ancak daha sonra yavaşlar. Ayrıca, sayıların doğru olup olmadığından henüz emin değilim, ancak ilk birkaçı yorumlarla aynı çizgide görünüyor. Herhangi bir öneri bekliyoruz.

Çıktı

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

açıklama

Temel fikir özyineleme. Aslında boş bir tahta, tüm sıfırları olan bir tahta ile başlarsınız. Özyinelemeli yöntem, bir sonraki konuma siyah veya beyaz bir piyon koyabildiğini kontrol eder, sadece bir renk koyabilirse, oraya koyar ve kendini çağırır. Her iki rengi de koyabilirse, her renkte bir tane olmak üzere iki kez kendini çağırır. Kendisini her çağırdığında kalan kareleri ve kalan uygun rengi azaltır. Tüm tahtayı doldurduğunda geçerli sayıyı + 1 döndürür. Bir sonraki konuma siyah veya beyaz bir piyon koymanın bir yolu olmadığını öğrenirse, 0 döndürür, bu ölü bir yol anlamına gelir.

kod

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;
                }
            }
        }
    }
}

Burada deneyin (Ideone için yeterince hızlı çalışmıyor, bu yüzden son değer yazdırılmıyor, çözümüm çok iyi değil gibi görünüyor!)


6148'e kadar hemfikirim ve henüz bunun ötesinde herhangi bir değer üretmedim.
Peter Taylor

@PeterTaylor Eh Agnishom bunun 3, 28, 408 olması gerektiğini söylüyor, bu yüzden 6148'in doğru olduğundan şüphe ediyorum. Acaba ikimiz de neyi yanlış yapıyoruz?
cmxu

Benimkinden çok daha hızlı. Sonuçlar üzerinde anlaşmasam bile +1
edc65

Merhaba! 28, 408 olduğunu söylemedim. Doğru sıra 3.30.410, ...
Agnishom Chattopadhyay

@ Edc65 doğru değerlere sahipti ve değerleri 28, 408 mi?
cmxu

4

Pthreads C ++, n = 147 156

En son sonuç, bir kod makinesinde daha önce olduğu gibi aynı kodu çalıştırmaktır. Bu şimdi dört çekirdekli i7 (Core i7-4770) olan bir masaüstünde çalıştırıldı ve 120 saniyede n = 156'ya ulaştı. Sonuç:

7858103688882482349696225090648142317093426691269441606893544257091315906431773702676266198643058148987365151560565922891852481847049321541347582728793175114543840164406674137410614843200

Bu, dinamik bir programlama algoritması kullanıyor. Başlangıçta sonucun satır satır oluşturulacağı yaklaşımları düşündüm, ancak bir ton durumu izlemeden çözümü genişletmenin bir yolunu asla bulamadım.

Oldukça verimli bir çözüm sağlayan temel bilgiler şunlardır:

  • Siyah karelerdeki piyonlar yalnızca diğer siyah karelerdeki piyonlara saldırabildiğinden ve aynı şey beyaz kareler için de geçerli olduğundan, siyah beyaz kareler bağımsızdır ve ayrı olarak işlenebilir. Ve eşdeğer olduklarından, sadece ikisinden birini işlememiz gerekir.
  • Tahtayı diyagonal olarak işlerken sorun çok daha kolaylaşır.

Geçerli bir yapılandırmanın bir köşesine bakarsanız, her zaman bir dizi siyah piyondan sonra bir dizi beyaz piyondan oluşur (bu iki diziden biri de boş olabilir). Başka bir deyişle, her köşegen, siyah piyon sayısıyla tamamen karakterize edilebilir.

Bu nedenle, her diyagonal için izlenen durum, her bir kombinasyon için geçerli piyon konfigürasyonlarının sayısıdır:

  • Satırdaki siyah piyon sayısı (veya başka bir deyişle, diyagonal içinde siyah piyonları beyaz piyonlardan ayıran konum).
  • Toplam kullanılan siyah piyon sayısı. Her şeyi piyon sayısı başına izlemeliyiz çünkü en sonunda sadece eşit sayıda siyah piyona ve beyaz piyona ihtiyacımız var. Çaprazları işlerken sayımlar farklı olabilir ve yine de sonunda geçerli çözümlerle sonuçlanır.

Bir diyagonalden diğerine geçerken, geçerli çözümler oluşturmak için başka bir kısıtlama vardır: Siyah piyonları beyaz piyonlardan ayıran konum artamaz. Dolayısıyla, geçerli yapılandırmaların sayısı, eşit veya daha büyük konumlar için önceki diyagonalin geçerli yapılandırmalarının toplamı olarak hesaplanır.

Temel DP adımı çok basittir. Diyagonaldeki her değer, önceki diyagonalden sadece değerlerin toplamıdır. Tek acı verici kısım, endeksleri ve döngü aralıklarını doğru hesaplamaktır. Çaprazlar üzerinde çalıştığımız için, hesaplamanın ilk yarısında uzunluk artar ve ikinci yarı için azalır, bu da döngü aralıklarının hesaplanmasını daha hantal hale getirir. Tahtanın sınırındaki değerler için de bazı hususlar vardır, çünkü diyagonalden diyagonallere geçerken bir tarafta sadece çapraz komşular vardır.

Kullanılan bellek miktarı O (n ^ 3) 'tür. Durum verilerinin iki kopyasını ve aralarında ping pong tutuyorum. Devlet verilerinin tek bir örneği ile çalışmanın mümkün olacağına inanıyorum. Ancak, eski değerler tamamen tüketilmeden önce hiçbir değerin güncellenmemesine çok dikkat etmelisiniz. Ayrıca, tanıttığım paralel işleme için iyi çalışmaz.

Çalışma zamanı karmaşıklığı ... polinomdur. Algoritmada 4 iç içe döngü vardır, bu yüzden ilk bakışta O (n ^ 4) gibi görünecektir. Ancak bu boyutlarda büyük noktalara ihtiyacınız var ve sayıların kendileri de daha büyük boyutlarda daha uzun oluyor. Sonuçtaki basamak sayısı kabaca n ile orantılı olarak artar gibi görünür, bu da her şeyi O (n ^ 5) yapar. Öte yandan, tüm döngülerden geçmekten kaçınan bazı performans iyileştirmeleri buldum.

Bu hala oldukça pahalı bir algoritma olmasına rağmen, çözümlerin üstel olduğu algoritmalardan çok daha fazlasını alıyor.

Uygulama ile ilgili bazı notlar:

  • Siyah karelerde 2 * n ^ 2 siyah piyon olabilirken, yalnızca n ^ 2 siyah piyona kadar yapılandırma numaralarını hesaplıyorum. Siyah ve beyaz piyonlar arasında bir simetri olduğundan, k ve 2 * n ^ 2-k için yapılandırma sayısı aynıdır.
  • Çözeltilerin sayısı, benzer bir simetriye dayanarak siyah karelerdeki konfigürasyon sayımlarının sonunda hesaplanır. Toplam çözüm sayısı (her rengin 2 * n ^ 2 piyonu olması gerekir), bir renk karesinde k siyah piyon için 2 * n ^ 2-k siyah piyon için yapılandırma sayısıyla çarpılan yapılandırma sayısıdır diğer kareler üzerinde toplanan karelerin diğer renginde.
  • Sadece diyagonal konum ve konfigürasyon sayım başına konfigürasyon sayımlarını depolamanın yanı sıra, pozisyon başına geçerli konfigürasyonlara sahip piyon sayımları aralığını da saklıyorum. Bu, iç halkanın aralığını önemli ölçüde azaltmaya izin verir. Bu olmadan, çok sayıda sıfır eklendiğini fark ettim. Bundan çok önemli bir performans artışı elde ettim.
  • Algoritma, özellikle büyük boyutlarda oldukça iyi paralellik göstermektedir. Çaprazlar sırayla süreçler olmalıdır, bu nedenle her bir diyagonalin sonunda bir engel vardır. Ancak diyagonal içindeki pozisyonlar paralel olarak işlenebilir.
  • Profilleme, darboğazın açıkça bigint değerleri eklemede olduğunu göstermektedir. Kodun bazı varyasyonları ile oynadım, ancak çok optimize edilmedi. Taşıma ile 64 bit eklemeler kullanmak için satır içi montajda önemli bir gelişme olabileceğinden şüpheleniyorum.

Ana algoritma kodu. THREADSCPU çekirdek sayısının makul bir başlangıç ​​noktası olması gereken kullanılan iş parçacığı sayısını kontrol eder:

#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;
}

Bunun da bu amaçla yazdığım bigint sınıfına ihtiyacı var. Bunun genel amaçlı bir bigint sınıfı olmadığını unutmayın. Bu özel algoritma tarafından kullanılan işlemleri desteklemek için yeterlidir:

#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

0

Fantom

İşte çerçeveyi oluşturan ilk gönderi. Prosedür nispeten iyi bir prosedür olduğunu düşünüyorum, ancak şu anda uygulama biraz berbat. Muhtemelen yaptığım hesaplamaların sayısını en aza indirmeye çalışmam ve bunun yerine daha fazla sabit iletmem gerekiyor.

strateji

Temel olarak, her beyaz piyonun diğer beyaz piyonlara saldırması gerekir. Bu yüzden beyaz bir piyon yerleştirerek, saldırdığı her yere piyon yerleştirerek ve aslında tahtaya beyaz bir piyonun gitmesi gereken tüm yerlerle doldurarak başlıyorum. Zaten çok fazla beyaz piyon eklediysem duruyorum. Bunun sonunda, tam olarak 2n ^ 2 piyonum varsa, bu bir çözümdür. Bundan daha azsa, bir yere başka bir beyaz piyon ekleyin, gerekli tüm yerleri doldurun ve tekrar sayın. 2n ^ 2'den daha az bir dolgu bulunduğunda yinelemeli olarak böldüm ve en son eklediğim piyonlu ve eksiz çözümler sayısını hesapladım.

kod

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
  }

}

Çıktı

Sadece şu anda 5'e ulaşıyor, ancak sorunun çoğunun uygulamada olduğunu düşünüyorum.

3
30
410
6148
96120

Ölçek


Benim stratejim de bu, ancak burada yayınlanan diğer çözümlere kıyasla çok yavaş görünüyor.
edc65

@ edc65 Çözümleri numaralandıran yaklaşımların şansı olmayacak. Herhangi bir şüpheniz varsa, feersum algoritması tarafından üretilen tam sayı bunun kanıtıdır. Çözüm sayısını numaralandırmadan hesaplayan bir tür dinamik programlama algoritması buraya gitmenin yoludur.
Reto Koradi
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.