Temukan semua Solusi untuk Puzzle Nomor ini dalam Waktu sesingkat Mungkin

16

Sejarah

Perusahaan saya mengirimkan buletin mingguan kepada semua orang di dalam perusahaan. Termasuk dalam buletin ini adalah teka-teki, bersama dengan teriakan kepada siapa pun di perusahaan adalah orang pertama yang mengirim email / memberikan solusi untuk teka-teki minggu lalu. Sebagian besar teka-teki ini cukup sepele, dan sejujurnya cukup membosankan untuk sebuah perusahaan teknologi, tetapi ada satu, beberapa bulan yang lalu, yang menarik perhatian saya.

Teka-teki Asli:

Diberikan bentuk di bawah ini:

Gambar Puzzle

Anda memiliki Bilangan Alami dari 1 hingga 16. Paskan semuanya ke dalam bentuk ini, sehingga semua baris dan kolom yang berdekatan berjumlah hingga 29.

Misalnya, salah satu solusi untuk teka-teki ini (yang merupakan solusi "kanonik" yang saya kirimkan ke buletin) adalah sebagai berikut:

Gambar Puzzle Terpecahkan

Namun, dalam penyelesaiannya, saya menemukan beberapa informasi yang agak menarik:

  • Ada jauh lebih banyak solusi daripada hanya satu itu; sebenarnya, ada 9.368 Solusi.
  • Jika Anda memperluas aturan hanya mensyaratkan bahwa baris dan kolom sama satu sama lain, belum tentu 29, Anda mendapatkan 33.608 solusi:
    • 4.440 Solusi dengan jumlah 27.
    • 7.400 Solusi dengan jumlah 28.
    • 9.368 Solusi dengan jumlah 29.
    • 6.096 Solusi dengan jumlah 30.
    • 5.104 Solusi dengan jumlah 31.
    • 1.200 Solusi dengan jumlah 32.

Jadi saya dan rekan kerja saya (meskipun sebagian besar hanya manajer saya, karena ia adalah satu-satunya orang selain saya yang memiliki keterampilan pemrograman "Tujuan Umum") memulai sebuah tantangan, yang berlangsung hampir sepanjang bulan — kami memiliki pekerjaan lain yang sebenarnya - kewajiban terkait yang harus kami hadiri — untuk mencoba menulis sebuah program yang akan menemukan setiap solusi dengan cara tercepat yang mungkin.

Statistik Asli

Program pertama yang saya tulis untuk memecahkan masalah cukup memeriksa solusi acak berulang-ulang, dan berhenti ketika menemukan solusi. Jika Anda sudah melakukan analisis matematika pada masalah ini, Anda mungkin sudah tahu bahwa ini seharusnya tidak berhasil; tapi entah bagaimana saya beruntung, dan butuh program hanya satu menit untuk menemukan solusi tunggal (yang saya posting di atas). Pengulangan program seringkali memakan waktu 10 atau 20 menit, jadi jelas ini bukan solusi yang tepat untuk masalah ini.

Saya beralih ke Solusi Rekursif yang berulang melalui setiap permutasi yang mungkin dari teka-teki, dan membuang banyak solusi sekaligus dengan menghilangkan jumlah yang tidak bertambah. Yaitu jika baris / kolom pertama yang saya bandingkan sudah tidak sama, saya bisa berhenti memeriksa cabang itu segera, mengetahui bahwa tidak ada hal lain yang dimasukkan ke dalam puzzle akan mengubah itu.

Dengan menggunakan algoritma ini, saya mendapatkan kesuksesan "layak" pertama: program ini dapat menghasilkan dan mengeluarkan semua 33.608 solusi dalam waktu sekitar 5 menit.

