Tìm tất cả các giải pháp cho câu đố số này trong thời gian ngắn nhất có thể


16

Lịch sử

Công ty của tôi gửi một bản tin hàng tuần cho mọi người trong công ty. Bao gồm trong các bản tin này là một câu đố, cùng với một tiếng hét cho bất cứ ai trong công ty là người đầu tiên gửi email / cung cấp một giải pháp cho câu đố tuần trước. Hầu hết các câu đố này khá tầm thường, và thực sự khá buồn tẻ đối với một công ty công nghệ, nhưng có một, vài tháng trước, điều đó đã thu hút sự chú ý của tôi.

Câu đố gốc:

Cho hình dưới đây:

Câu đố hình ảnh

Bạn có các số tự nhiên từ 1 đến 16. Ghép tất cả chúng vào hình dạng này, sao cho tất cả các hàng liền kề và các cột liền kề có tổng số lên tới 29.

Ví dụ, một giải pháp như vậy cho câu đố này (đó là giải pháp "kinh điển" mà tôi đã gửi đến bản tin) là:

Giải câu đố hình ảnh

Tuy nhiên, trong quá trình giải quyết nó, tôi đã tìm thấy một số thông tin khá thú vị:

  • Có nhiều giải pháp hơn đáng kể so với chỉ có một; Trên thực tế, có 9.368 Giải pháp.
  • Nếu bạn mở rộng quy tắc để chỉ yêu cầu các hàng và cột bằng nhau, không nhất thiết phải là 29, bạn sẽ nhận được 33.608 giải pháp:
    • 4.440 Giải pháp cho tổng số 27.
    • 7.400 Giải pháp cho tổng số 28.
    • 9.368 Giải pháp cho tổng số 29.
    • 6.096 Giải pháp cho tổng số 30.
    • 5.1104 Giải pháp cho tổng số 31.
    • 1.200 Giải pháp cho tổng số 32.

Vì vậy, tôi và đồng nghiệp của tôi (mặc dù hầu hết chỉ là người quản lý của tôi, vì anh ta là người duy nhất ngoài tôi có kỹ năng lập trình "Mục đích chung") đã đặt ra một thách thức, kéo dài trong hầu hết các tháng mà chúng tôi có, công việc thực tế khác- nghĩa vụ liên quan, chúng tôi đã phải tham dự vào chương trình cố gắng để viết một chương trình tìm mọi giải pháp một cách nhanh nhất có thể.

Thống kê gốc

Chương trình đầu tiên tôi viết để giải quyết vấn đề chỉ đơn giản là kiểm tra các giải pháp ngẫu nhiên lặp đi lặp lại và dừng lại khi tìm thấy giải pháp. Nếu bạn đã thực hiện phân tích toán học về vấn đề này, có lẽ bạn đã biết rằng điều này không nên làm việc; nhưng bằng cách nào đó tôi đã gặp may, và chương trình chỉ mất một phút để tìm ra một giải pháp duy nhất (giải pháp tôi đã đăng ở trên). Các lần chạy lặp lại của chương trình thường mất tới 10 hoặc 20 phút, vì vậy rõ ràng đây không phải là một giải pháp nghiêm ngặt cho vấn đề.

Tôi đã chuyển sang một Giải pháp đệ quy lặp đi lặp lại qua mọi hoán vị có thể có của câu đố và loại bỏ rất nhiều giải pháp cùng một lúc bằng cách loại bỏ các khoản tiền không cộng dồn. IE nếu hàng / cột đầu tiên tôi so sánh đã không bằng nhau, tôi có thể dừng kiểm tra nhánh đó ngay lập tức, biết rằng không có gì khác thấm vào câu đố sẽ thay đổi điều đó.

Sử dụng thuật toán này, tôi đã có được thành công "đúng đắn" đầu tiên: chương trình có thể tạo và phun ra tất cả 33.608 giải pháp trong khoảng 5 phút.

Người quản lý của tôi đã có một cách tiếp cận khác: dựa trên công việc của tôi rằng các giải pháp khả thi duy nhất có tổng số 27, 28, 29, 30, 31 hoặc 32, ông đã viết một giải pháp đa luồng chỉ kiểm tra các khoản tiền có thể có cho các giá trị cụ thể đó. Anh quản lý để có được chương trình của mình để chạy chỉ trong 2 phút. Vì vậy, tôi lặp lại một lần nữa; Tôi đã băm tất cả các khoản tiền 3/4 chữ số có thể (khi bắt đầu chương trình; nó được tính trong tổng thời gian chạy) và sử dụng "tổng một phần" của một hàng để tra cứu giá trị còn lại dựa trên một hàng đã hoàn thành trước đó, thay vì kiểm tra tất cả các giá trị còn lại và giảm thời gian xuống còn 72 giây. Sau đó, với một số logic đa luồng, tôi đã giảm xuống còn 40 giây. Người quản lý của tôi đã mang chương trình về nhà, thực hiện một số tối ưu hóa về cách chương trình chạy và giảm xuống còn 12 giây. Tôi sắp xếp lại việc đánh giá các hàng và cột,

