Trova tutte le soluzioni a questo numero Puzzle nel più breve tempo possibile


16

Storia

La mia azienda invia una newsletter settimanale a tutti i membri dell'azienda. In queste newsletter è incluso un indovinello, insieme a uno shoutout a chiunque in azienda sia stato il primo a inviare e-mail / fornire una soluzione all'enigma della scorsa settimana. La maggior parte di questi indovinelli sono piuttosto banali e onestamente piuttosto noiosi per un'azienda tecnologica, ma ce n'è stato uno, diversi mesi fa, che ha attirato la mia attenzione.

The Original Riddle:

Data la forma seguente:

Immagine Puzzle

Hai i numeri naturali da 1 a 16. Adattali tutti in questa forma, in modo che tutte le righe contigue e le colonne contigue si sommino a 29.

Ad esempio, una di queste soluzioni a questo puzzle (che era la soluzione "canonica" che ho inviato alla newsletter) era la seguente:

Risolto Puzzle Image

Tuttavia, nel corso della risoluzione, ho trovato alcune informazioni piuttosto interessanti:

  • Esistono molte più soluzioni rispetto a quella; in effetti, ci sono 9.368 soluzioni.
  • Se si espande il set di regole per richiedere solo che righe e colonne siano uguali tra loro, non necessariamente 29, si ottengono 33.608 soluzioni:
    • 4.440 soluzioni per un importo di 27.
    • 7.400 soluzioni per un importo di 28.
    • 9.368 soluzioni per un importo di 29.
    • 6.096 Soluzioni per un importo di 30.
    • 5.104 Soluzioni per un importo di 31.
    • 1.200 soluzioni per un totale di 32.

