Знайдіть усі рішення цієї головоломки за найкоротший час


16

Історія

Моя компанія щотижнево розсилає інформаційний бюлетень усім в межах компанії. До цих бюлетенів входить загадка, а також вигук, хто в компанії першим надіслав електронною поштою / запропонував рішення загадки минулого тижня. Більшість цих загадок досить тривіальні і, чесно кажучи, досить тупі для технічної компанії, але був один, кілька місяців тому, що привернув мою увагу.

Оригінальна загадка:

З огляду на форму нижче:

Зображення головоломки

У вас природні номери від 1 до 16. Встановіть їх у цю форму, щоб усі суміжні ряди та суміжні стовпці складали до 29.

Наприклад, одним із таких варіантів цієї головоломки (що було "канонічним" рішенням, яке я подав у розсилку) було таке:

Розв’язано зображення головоломки

Однак у процесі її вирішення я знайшов досить цікаву інформацію:

  • Рішень є значно більше, ніж лише те; насправді існує 9 368 рішень.
  • Якщо розгорнути набір правил, вимагаючи лише, щоб рядки та стовпці були рівні між собою, а не обов'язково 29, ви отримаєте 33 608 рішення:
    • 4,440 Рішення на суму 27.
    • 7400 Рішення на суму 28.
    • 9,368 Рішення на суму 29.
    • 696 Розв’язання на суму 30.
    • 5,104 Рішення на суму 31.
    • 1200 рішень на суму 32.

Тож я та мої колеги (хоча здебільшого просто мій менеджер, оскільки він був єдиною іншою людиною, ніж я, із навичками програмування "Загальна мета"), поставили перед собою виклик, який тривав більшу частину місяця - у нас була інша, фактична робота - пов'язані зобов’язання, які ми повинні були взяти на себе - спробувати написати програму, яка б найшвидше знаходила кожне рішення.

Оригінальна статистика

Перша програма, яку я написав для вирішення проблеми, просто перевіряла випадкові рішення знову і знову, і зупинялася, коли знайшла рішення. Якщо ви зробили математичний аналіз цієї проблеми, напевно, ви вже знаєте, що це не повинно було працювати; але мені якось пощастило, і програмі знадобилося лише хвилину, щоб знайти єдине рішення (те, що я розмістив вище). Повторні запуски програми часто займали цілих 10 або 20 хвилин, тому очевидно, це не було жорстким рішенням проблеми.

Я перейшов до рекурсивного рішення, яке повторювалось через кожну можливу перестановку головоломки, і відкинуло безліч розв’язків відразу, усунувши суми, які не були складені. Тобто, якщо перший рядок / стовпець, який я порівнював, вже не був рівним, я міг би негайно припинити перевірку цієї гілки, знаючи, що нічого іншого, що перебуває у головоломці, це не змінить.

Використовуючи цей алгоритм, я отримав перший «належний» успіх: програма могла генерувати і виплюнути всі 33 608 рішень за 5 хвилин.

Мій менеджер мав інший підхід: знаючи на основі моєї роботи, що єдино можливі рішення мають суми 27, 28, 29, 30, 31 або 32, він написав багатопотокове рішення, яке перевіряло можливі суми лише для тих конкретних значень. Йому вдалося запустити свою програму лише за 2 хвилини. Тож я повторив ще раз; Я хеширував усі можливі 3/4-значні суми (на початку програми; вона рахується в загальній тривалості виконання) і використовував "часткову суму" рядка для пошуку залишкового значення на основі раніше завершеного рядка, а не випробувавши всі залишилися значення і звів час до 72 секунд. Тоді, з деякою логікою багатопотокової передачі, я знизив її до 40 секунд. Мій менеджер взяв програму додому, провів оптимізацію роботи програми та знизив її до 12 секунд. Я упорядкував оцінку рядків і стовпців,

Найшвидший з нас отримав наші програми через місяць, для мого менеджера - 0,15 секунди, а для мене - 0,33 секунди. Я, нарешті, стверджував, що моя програма була швидшою, оскільки програма мого менеджера, поки вона знайшла всі рішення, не надрукувала їх у текстовий файл. Якщо він додав таку логіку до свого коду, це часто займало 0,4-0,5 секунди.

З тих пір ми дозволили втриматись всередині особистого виклику, але, звичайно, залишається питання: чи можна цю програму зробити швидше?

Це виклик, який я буду ставити перед вами, хлопці.

Ваш виклик

Параметри, за якими ми працювали, зменшили правило "сума 29", а натомість "всі рядки / стовпці" сум рівні ", і я збираюся встановити це правило і для вас, хлопці. Завдання, таким чином, є: Написати програму, яка знайде (і надрукує!) Всі рішення цієї загадки в найкоротші терміни. Я збираюся встановити межу для поданих рішень: Якщо програма займає більше ніж 10 секунд на порівняно пристойному комп'ютері (<8 років), це, мабуть, занадто повільно, щоб рахувати.

