Trouvez toutes les solutions à ce puzzle numérique dans les plus brefs délais

16

Histoire

Mon entreprise envoie un bulletin hebdomadaire à tous les membres de l'entreprise. Inclus dans ces newsletters est une énigme, avec un cri à quiconque dans l'entreprise a été le premier à envoyer un e-mail / fournir une solution à l'énigme de la semaine dernière. La plupart de ces énigmes sont assez banales et honnêtement assez ennuyeuses pour une entreprise de technologie, mais il y en a eu un, il y a plusieurs mois, qui a attiré mon attention.

L'énigme originale:

Compte tenu de la forme ci-dessous:

Image de puzzle

Vous avez les nombres naturels de 1 à 16. Ajustez-les tous dans cette forme, de sorte que toutes les lignes et colonnes contiguës totalisent jusqu'à 29.

Par exemple, une solution à ce casse-tête (qui était la solution "canonique" que j'ai soumise à la newsletter) était la suivante:

Image de puzzle résolu

Cependant, au cours de sa résolution, j'ai trouvé des informations assez intéressantes:

  • Il y a beaucoup plus de solutions que celle-là; en fait, il existe 9 368 solutions.
  • Si vous développez l'ensemble de règles pour exiger uniquement que les lignes et les colonnes soient égales les unes aux autres, pas nécessairement 29, vous obtenez 33608 solutions:
    • 4 440 Solutions pour une somme de 27.
    • 7 400 Solutions pour une somme de 28.
    • 9 368 Solutions pour une somme de 29.
    • 6 096 Solutions pour une somme de 30.
    • 5 104 Solutions pour une somme de 31.
    • 1200 Solutions pour une somme de 32.

Alors mes collègues et moi (mais surtout juste mon manager, car il était la seule autre personne que moi à avoir des compétences en programmation "à usage général") nous sommes lancés dans un défi, qui a duré pendant la majeure partie du mois - nous avions un autre travail réel - obligations connexes auxquelles nous devions nous conformer: essayer d'écrire un programme qui trouverait chaque solution de la manière la plus rapide possible.

Statistiques originales

Le tout premier programme que j'ai écrit pour résoudre le problème a simplement vérifié les solutions aléatoires encore et encore et s'est arrêté lorsqu'il a trouvé une solution. Si vous avez fait une analyse mathématique de ce problème, vous savez probablement déjà que cela n'aurait pas dû fonctionner; mais en quelque sorte, j'ai eu de la chance, et il n'a fallu au programme qu'une minute pour trouver une solution unique (celle que j'ai publiée ci-dessus). Les répétitions du programme prenaient souvent jusqu'à 10 ou 20 minutes, donc ce n'était évidemment pas une solution rigoureuse au problème.

Je suis passé à une solution récursive qui a itéré à travers toutes les permutations possibles du puzzle, et j'ai rejeté de nombreuses solutions à la fois en éliminant les sommes qui ne s'additionnaient pas. IE si la première ligne / colonne que je comparais n'était déjà pas égale, je pouvais arrêter de vérifier cette branche immédiatement, sachant que rien d'autre permuté dans le puzzle ne changerait cela.

En utilisant cet algorithme, j'ai obtenu le premier succès "correct": le programme a pu générer et cracher toutes les 33 608 solutions en environ 5 minutes.

Mon manager avait une approche différente: sachant sur la base de mon travail que les seules solutions possibles avaient des sommes de 27, 28, 29, 30, 31 ou 32, il a écrit une solution multi-thread qui vérifiait les sommes possibles uniquement pour ces valeurs spécifiques. Il a réussi à exécuter son programme en seulement 2 minutes. J'ai donc réitéré; J'ai haché toutes les sommes possibles de 3/4 chiffres (au début du programme; il est compté dans le temps d'exécution total) et j'ai utilisé la "somme partielle" d'une ligne pour rechercher la valeur restante sur la base d'une ligne précédemment complétée, plutôt que tester toutes les valeurs restantes et réduire le temps à 72 secondes. Ensuite, avec une logique multi-threading, je l'ai réduit à 40 secondes. Mon manager a ramené le programme à la maison, a effectué quelques optimisations sur la façon dont le programme a fonctionné et est descendu à 12 secondes. J'ai réorganisé l'évaluation des lignes et des colonnes,

Le plus rapide que nous ayons obtenu nos programmes après un mois était de 0,15 seconde pour mon manager et de 0,33 seconde pour moi. J'ai fini par affirmer que mon programme était plus rapide, car le programme de mon manager, bien qu'il ait trouvé toutes les solutions, ne les imprimait pas dans un fichier texte. S'il ajoutait cette logique à son code, cela prenait souvent plus de 0,4-0,5 seconde.

Depuis, nous avons laissé subsister notre défi intra-personnel, mais bien sûr, la question demeure: ce programme peut- il être accéléré?

C'est le défi que je vais vous poser.

Votre défi

Les paramètres sur lesquels nous avons travaillé ont assoupli la règle de la «somme de 29» pour qu'elle soit «toutes les sommes des lignes / colonnes égales», et je vais également définir cette règle pour vous. Le défi est donc: Ecrire un programme qui trouve (et imprime!) Toutes les solutions à cette énigme dans les plus brefs délais. Je vais fixer un plafond aux solutions soumises: si le programme prend plus de 10 secondes sur un ordinateur relativement décent (<8 ans), c'est probablement trop lent pour être compté.

De plus, j'ai quelques bonus pour le puzzle:

  • Pouvez-vous généraliser la solution pour qu'elle fonctionne pour n'importe quel ensemble de 16 chiffres, pas seulement int[1,16]? Le score de synchronisation serait évalué sur la base de l'ensemble de numéros d'invite d'origine, mais passé par ce chemin de code. (-dix%)
  • Pouvez-vous écrire le code d'une manière qu'il gère avec élégance et résout avec des nombres en double? Ce n'est pas aussi simple que cela puisse paraître! Les solutions «visuellement identiques» doivent être uniques dans l'ensemble de résultats. (-5%)
  • Pouvez-vous gérer des nombres négatifs? (-5%)

Vous pouvez également essayer de générer une solution qui gère les nombres à virgule flottante, mais bien sûr, ne soyez pas choqué si cela échoue complètement. Si vous trouvez une solution robuste, cela pourrait valoir un gros bonus!

À toutes fins utiles, les «rotations» sont considérées comme des solutions uniques. Ainsi, une solution qui n'est qu'une rotation d'une solution différente compte comme sa propre solution.

Les IDE que je travaille sur mon ordinateur sont Java et C ++. Je peux accepter les réponses d'autres langues, mais vous devrez peut-être également fournir un lien vers où obtenir un environnement d'exécution facile à configurer pour votre code.

Xirema
la source
3
Saints chats, belle première question! ... à l'exception des bonus, que nous décourageons (surtout sur les questions de code-golf , donc ça devrait aller ici)
chat
4
@cat Je pense que les bonus ont un sens ici, car lorsque j'ai résolu ces problèmes dans mon propre code, ils avaient tendance à ralentir le code de manière significative. Je pense donc qu'un bonus est pour justifier l'inclusion.
Xirema
2
BTW, y a-t-il des emplois chez vous? on dirait que vous avez un patron décontracté et beaucoup de temps libre :-)
Level River St
1
Avec des numéros en double, est-il OK d'imprimer des solutions en double où les deux numéros identiques sont échangés? cela ferait une grande différence dans la façon dont ce bonus est interprété. Veuillez clarifier, mais j'envisagerais d'éliminer complètement ce bonus.
Level River St
1
De plus, les rotations à 180 degrés sont-elles considérées comme la même solution ou des solutions différentes?
Level River St