Quindi io e i miei colleghi (sebbene per lo più solo il mio manager, dato che era l'unica persona diversa da me con abilità di programmazione "Scopo generale") ci siamo lanciati in una sfida, che è durata per la maggior parte del mese - abbiamo avuto un altro lavoro effettivo- obblighi correlati a cui dovevamo attenerci: per provare a scrivere un programma che trovasse ogni singola soluzione nel modo più veloce possibile.

Statistiche originali

Il primo programma che ho scritto per risolvere il problema ha semplicemente verificato più volte soluzioni casuali e si è fermato quando ha trovato una soluzione. Se hai fatto un'analisi matematica su questo problema, probabilmente già sai che questo non avrebbe dovuto funzionare; ma in qualche modo sono stato fortunato, e il programma ha impiegato solo un minuto per trovare un'unica soluzione (quella che ho pubblicato sopra). Le ripetute esecuzioni del programma richiedevano spesso fino a 10 o 20 minuti, quindi ovviamente questa non era una soluzione rigorosa al problema.

Sono passato a una soluzione ricorsiva che ha ripetuto ogni possibile permutazione del puzzle e ho scartato molte soluzioni contemporaneamente eliminando le somme che non si stavano sommando. IE se la prima riga / colonna che stavo confrontando non fosse già uguale, potrei smettere di controllare immediatamente quel ramo, sapendo che nient'altro permutato nel puzzle lo cambierebbe.

Usando questo algoritmo, ho ottenuto il primo successo "corretto": il programma potrebbe generare e sputare tutte le 33.608 soluzioni in circa 5 minuti.

Il mio manager aveva un approccio diverso: sapendo in base al mio lavoro che le uniche soluzioni possibili avevano somme di 27, 28, 29, 30, 31 o 32, scrisse una soluzione multi-thread che controllava possibili somme solo per quei valori specifici. È riuscito a far funzionare il suo programma in soli 2 minuti. Così ho ripetuto di nuovo; Ho sommato tutte le possibili somme di 3/4 cifre (all'inizio del programma; viene conteggiato nel tempo di esecuzione totale) e ho usato la "somma parziale" di una riga per cercare il valore rimanente in base a una riga precedentemente completata, anziché testare tutti i valori rimanenti e ridurre il tempo a 72 secondi. Quindi, con una certa logica multi-threading, l'ho ridotto a 40 secondi. Il mio manager ha portato a casa il programma, ha eseguito alcune ottimizzazioni su come il programma è stato eseguito e lo ha ridotto a 12 secondi. Ho riordinato la valutazione delle righe e delle colonne,

Uno dei due più veloci tra i nostri programmi dopo un mese è stato di 0,15 secondi per il mio manager e 0,33 secondi per me. Alla fine ho sostenuto che il mio programma era più veloce, poiché il programma del mio manager, sebbene trovasse tutte le soluzioni, non le stesse stampando in un file di testo. Se aggiungeva quella logica al suo codice, spesso impiegava fino a 0,4-0,5 secondi.

Da allora abbiamo permesso alla nostra sfida intra-personale di sussistere, ma ovviamente rimane la domanda: questo programma può essere reso più veloce?

Questa è la sfida che porterò a voi ragazzi.

La tua sfida

I parametri su cui abbiamo lavorato hanno rilassato la regola della "somma di 29" invece di "tutte le somme di righe / colonne uguali" e imposterò questa regola anche per voi. La sfida, quindi, è: scrivere un programma che trovi (e stampa!) Tutte le soluzioni a questo indovinello nel più breve tempo possibile. Ho intenzione di fissare un limite alle soluzioni presentate: se il programma impiega più di 10 secondi su un computer relativamente decente (<8 anni), probabilmente è troppo lento per essere conteggiato.

Inoltre, ho alcuni bonus per il puzzle:

  • Puoi generalizzare la soluzione in modo che funzioni per qualsiasi set di 16 numeri, non solo int[1,16]? Il punteggio di tempo verrà valutato in base al numero di prompt iniziale impostato, ma passato attraverso questo codice. (-10%)
  • Riesci a scrivere il codice in modo che gestisca e risolva con garbo con numeri duplicati? Non è così semplice come potrebbe sembrare! Le soluzioni "visivamente identiche" dovrebbero essere uniche nel set di risultati. (-5%)
  • Riesci a gestire numeri negativi? (-5%)

Puoi anche provare a generare una soluzione che gestisca i numeri in virgola mobile, ma ovviamente non essere scioccato se fallisce completamente. Se trovi una soluzione solida, potrebbe valere un grande bonus!

A tutti gli effetti, le "rotazioni" sono considerate soluzioni uniche. Quindi una soluzione che è solo una rotazione di una soluzione diversa conta come propria soluzione.

Gli IDE che sto lavorando sul mio computer sono Java e C ++. Posso accettare risposte da altre lingue, ma potrebbe essere necessario fornire anche un collegamento a dove posso ottenere un ambiente di runtime facile da configurare per il tuo codice.


3
Gatti santi, bella prima domanda! ... ad eccezione dei bonus, che noi scoraggiamo in qualche modo (principalmente su questioni di code-golf , quindi dovrebbero andare bene qui)
cat

4
@cat Penso che i bonus abbiano senso qui, perché quando ho risolto quei problemi nel mio codice, tendevano a rallentare il codice in modo significativo. Quindi penso che un bonus sia per giustificare l'inclusione.
Xirema,

2
A proposito, ci sono lavori che vanno a casa tua? sembra che tu abbia un capo accomodante e un sacco di tempo a disposizione :-)
Level River St

1
Con numeri duplicati, è corretto stampare soluzioni duplicate in cui vengono scambiati i due numeri identici? ciò farebbe una grande differenza su come viene interpretato quel bonus. Per favore, chiarisci, ma prenderei in considerazione l'eliminazione del bonus.
Level River St,

1
Inoltre, le rotazioni di 180 gradi sono considerate la stessa soluzione o soluzioni diverse?
Level River St,

Risposte:


7

C - vicino a 0,5 sec

Questo programma molto ingenuo offre tutte le soluzioni in mezzo secondo sul mio laptop di 4 anni. Nessun multithread, nessun hash.

Windows 10, Visual Studio 2010, CPU core I7 64 bit

Prova online su ideone

#include <stdio.h>
#include <time.h>

int inuse[16];
int results[16+15+14];

FILE *fout;

int check(int number)
{
    if (number > 0 && number < 17 && !inuse[number-1])
    {
        return inuse[number-1]=1;
    }
    return 0;
}

void free(int number)
{
    inuse[number-1]=0;
}

void out(int t, int* p)
{
    int i;
    fprintf(fout, "\n%d",t);
    for(i=0; i< 16; i++) fprintf(fout, " %d",*p++);
}

void scan() 
{
    int p[16];
    int t,i;
    for (p[0]=0; p[0]++<16;) if (check(p[0]))
    {
        for (p[1]=0; p[1]++<16;) if (check(p[1]))
        {
            for (p[2]=0; p[2]++<16;) if (check(p[2]))
            {
                t = p[0]+p[1]+p[2]; // top horiz: 0,1,2
                for (p[7]=0; p[7]++<16;) if (check(p[7]))
                {
                    if (check(p[11] = t-p[7]-p[2])) // right vert: 2,7,11
                    {
                        for(p[9]=0; p[9]++<16;) if (check(p[9]))
                        {
                            for (p[10]=0; p[10]++<16;) if (check(p[10]))
                            {
                                if (check(p[12] = t-p[9]-p[10]-p[11])) // right horiz: 9,10,11,12
                                {
                                    for(p[6]=0; p[6]++<16;) if (check(p[6]))
                                    {
                                        if (check(p[15] = t-p[0]-p[6]-p[9])) // middle vert: 0,6,9,15
                                        {
                                            for(p[13]=0; p[13]++<16;) if (check(p[13]))
                                            {
                                                if (check(p[14] = t-p[13]-p[15])) // bottom horiz:  13,14,15
                                                {
                                                    for(p[4]=0; p[4]++<16;) if (check(p[4]))
                                                    {
                                                        if (check(p[8] = t-p[4]-p[13])) // left vert: 4,8,13
                                                        {
                                                            for(p[3]=0; p[3]++<16;) if (check(p[3]))
                                                            {
                                                                if (check(p[5] = t-p[3]-p[4]-p[6])) // left horiz: 3,4,5,6
                                                                {
                                                                    ++results[t];
                                                                    out(t,p);
                                                                    free(p[5]);
                                                                }
                                                                free(p[3]);
                                                            }
                                                            free(p[8]);
                                                        }
                                                        free(p[4]);
                                                    }
                                                    free(p[14]);
                                                }
                                                free(p[13]);
                                            }
                                            free(p[15]);
                                        }
                                        free(p[6]);
                                    }
                                    free(p[12]);
                                }
                                free(p[10]);
                            }
                            free(p[9]);
                        }
                        free(p[11]);
                    }
                    free(p[7]);
                }    
                free(p[2]);
            } 
            free(p[1]);
        }
        free(p[0]);
    }
    for(i=0;i<15+16+14;i++)
    {
        if(results[i]) printf("%d %d\n", i, results[i]);
    }
}

void main()
{
    clock_t begin, end;
    double time_spent;
    begin = clock();

    fout = fopen("c:\\temp\\puzzle29.txt", "w");
    scan();
    fclose(fout);

    end = clock();
    time_spent = (double)(end - begin) / CLOCKS_PER_SEC;
    printf("Time %g sec\n", time_spent);
}

Santo dolce diabolico Gesù vampirico, quelli nidificati per gli anelli. Scommetto che però ha reso il tuo compilatore davvero felice. XD
Xirema,

@ edc65, FYI che puoi sostituire int inuse[16];con just int inuse;e quindi utilizzare operatori bit per manipolarlo. Non sembra aumentare così tanto la velocità , ma aiuta un po '.
Andrew Epstein,

@AndrewEpstein potrebbe anche diventare più lento - bitshift vs indicizzazione
edc65

@ edc65, mi sono preso la libertà di usare dumbbench per testare la tua versione originale rispetto alla versione bitshift. Ecco i risultati: Indicizzazione: 0,2253 +/- 5,7590e-05 Spostamento bit: 0,2093 +/- 6,6595e-05 Quindi, circa una velocità di 16 ms sulla mia macchina. Il comando che ho usato è stato:dumbbench --precision=.01 -vvv --initial=500 ./solve
Andrew Epstein il

3

C ++ - 300 Millisecondi

Per richiesta, ho aggiunto il mio codice per risolvere questo enigma. Sul mio computer, si attiva in media 0,310 secondi (310 millisecondi) ma, a seconda della varianza, può essere eseguito con la stessa velocità di 287 millisecondi. Raramente lo vedo salire oltre i 350 millisecondi, di solito solo se il mio sistema è impantanato con un compito diverso.

Questi tempi si basano sull'auto-reporting utilizzato nel programma, ma ho anche testato utilizzando un timer esterno e ho ottenuto risultati simili. Le spese generali nel programma sembrano aggiungere circa 10 millisecondi.

Inoltre, il mio codice non abbastanza gestisce correttamente i duplicati. Può risolverli usandoli, ma non elimina soluzioni "visivamente identiche" dal set di soluzioni.

#include<iostream>
#include<vector>
#include<random>
#include<functional>
#include<unordered_set>
#include<unordered_map>
#include<array>
#include<thread>
#include<chrono>
#include<fstream>
#include<iomanip>
#include<string>
#include<mutex>
#include<queue>
#include<sstream>
#include<utility>
#include<atomic>
#include<algorithm>

//#define REDUCE_MEMORY_USE

typedef std::pair<int, std::vector<std::pair<int, int>>> sumlist;
typedef std::unordered_map<int, std::vector<std::pair<int, int>>> summap;
typedef std::array<int, 16> solution_space;

class static_solution_state {
public:
    std::array<int, 16> validNumbers;
    summap twosums;
    size_t padding;
    std::string spacing;

    static_solution_state(const std::array<int, 16> & _valid);

    summap gettwovaluesums();
    std::vector<sumlist> gettwovaluesumsvector();
};

static_solution_state::static_solution_state(const std::array<int, 16> & _valid) 
    : validNumbers(_valid) {
    twosums = gettwovaluesums();
    padding = 0;
    for (int i = 0; i < 16; i++) {
        size_t count = std::to_string(validNumbers[i]).size();
        if (padding <= count) padding = count + 1;
    }
    spacing.resize(padding, ' ');
}

class solution_state {
private:
    const static_solution_state * static_state;
public:
    std::array<int, 16> currentSolution;
    std::array<bool, 16> used;
    std::array<int, 7> sums;
    size_t solutions_found;
    size_t permutations_found;
    size_t level;
    std::ostream * log;

    solution_state(const static_solution_state & _sstate);
    solution_state(static_solution_state & _sstate) = delete;
    void setLog(std::ostream & out);
    const int & operator[](size_t index) const;

};

solution_state::solution_state(const static_solution_state & _sstate) {
    static_state = &_sstate;
    sums = { 0 };
    used = { false };
    currentSolution = { -1 };
    solutions_found = 0;
    permutations_found = 0;
    level = 0;
}

void solution_state::setLog(std::ostream & out) {
    log = &out;
}

const int & solution_state::operator[](size_t index) const {
    return static_state->validNumbers[currentSolution[index]];
}

int getincompletetwosum(const static_solution_state & static_state, const solution_state & state);
void permute(const static_solution_state & static_state, solution_state & state, volatile bool & reportProgress, const volatile size_t & total_tests, volatile bool & done);
void setupOutput(std::fstream & out);
void printSolution(const static_solution_state & static_state, const solution_state & state);
constexpr size_t factorial(const size_t iter);

const bool findnext2digits[16]{
    false, false, false,
    true, false,
    false, true, false,
    true, false,
    true, false,
    true, false,
    true, false
};

const int currentsum[16]{
    0, 0, 0,
    1, 1,
    2, 2, 2,
    3, 3,
    4, 4,
    5, 5,
    6, 6
};

const int twosumindexes[7][2]{
    { 0, -1},
    { 2, -1},
    { 5, -1},
    { 5, -1},
    { 0,  7},
    { 11, 4},
    { 10, 9}
};

const std::array<size_t, 17> facttable = [] {
    std::array<size_t, 17> table;
    for (int i = 0; i < 17; i++) table[i] = factorial(i);
    return table;
}();

const int adj = 1;

std::thread::id t1id;

int main(int argc, char** argv) {
    //std::ios_base::sync_with_stdio(false);
    std::array<int, 16> values = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 };
    if (argc == 17) {
        for (int i = 0; i < 16; i++) {
            values[i] = atoi(argv[i + 1]);
        }
    }
    auto start = std::chrono::high_resolution_clock::now();
    const static_solution_state static_state(values);
#if defined(REDUCE_MEMORY_USE)
    const int num_of_threads = max(1u, min(thread::hardware_concurrency(), 16u));
#else
    const int num_of_threads = 16;
#endif
    std::vector<solution_state> states(num_of_threads, static_state);
    for (int i = 0; i < num_of_threads; i++) {
        int start = i * 16 / num_of_threads;
        states[i].permutations_found += start * factorial(16) / 16;
    }
    std::fstream out;
    setupOutput(out);
    std::locale loc("");
    std::cout.imbue(loc);
    volatile bool report = false;
    volatile bool done = false;
    volatile size_t tests = 0;

    std::thread progress([&]() {
        auto now = std::chrono::steady_clock::now();
        while (!done) {
            if (std::chrono::steady_clock::now() - now > std::chrono::seconds(1)) {
                now += std::chrono::seconds(1);

                size_t t_tests = 0;
                for (int i = 0; i < num_of_threads; i++) t_tests += states[i].permutations_found - i * factorial(16) / num_of_threads;
                tests = t_tests;
                report = true;
            }
            std::this_thread::yield();
        }
    });

    if (num_of_threads <= 1) {


        states[0].setLog(out);
        permute(static_state, states[0], report, tests, done);


    } 
    else {
        std::vector<std::thread> threads;
#if defined(REDUCE_MEMORY_USE)
        std::vector<std::fstream> logs(num_of_threads);
#else
        std::vector<std::stringstream> logs(num_of_threads);
#endif
        for (int i = 0; i < num_of_threads; i++) {
            threads.emplace_back([&, i]() {
                if (i == 0) t1id = std::this_thread::get_id();
                int start = i * 16 / num_of_threads;
                int end = (i + 1) * 16 / num_of_threads;
#if defined(REDUCE_MEMORY_USE)
                logs[i].open("T"s + to_string(i) + "log.tmp", ios::out);
#endif
                logs[i].imbue(loc);
                states[i].setLog(logs[i]);

                for (int j = start; j < end; j++) {


                    states[i].currentSolution = { j };
                    states[i].level = 1;
                    states[i].used[j] = true;
                    permute(static_state, states[i], report, tests, done);


                }
            });
        }

        std::string buffer;
        for (int i = 0; i < num_of_threads; i++) {
            threads[i].join();
#if defined(REDUCE_MEMORY_USE)
            logs[i].close();
            logs[i].open("T"s + to_string(i) + "log.tmp", ios::in);
            logs[i].seekg(0, ios::end);
            auto length = logs[i].tellg();
            logs[i].seekg(0, ios::beg);
            buffer.resize(length);
            logs[i].read(&buffer[0], length);
            logs[i].close();
            remove(("T"s + to_string(i) + "log.tmp").c_str());
            out << buffer;
#else
            out << logs[i].str();
#endif
        }
    }
    done = true;
    out.close();

    if (num_of_threads > 1) {
        size_t t_tests = 0;
        for (int i = 0; i < num_of_threads; i++) t_tests += states[i].permutations_found - i * factorial(16) / num_of_threads;
        tests = t_tests;
    }

    size_t solutions = 0;
    for (const auto & state : states) {
        solutions += state.solutions_found;
    }

    auto end = std::chrono::high_resolution_clock::now();

    progress.join();

    auto duration = end - start;
    auto secondsDuration = std::chrono::duration_cast<std::chrono::milliseconds>(duration);
    std::cout << "Total time to process all " << tests << " results: " << std::setprecision(3) << std::setiosflags(std::ostream::fixed) << (secondsDuration.count()/1000.0) << "s" << "\n";
    std::cout << "Solutions found: " << solutions << std::endl;
    //system("pause");
    return 0;
}