Manajer saya memiliki pendekatan yang berbeda: mengetahui berdasarkan pekerjaan saya bahwa satu-satunya solusi yang mungkin memiliki jumlah 27, 28, 29, 30, 31, atau 32, ia menulis solusi multi-utas yang memeriksa jumlah yang mungkin hanya untuk nilai-nilai spesifik tersebut. Dia berhasil menjalankan programnya hanya dalam 2 menit. Jadi saya mengulangi lagi; Saya hash semua kemungkinan jumlah 3/4 digit (pada awal program; ini dihitung dalam runtime total) dan menggunakan "jumlah parsial" dari sebuah baris untuk mencari nilai yang tersisa berdasarkan pada baris yang sebelumnya diselesaikan, daripada menguji semua nilai yang tersisa, dan membawa waktu ke 72 detik. Kemudian dengan beberapa logika multi-threading, saya mendapatkannya hingga 40 detik. Manajer saya membawa pulang program, melakukan beberapa optimasi tentang bagaimana program berjalan, dan menurunkannya menjadi 12 detik. Saya memesan kembali evaluasi baris dan kolom,

Yang tercepat dari kami mendapatkan program kami setelah sebulan adalah 0,15 detik untuk manajer saya, dan 0,33 detik untuk saya. Saya akhirnya mengklaim bahwa program saya lebih cepat, karena program manajer saya, sementara itu menemukan semua solusi, tidak mencetaknya ke dalam file teks. Jika dia menambahkan logika itu ke kodenya, seringkali butuh waktu 0,4-0,5 detik.

Sejak itu kami membiarkan tantangan intra-pribadi kami bertahan, tetapi tentu saja, pertanyaannya tetap: bisakah program ini dibuat lebih cepat?

Itulah tantangan yang akan saya ajukan kepada kalian.

Tantangan Anda

Parameter yang kami kerjakan melonggarkan aturan "jumlah 29" sebagai gantinya "jumlah semua baris / kolom sama", dan saya akan menetapkan aturan itu juga untuk kalian. Tantangannya, oleh karena itu, adalah: Menulis program yang menemukan (dan Mencetak!) Semua solusi untuk teka-teki ini dalam waktu sesingkat mungkin. Saya akan menetapkan batas pada solusi yang diajukan: Jika program membutuhkan lebih dari 10 detik pada komputer yang relatif baik (<8 tahun), mungkin terlalu lambat untuk dihitung.

Juga, saya punya beberapa Bonus untuk puzzle:

  • Bisakah Anda menggeneralisasi solusi sehingga berfungsi untuk set angka 16, bukan hanya int[1,16]? Skor waktu akan dievaluasi berdasarkan set angka prompt yang asli, tetapi melewati codepath ini. (-10%)
  • Bisakah Anda menulis kode dengan cara yang anggun menangani dan menyelesaikan dengan angka duplikat? Ini tidak semudah kelihatannya! Solusi yang "identik secara visual" harus unik dalam set hasil. (-5%)
  • Bisakah Anda menangani angka negatif? (-5%)

Anda juga dapat mencoba menghasilkan solusi yang menangani Angka Titik Apung, tetapi tentu saja, jangan kaget jika itu gagal total. Jika Anda menemukan solusi yang kuat, itu mungkin bernilai bonus besar!

Untuk semua maksud dan tujuan, "Rotasi" dianggap sebagai solusi unik. Jadi solusi yang hanya merupakan rotasi dari solusi yang berbeda dianggap sebagai solusi sendiri.

IDE yang saya kerjakan di komputer saya adalah Java dan C ++. Saya dapat menerima jawaban dari bahasa lain, tetapi Anda mungkin juga perlu memberikan tautan ke tempat saya bisa mendapatkan lingkungan runtime yang mudah diatur untuk kode Anda.

Xirema
sumber
3
Kucing suci, pertanyaan pertama yang bagus! ... kecuali untuk bonus, yang kami anggap kecil hati (kebanyakan pada pertanyaan kode-golf , jadi mereka harusnya baik-baik saja di sini)
cat
4
@cat Saya pikir bonus masuk akal di sini, karena ketika saya memecahkan masalah-masalah dalam kode saya sendiri, mereka cenderung menyebabkan kode melambat secara signifikan. Jadi saya pikir bonus adalah untuk membenarkan inklusi.
Xirema
2
BTW, apakah ada pekerjaan di tempat Anda? sepertinya Anda memiliki atasan yang santai dan banyak waktu di tangan Anda :-)
Level River St
1
Dengan nomor duplikat, apakah boleh mencetak solusi duplikat di mana dua nomor yang sama dipertukarkan? itu akan membuat perbedaan besar tentang bagaimana bonus itu ditafsirkan. Tolong jelaskan, tapi saya akan mempertimbangkan untuk menghilangkan bonus itu sama sekali.
Level River St
1
Juga, apakah rotasi 180 derajat dianggap sebagai solusi yang sama atau solusi yang berbeda?
Level River St