Réponses:

7

C - près de 0,5 sec

Ce programme très naïf donne toutes les solutions en une demi-seconde sur mon portable de 4 ans. Pas de multithread, pas de hachage.

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

Essayez en ligne sur 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);
}
edc65
la source
Holy sweet Evil Vampiric Jesus, ceux imbriqués pour les boucles. Je parie que cela a rendu votre compilateur vraiment heureux. XD
Xirema
@ edc65, FYI vous pouvez remplacer int inuse[16];par juste int inuse;et ensuite utiliser des opérateurs au niveau du bit pour le manipuler. Il ne semble pas augmenter la vitesse que beaucoup, mais il aide un peu.
Andrew Epstein
@AndrewEpstein cela pourrait même devenir plus lent - bitshift vs indexation
edc65
@ edc65, j'ai pris la liberté d'utiliser dumbbench pour tester votre version originale par rapport à la version bitshift. Voici les résultats: Indexation: 0,2253 +/- 5,7590e-05 Bitshifting: 0,2093 +/- 6,6595e-05 Donc, environ une accélération de 16 ms sur ma machine. La commande que j'ai utilisée était:dumbbench --precision=.01 -vvv --initial=500 ./solve
Andrew Epstein
3

C ++ - 300 millisecondes

Par demande, j'ai ajouté mon propre code pour résoudre ce puzzle. Sur mon ordinateur, il se déclenche en moyenne à 0,310 seconde (310 millisecondes), mais en fonction de la variance, il peut fonctionner aussi rapidement que 287 millisecondes. Je le vois très rarement dépasser 350 millisecondes, généralement seulement si mon système est enlisé dans une tâche différente.

Ces temps sont basés sur l'auto-évaluation utilisée dans le programme, mais j'ai également testé en utilisant une minuterie externe et j'obtiens des résultats similaires. Les frais généraux du programme semblent ajouter environ 10 millisecondes.

De plus, mon code ne gère pas tout à fait correctement les doublons. Il peut résoudre leur utilisation, mais il n'élimine pas les solutions «visuellement identiques» de l'ensemble de solutions.

#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;
}
Xirema
la source
Comme je suis sûr que vous le savez, si vous simplifiez un peu la sortie, vous pouvez raser un temps décent. Voici le temps pour votre code: 0.1038s +/- 0.0002 Et voici le temps pour votre code avec une sortie simplifiée: 0.0850s +/- 0.0001 Ainsi, vous pouvez économiser ~ 18 ms, au moins sur ma machine. J'ai exécuté les deux versions plus de 500 fois avec des valeurs aberrantes rejetées, en utilisant un dumbbench
Andrew Epstein
1

Prolog - 3 minutes

Ce type de puzzle semble être un cas d'utilisation parfait pour Prolog. J'ai donc codé une solution dans Prolog! C'est ici:

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

Malheureusement, ce n'est pas aussi rapide que prévu. Peut-être que quelqu'un de plus expérimenté en programmation déclarative (ou en particulier Prolog) peut offrir quelques conseils d'optimisation. Vous pouvez appeler la règle puzzleavec la commande suivante:

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

Essayez-le en ligne ici . Vous pouvez remplacer n'importe quel nombre à la place des 29s dans le code pour générer toutes les solutions. À l'heure actuelle, toutes les 29 solutions sont trouvées en environ 30 secondes, donc pour trouver toutes les solutions possibles, il faut environ 3 minutes.

Andrew Epstein
la source