void permute(const static_solution_state & static_state, solution_state & state, volatile bool & reportProgress, const volatile size_t & total_tests, volatile bool & done) {
    if (done) return;
    if (state.level >= 16) {
        if (reportProgress) {
            reportProgress = false;
            std::cout << "Current Status:" << "\n";
            std::cout << "Test " << total_tests << "\n";
            std::cout << "Contents: {";
            for (int i = 0; i < 15; i++) std::cout << std::setw(static_state.padding - 1) << state[i] << ",";
            std::cout << std::setw(static_state.padding - 1) << state[15] << "}" << "(Partial Sum: " << state.sums[0] << ")" << "\n";
            std::cout << "=====================" << "\n";
        }
        printSolution(static_state,state);
        state.solutions_found++;
        state.permutations_found++;
    }
    else {
        if (state.level == 3) state.sums[0] = state[0] + state[1] + state[2];

        if (!findnext2digits[state.level]) {
            for (int i = 0; i < 16; i++) {
                if (!state.used[i]) {
                    state.currentSolution[state.level] = i;
                    state.used[i] = true;
                    state.level++;
                    permute(static_state, state, reportProgress, total_tests, done);
                    state.level--;
                    state.used[i] = false;
                }
            }
        }
        else {
            int incompletetwosum = getincompletetwosum(static_state, state);
            if (static_state.twosums.find(incompletetwosum) == static_state.twosums.end()) {
                state.permutations_found += facttable[16 - state.level];
            }
            else {
                size_t successes = 0;
                const std::vector<std::pair<int, int>> & potentialpairs = static_state.twosums.at(incompletetwosum);
                for (const std::pair<int, int> & values : potentialpairs) {
                    if (!state.used[values.first] && !state.used[values.second]) {
                        state.currentSolution[state.level] = values.first;
                        state.currentSolution[state.level + 1] = values.second;
                        state.used[values.first] = true;
                        state.used[values.second] = true;
                        state.level += 2;
                        permute(static_state, state, reportProgress, total_tests, done);
                        state.level -= 2;
                        state.used[values.first] = false;
                        state.used[values.second] = false;

                        successes++;
                    }
                }
                state.permutations_found += facttable[16 - state.level - 2] * ((16 - state.level) * (15 - state.level) - successes); 
            }
        }
    }
}