Người nhanh nhất trong chúng tôi nhận được các chương trình của mình sau một tháng là 0,15 giây cho người quản lý của tôi và 0,33 giây cho tôi. Cuối cùng tôi đã tuyên bố rằng chương trình của tôi nhanh hơn, vì chương trình của người quản lý của tôi, trong khi nó đã tìm thấy tất cả các giải pháp, đã không in chúng ra thành một tệp văn bản. Nếu anh ta thêm logic đó vào mã của mình, nó thường mất tới 0,4-0,5 giây.

Kể từ đó, chúng tôi đã cho phép thử thách nội bộ của mình tồn tại, nhưng tất nhiên, câu hỏi vẫn là: có thể chương trình này được thực hiện nhanh hơn không?

Đó là thử thách tôi sẽ đặt ra cho các bạn.

Thử thách của bạn

Các tham số mà chúng tôi đã làm việc theo quy tắc "tổng 29" thay vào đó là "tổng của tất cả các hàng / cột" và tôi cũng sẽ đặt quy tắc đó cho các bạn. Do đó, Thách thức là: Viết chương trình tìm (và In!) Tất cả các giải pháp cho câu đố này trong thời gian ngắn nhất có thể. Tôi sẽ đặt trần cho các giải pháp đã gửi: Nếu chương trình mất hơn 10 giây trên một máy tính tương đối tốt (<8 tuổi), có lẽ quá chậm để tính.

Ngoài ra, tôi có một vài Phần thưởng cho câu đố:

  • Bạn có thể khái quát hóa giải pháp để nó hoạt động cho bất kỳ bộ 16 số nào không, không chỉ int[1,16] ? Điểm thời gian sẽ được đánh giá dựa trên bộ số nhắc nhở ban đầu, nhưng được chuyển qua bộ mã này. (-10%)
  • Bạn có thể viết mã theo cách mà nó xử lý một cách duyên dáng và giải quyết với các số trùng lặp không? Điều này không đơn giản như nó có vẻ! Các giải pháp "giống hệt trực quan" phải là duy nhất trong tập kết quả. (-5%)
  • Bạn có thể xử lý số âm? (-5%)

Bạn cũng có thể thử tạo một giải pháp xử lý Số dấu phẩy động, nhưng tất nhiên, đừng bị sốc nếu thất bại hoàn toàn. Nếu bạn tìm thấy một giải pháp mạnh mẽ, đó có thể là một phần thưởng lớn!

Đối với tất cả ý định và mục đích, "Xoay vòng" được coi là giải pháp duy nhất. Vì vậy, một giải pháp chỉ là một vòng quay của một giải pháp khác được tính là giải pháp của chính nó.

Các IDE tôi đã làm việc trên máy tính của mình là Java và C ++. Tôi có thể chấp nhận câu trả lời từ các ngôn ngữ khác, nhưng bạn cũng có thể cần cung cấp một liên kết đến nơi tôi có thể có được môi trường thời gian chạy dễ cài đặt cho mã của bạn.


3
Mèo thần thánh, câu hỏi đầu tiên tốt đẹp! ... ngoại trừ những phần thưởng mà chúng tôi không khuyến khích (chủ yếu là về các câu hỏi về môn đánh gôn , vì vậy chúng sẽ ổn ở đây)
mèo

4
@cat Tôi nghĩ rằng tiền thưởng có ý nghĩa ở đây, bởi vì khi tôi giải quyết những vấn đề đó trong mã của riêng tôi, họ có xu hướng làm cho mã chậm lại một cách đáng kể. Vì vậy, tôi nghĩ rằng một phần thưởng là để biện minh cho sự bao gồm.
Xirema

2
BTW, có công việc nào đang diễn ra tại chỗ của bạn không? Có vẻ như bạn có một ông chủ dễ tính và có nhiều thời gian trong tay :-)
Level River St