Також у мене є кілька бонусів за головоломку:

  • Чи можете ви узагальнити рішення так, щоб воно працювало для будь-якого набору з 16 чисел, а не тільки int[1,16]? Оцінка часу визначається на основі оригінального набору номерів підказки, але передається через цей кодовий шлях. (-10%)
  • Чи можете ви написати код таким чином, щоб він граціозно обробляв і вирішував дублікати цифр? Це не так просто, як може здатися! Рішення, які "візуально ідентичні", повинні бути унікальними в наборі результатів. (-5%)
  • Чи можете ви впоратися з негативними цифрами? (-5%)

Ви також можете спробувати створити рішення, яке обробляє номери з плаваючою точкою, але, звичайно, не шокуйтеся, якщо це не вдасться до кінця. Якщо ви все-таки знайдете надійне рішення, це, можливо, коштує великого бонусу!

За всіма намірами та цілями "Повороти" вважаються унікальними рішеннями. Тож рішення, яке є лише обертанням іншого рішення, вважається власним рішенням.

Ідентифікатор IDE, на якому я працюю на своєму комп’ютері, - це Java та C ++. Я можу приймати відповіді з інших мов, але вам може знадобитися надати посилання на те, де я можу отримати просте налаштування середовища виконання для вашого коду.


3
Святі коти, приємне перше запитання! ... окрім бонусів, які ми сортуємо-відштовхуємо (здебільшого з питань коду-гольфу , тож їм тут слід добре)
кіт

4
@cat Я думаю, що бонуси тут мають сенс, тому що коли я вирішував ці проблеми у власному коді, вони мали тенденцію спричиняти значне уповільнення коду. Тому я думаю, що бонус є для того, щоб виправдати включення.
Xirema

2
До речі, чи є у вас робота? це здається, що у вас легкий бос і багато часу на руках :-)
Level River St

1
З дублюючими номерами, чи добре друкувати дублюючі рішення, де обмінюються два однакових числа? це мало б велике значення щодо того, як трактується цей бонус. Будь ласка, уточніть, але я б розглядав можливість виключити цей бонус.
Рівень річки Св

1
Крім того, чи вважаються обертання на 180 градусів одним і тим же рішенням чи різними рішеннями?
Річка Рівня Св

Відповіді:


7

C - близько 0,5 сек

Ця дуже наївна програма дає всі рішення за півсекунди на моєму 4-річному ноутбуці. Ніякої багатопотоковості, жодного хешування.

Windows 10, Visual Studio 2010, ядро ​​процесора I7 64 біт

Спробуйте в Інтернеті на 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);
}

Святий солодкий Злий вампірський Ісус, який вклався в петлі. Б'юсь об заклад, що зробив ваш компілятор дуже щасливим. XD
Xirema

@ edc65, FYI ви можете замінити int inuse[16];на справедливі, int inuse;а потім використовувати побітові оператори для маніпулювання цим. Схоже , настільки не збільшує швидкість , але це трохи допомагає.
Ендрю Епштейн

@AndrewEpstein це може стати навіть повільніше - bitshift vs indexing
edc65

@ edc65, я взяв на себе сміливість використовувати гантелі, щоб перевірити свою оригінальну версію проти версії bitshift . Ось результати: Індексація: 0,2253 +/- 5,7590e-05 Бітсіфтінг: 0,2093 +/- 6,6595e-05 Отже, приблизно на 16 м прискорення роботи на моїй машині. Я використовував команду:dumbbench --precision=.01 -vvv --initial=500 ./solve
Ендрю Епштейн

3

C ++ - 300 мілісекунд

На запит я додав власний код для вирішення цієї загадки. На моєму комп’ютері він працює в середньому на 0,310 секунд (310 мілісекунд), але залежно від дисперсії може працювати так само швидко, як 287 мілісекунд. Я дуже рідко бачу, як він піднімається вище 350 мілісекунд, як правило, лише якщо моя система забита іншим завданням.

Ці часи базуються на самозвіті, який використовується у програмі, але я також перевірив зовнішній таймер та отримав подібні результати. Накладні витрати на програму, здається, додають приблизно 10 мілісекунд.

Крім того, мій код не дуже правильно обробляє дублікати. Він може вирішувати їх використання, але це не усуває "візуально ідентичні" рішення з набору рішень.

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

Як я впевнений, ви знаєте, що якщо ви трохи спростите свій вихід, ви можете поголити пристойну кількість часу. Ось час для вашого коду: 0.1038s +/- 0.0002 І ось час для вашого коду зі спрощеним виведенням. 0.0850s +/- 0.0001 Отже, ви можете заощадити ~ 18 мс, принаймні на моїй машині. Я запускав обидві версії 500+ разів із викинутими нападаючими людьми
Ендрю Епштейн

1

Пролог - 3 хвилини

Цей пазл здається ідеальним випадком використання для Prolog. Отже, я зашифрував рішення в Prolog! Ось:

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

На жаль, це не так швидко, як я очікував. Можливо, хтось більш добре розбирається в декларативному програмуванні (або конкретно в Prolog) може запропонувати кілька порад щодо оптимізації. Ви можете викликати правило puzzleза допомогою наступної команди:

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

Спробуйте його онлайн тут . Ви можете замінити будь-яке число замість 29s у коді, щоб генерувати всі рішення. Як відомо, всі 29 рішень знаходять приблизно за 30 секунд, тому знайти всі можливі рішення слід приблизно за 3 хвилини.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.