int getincompletetwosum(const static_solution_state & static_state, const solution_state & state) {
    int retvalue = state.sums[0];
    int thissum = currentsum[state.level];
    for (int i = 0; i < 2 && twosumindexes[thissum][i] >= 0; i++) {
        retvalue -= state[twosumindexes[thissum][i]];
    }
    return retvalue;
}

constexpr size_t factorial(size_t iter) {
    return (iter <= 0) ? 1 : iter * factorial(iter - 1);
}

void setupOutput(std::fstream & out) {
    out.open("puzzle.txt", std::ios::out | std::ios::trunc);
    std::locale loc("");
    out.imbue(loc);
}

void printSolution(const static_solution_state & static_state, const solution_state & state) {
    std::ostream & out = *state.log;
    out << "Test " << state.permutations_found << "\n";
    static const auto format = [](std::ostream & out, const static_solution_state & static_state, const solution_state & state, const std::vector<int> & inputs) {
        for (const int & index : inputs) {
            if (index < 0 || index >= 16) out << static_state.spacing;
            else out
                << std::setw(static_state.padding)
                << state[index];
        }
        out << "\n";
    };
    format(out, static_state, state, { -1, -1, -1,  0,  1,  2 });
    format(out, static_state, state, { 15,  9, 14, 10, -1,  3 });
    format(out, static_state, state, { -1,  8, -1, 11, 12,  4, 13 });
    format(out, static_state, state, { -1,  5,  6,  7});

    out << "Partial Sum: " << (state.sums[0]) << "\n";
    out << "=============================" << "\n";
}