Jawaban:

7

C - hampir 0,5 detik

Program yang sangat naif ini memberikan semua solusi dalam setengah detik pada laptop saya yang berumur 4 tahun. Tidak ada multithread, tidak ada hashing.

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

Coba online di 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
sumber
Ya Tuhan, Vampiric Evil Sweet manis, mereka bersarang untuk loop. Saya yakin itu membuat kompiler Anda benar-benar bahagia. XD
Xirema
@ edc65, FYI bisa Anda ganti int inuse[16];dengan adil int inuse;lalu gunakan operator bitwise untuk memanipulasinya. Ini tampaknya tidak meningkatkan kecepatan yang banyak, tapi membantu sedikit a.
Andrew Epstein
@AndrewEpstein bahkan bisa menjadi lebih lambat - bitshift vs pengindeksan
edc65
@ edc65, saya mengambil kebebasan menggunakan dumbbench untuk menguji versi asli Anda vs versi bitshift. Berikut adalah hasilnya: Pengindeksan: 0.2253 +/- 5.7590e-05 Bitshifting: 0.2093 +/- 6.6595e-05 Jadi, kira-kira speedup 16ms pada mesin saya. Perintah yang saya gunakan adalah:dumbbench --precision=.01 -vvv --initial=500 ./solve
Andrew Epstein
3

C ++ - 300 Milidetik

Per permintaan, saya telah menambahkan kode saya sendiri untuk menyelesaikan teka-teki ini. Di komputer saya, jam di rata-rata 0,310 detik (310 milidetik) tetapi tergantung pada varian dapat berjalan secepat 287 milidetik. Saya sangat jarang melihatnya naik di atas 350 milidetik, biasanya hanya jika sistem saya macet dengan tugas yang berbeda.

Waktu ini didasarkan pada pelaporan diri yang digunakan dalam program, tetapi saya juga diuji menggunakan pengatur waktu eksternal dan mendapatkan hasil yang serupa. Overhead dalam program ini sepertinya menambah sekitar 10 milidetik.

Juga, kode saya tidak cukup menangani duplikat dengan benar. Itu dapat menyelesaikan penggunaannya, tetapi tidak menghilangkan solusi "identik secara visual" dari set solusi.

#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
sumber
Karena saya yakin Anda sadar, jika Anda menyederhanakan output Anda sedikit, Anda dapat menghemat waktu yang cukup lama. Inilah waktu untuk kode Anda: 0.1038s +/- 0.0002 Dan inilah waktu untuk kode Anda dengan output yang disederhanakan: 0.0850s +/- 0.0001 Jadi, Anda dapat menyimpan ~ 18ms, setidaknya di komputer saya. Saya menjalankan kedua versi 500+ kali dengan outlier yang dibuang, menggunakan dumbbench
Andrew Epstein
1

Prolog - 3 menit

Teka-teki semacam ini tampaknya seperti kasus penggunaan yang sempurna untuk Prolog. Jadi, saya membuat solusi di Prolog! Ini dia:

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

Sayangnya, ini tidak secepat yang saya harapkan. Mungkin seseorang yang lebih berpengalaman dalam pemrograman deklaratif (atau Prolog khusus) dapat menawarkan beberapa tips pengoptimalan. Anda bisa menjalankan aturan puzzledengan perintah berikut:

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

Cobalah online di sini . Anda dapat mengganti angka apa saja dengan 29huruf s dalam kode untuk menghasilkan semua solusi. Seperti berdiri, semua 29 solusi ditemukan dalam waktu sekitar 30 detik, jadi untuk menemukan semua solusi yang mungkin harus sekitar 3 menit.

Andrew Epstein
sumber