1
Với các số trùng lặp, bạn có thể in các giải pháp trùng lặp trong đó hai số giống nhau được trao đổi không? điều đó sẽ tạo ra một sự khác biệt lớn như cách giải thích về phần thưởng đó. Vui lòng làm rõ, nhưng tôi sẽ xem xét loại bỏ hoàn toàn phần thưởng đó.
Cấp sông St

1
Ngoài ra, xoay 180 độ được coi là cùng một giải pháp hoặc các giải pháp khác nhau?
Cấp sông St

Câu trả lời:


7

C - gần 0,5 giây

Chương trình rất ngây thơ này cung cấp tất cả các giải pháp trong nửa giây trên máy tính xách tay 4 tuổi của tôi. Không đa luồng, không băm.

Windows 10, Visual Studio 2010, lõi CPU I7 64 bit

Thử trực tuyến trên 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);
}

Chúa Giêsu ma quỷ ngọt ngào, những người làm tổ cho các vòng lặp. Tôi cá rằng điều đó làm cho trình biên dịch của bạn thực sự hạnh phúc mặc dù. XD
Xirema

@ edc65, FYI bạn có thể thay thế int inuse[16];bằng chỉ int inuse;và sau đó sử dụng các toán tử bitwise để thao tác nó. Nó dường như không để tăng tốc độ nhiều, nhưng nó sẽ giúp một chút.
Andrew Epstein

@AndrewEpstein nó thậm chí có thể trở nên chậm hơn - bitshift so với lập chỉ mục
edc65

@ edc65, tôi đã tự do sử dụng dumbbench để kiểm tra phiên bản gốc của bạn so với phiên bản bithift . Đây là kết quả: Lập chỉ mục: 0.2253 +/- 5.7590e-05 Bitshifting: 0.2093 +/- 6.6595e-05 Vì vậy, tốc độ tăng tốc khoảng 16ms trên máy của tôi. Lệnh tôi đã sử dụng là:dumbbench --precision=.01 -vvv --initial=500 ./solve
Andrew Epstein

3

C ++ - 300 mili giây

Theo yêu cầu, tôi đã thêm mã của riêng mình để giải câu đố này. Trên máy tính của tôi, nó có tốc độ trung bình là 0,310 giây (310 mili giây) nhưng tùy thuộc vào phương sai có thể chạy nhanh tới 287 mili giây. Tôi rất hiếm khi thấy nó tăng lên trên 350 mili giây, thường chỉ khi hệ thống của tôi bị sa lầy với một nhiệm vụ khác.

Những lần này dựa trên tự báo cáo được sử dụng trong chương trình, nhưng tôi cũng đã thử nghiệm bằng cách sử dụng bộ hẹn giờ bên ngoài và nhận được kết quả tương tự. Chi phí trong chương trình dường như thêm khoảng 10 mili giây.

Ngoài ra, mã của tôi không khá xử lý các bản sao chính xác. Nó có thể giải quyết bằng cách sử dụng chúng, nhưng nó không loại bỏ các giải pháp "giống hệt trực quan" khỏi bộ giải pháp.

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

Như tôi chắc chắn rằng bạn biết, nếu bạn đơn giản hóa đầu ra của mình một chút, bạn có thể loại bỏ một khoảng thời gian kha khá. Đây là thời gian cho mã của bạn: 0.1038s +/- 0.0002 Và đây là thời gian cho mã của bạn với đầu ra được đơn giản hóa: 0.0850s +/- 0.0001 Vì vậy, bạn có thể tiết kiệm ~ 18ms, ít nhất là trên máy của tôi. Tôi đã chạy cả hai phiên bản hơn 500 lần với các ngoại lệ bị ném ra ngoài, sử dụng dumbbench
Andrew Epstein

1

Prolog - 3 phút

Loại câu đố này có vẻ như là một trường hợp sử dụng hoàn hảo cho Prolog. Vì vậy, tôi đã mã hóa một giải pháp trong Prolog! Đây là:

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

Thật không may, nó không nhanh như tôi mong đợi. Có lẽ ai đó thành thạo hơn trong lập trình khai báo (hoặc cụ thể là Prolog) có thể cung cấp một số mẹo tối ưu hóa. Bạn có thể gọi quy tắc puzzlebằng lệnh sau:

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

Dùng thử trực tuyến tại đây . Bạn có thể thay thế bất kỳ số nào thay cho 29s trong mã để tạo tất cả các giải pháp. Vì hiện tại, tất cả 29 giải pháp được tìm thấy trong khoảng 30 giây, vì vậy để tìm tất cả các giải pháp có thể nên có khoảng 3 phút.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.