summap static_solution_state::gettwovaluesums() {
    summap sums;
    for (int i = 0; i < 16; i++) {
        for (int j = 0; j < 16; j++) {
            if (i == j) continue;
            std::pair<int,int> values( i, j );
            int sum = validNumbers[values.first] + validNumbers[values.second];
            sums[sum].push_back(values);
        }
    }
    return sums;
}

std::vector<sumlist> static_solution_state::gettwovaluesumsvector() {
    std::vector<sumlist> sums;
    for (auto & key : twosums) {
        sums.push_back(key);
    }

    std::sort(sums.begin(), sums.end(), [](sumlist a, sumlist b) {
        return a.first < b.first;
    });
    return sums;
}

Come sono sicuro che sei a conoscenza, se semplifichi un po 'l'output, puoi radere una discreta quantità di tempo. Ecco il tempo per il tuo codice: 0.1038s +/- 0.0002 Ed ecco il tempo per il tuo codice con output semplificato: 0.0850s +/- 0.0001 Quindi, puoi risparmiare ~ 18ms, almeno sulla mia macchina. Ho eseguito entrambe le versioni 500+ volte con gli outlier eliminati, usando il dumbbench
Andrew Epstein,

1

Prolog - 3 minuti

Questo tipo di puzzle sembra un caso d'uso perfetto per Prolog. Quindi, ho codificato una soluzione in Prolog! Ecco qui:

:- use_module(library(clpfd)).

puzzle(P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15) :-
    Vars = [P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15],
    Vars ins 1..16,
    all_different(Vars),
    29 #= P0 + P1 + P2,
    29 #= P3 + P4 + P5 + P6,
    29 #= P9 + P10 + P11 + P12,
    29 #= P13 + P14 + P15,
    29 #= P0 + P6 + P9 + P15,
    29 #= P2 + P7 + P11,
    29 #= P4 + P8 + P13.

Sfortunatamente, non è veloce come mi aspettavo. Forse qualcuno più esperto nella programmazione dichiarativa (o nello specifico Prolog) può offrire alcuni suggerimenti per l'ottimizzazione. Puoi invocare la regola puzzlecon il seguente comando:

time(aggregate_all(count, (puzzle(P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15), labeling([leftmost, up, enum], [P9, P15, P13, P0, P4, P2, P6, P11, P1, P5, P3, P7, P14, P12, P10, P8])), Count)).

Provalo online qui . È possibile sostituire qualsiasi numero al posto della 29s nel codice per generare tutte le soluzioni. Allo stato attuale, tutte le 29 soluzioni si trovano in circa 30 secondi, quindi per trovare tutte le possibili soluzioni dovrebbero essere di circa 3 minuti.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.