ค้นหาวิธีแก้ปัญหาทั้งหมดของปริศนาตัวเลขนี้ในเวลาที่สั้นที่สุด


16

ประวัติศาสตร์

บริษัท ของฉันส่งจดหมายข่าวรายสัปดาห์ให้กับทุกคนภายใน บริษัท รวมอยู่ในจดหมายข่าวเหล่านี้เป็นปริศนาพร้อมกับตะโกนให้คนใน บริษัท เป็นคนแรกที่ส่งอีเมล / ให้บริการแก้ปัญหาลับสมองของสัปดาห์ก่อน ปริศนาเหล่านี้ส่วนใหญ่ค่อนข้างน่าเบื่อและค่อนข้างน่าเบื่อสำหรับ บริษัท เทคโนโลยี แต่มีหนึ่งเดือนหลายเดือนหลังที่ดึงดูดความสนใจของฉัน

ปริศนาดั้งเดิม:

รับรูปร่างด้านล่าง:

ภาพปริศนา

คุณมีตัวเลขธรรมชาติตั้งแต่ 1 ถึง 16 พอดีกับพวกเขาทั้งหมดเป็นรูปร่างนี้เช่นที่แถวที่ต่อเนื่องกันและคอลัมน์ที่อยู่ติดกันรวมกันได้ถึง 29

ตัวอย่างเช่นทางออกหนึ่งของปริศนานี้ (ซึ่งเป็นโซลูชัน "บัญญัติ" ที่ฉันส่งไปยังจดหมายข่าว) มีดังต่อไปนี้:

แก้ไขภาพปริศนา

อย่างไรก็ตามในการแก้ปัญหาฉันพบข้อมูลที่น่าสนใจบ้าง:

  • มีวิธีแก้ปัญหามากกว่าแค่นั้น ในความเป็นจริงมี 9,368 โซลูชั่น
  • หากคุณขยายชุดกฎเพื่อกำหนดให้แถวและคอลัมน์เท่ากับกันไม่จำเป็นต้องเป็น 29 คุณจะได้รับ 33,608 โซลูชัน:
    • 4,440 โซลูชั่นรวมเป็น 27
    • 7,400 โซลูชั่นรวมเป็น 28
    • 9,368 โซลูชั่นรวมเป็น 29
    • 6,096 โซลูชั่นรวมเป็น 30
    • 5,104 โซลูชั่นรวมเป็น 31
    • 1,200 โซลูชั่นรวมเป็น 32

ดังนั้นฉันและเพื่อนร่วมงานของฉัน (แต่ส่วนใหญ่เป็นเพียงผู้จัดการของฉันเนื่องจากเขาเป็นคนอื่นที่ไม่ใช่ฉันด้วยทักษะการเขียนโปรแกรม "จุดประสงค์ทั่วไป") ที่กำหนดไว้ในความท้าทายซึ่งกินเวลาเกือบทั้งเดือน - เรามีงานอื่น ๆ ภาระผูกพันที่เกี่ยวข้องที่เราต้องเข้าร่วม - เพื่อพยายามเขียนโปรแกรมซึ่งจะพบทุกวิธีการแก้ปัญหาในวิธีที่เร็วที่สุดที่เป็นไปได้

สถิติดั้งเดิม

โปรแกรมแรกที่ฉันเขียนเพื่อแก้ปัญหาเพียงตรวจสอบการแก้ปัญหาแบบสุ่มซ้ำแล้วซ้ำอีกและหยุดเมื่อพบวิธีแก้ปัญหา หากคุณทำการวิเคราะห์ทางคณิตศาสตร์เกี่ยวกับปัญหานี้แล้วคุณอาจรู้อยู่แล้วว่านี่ไม่ได้ผล แต่อย่างใดฉันก็โชคดีและใช้เวลาเพียงหนึ่งนาทีในการค้นหาโซลูชันเดียว (อันที่ฉันโพสต์ไว้ด้านบน) การรันซ้ำของโปรแกรมมักใช้เวลามากถึง 10 หรือ 20 นาทีดังนั้นแน่นอนว่านี่ไม่ใช่วิธีการแก้ปัญหาที่เข้มงวด

ฉันเปลี่ยนไปใช้ Recursive Solution ซึ่งวนซ้ำไปตามการเปลี่ยนแปลงที่เป็นไปได้ของปริศนาและทิ้งโซลูชันจำนวนมากในคราวเดียวโดยกำจัดจำนวนเงินที่ไม่ได้เพิ่มขึ้น IE ถ้าแถว / คอลัมน์แรกที่ฉันเปรียบเทียบแล้วไม่เท่ากันฉันสามารถหยุดตรวจสอบสาขานั้นได้ทันทีโดยรู้ว่าไม่มีอะไรอื่นที่เกี่ยวข้องกับตัวต่อปริศนาจะเปลี่ยนสิ่งนั้น

การใช้อัลกอริทึมนี้ฉันได้รับความสำเร็จ "เหมาะสม" ครั้งแรก: โปรแกรมสามารถสร้างและคายโซลูชั่น 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]หรือ? คะแนนเวลาจะได้รับการประเมินตามชุดหมายเลขพรอมต์ต้นฉบับ แต่ส่งผ่าน codepath นี้ (-10%)
  • คุณสามารถเขียนรหัสในแบบที่มันจัดการและแก้ไขด้วยตัวเลขที่ซ้ำกันได้หรือไม่? มันไม่ตรงไปตรงมาอย่างที่คิด! โซลูชันที่ "เหมือนกันทางสายตา" ควรมีลักษณะเฉพาะในชุดผลลัพธ์ (-5%)
  • คุณสามารถจัดการกับจำนวนลบได้หรือไม่? (-5%)

คุณสามารถลองสร้างวิธีแก้ปัญหาที่จัดการกับตัวเลขจุดลอยตัวได้ แต่แน่นอนว่าอย่าตกใจถ้ามันล้มเหลวอย่างสิ้นเชิง หากคุณพบทางออกที่แข็งแกร่ง แต่นั่นอาจจะคุ้มค่าโบนัสมาก!

สำหรับทุกเจตนารมณ์และวัตถุประสงค์ "การหมุนเวียน" ถือเป็นโซลูชันที่ไม่ซ้ำใคร ดังนั้นการแก้ปัญหาที่เป็นเพียงการหมุนรอบของการแก้ปัญหาที่แตกต่างกันจึงนับเป็นทางออกของตัวเอง

IDEs ที่ฉันใช้กับคอมพิวเตอร์คือ Java และ C ++ ฉันสามารถรับคำตอบจากภาษาอื่น ๆ ได้ แต่คุณอาจต้องระบุลิงก์ไปยังที่ที่ฉันสามารถรับสภาพแวดล้อมรันไทม์ที่ติดตั้งง่ายสำหรับรหัสของคุณ


3
แมวศักดิ์สิทธิ์คำถามแรกดี! ... ยกเว้นโบนัสซึ่งเราแยกแยะท้อ (ส่วนใหญ่เกี่ยวกับคำถามรหัสกอล์ฟดังนั้นพวกเขาควรจะปรับได้ที่นี่)
cat

4
@cat ฉันคิดว่าโบนัสมีเหตุผลที่นี่เพราะเมื่อฉันแก้ไขปัญหาเหล่านั้นด้วยรหัสของฉันเองพวกเขามักจะทำให้รหัสช้าลงอย่างมีนัยสำคัญ ดังนั้นฉันคิดว่าโบนัสคือการพิสูจน์ให้เห็นถึงการรวม
Xirema

2
BTW มีงานใดที่เกิดขึ้นในสถานที่ของคุณ? ดูเหมือนว่าคุณจะมีเจ้านายที่ไม่พิถีพิถันและมีเวลาเหลือเฟือในมือของคุณ :-)
Level River St

1
ด้วยตัวเลขที่ซ้ำกันจะสามารถพิมพ์โซลูชันที่ซ้ำกันซึ่งมีการแลกเปลี่ยนตัวเลขที่เหมือนกันสองหมายเลขได้หรือไม่? ซึ่งจะสร้างความแตกต่างอย่างมากกับการตีความโบนัสนั้น โปรดอธิบาย แต่ฉันจะพิจารณากำจัดโบนัสนั้นโดยสิ้นเชิง
Level River St

1
นอกจากนี้การหมุน 180 องศาถือว่าเป็นคำตอบเดียวกันหรือต่างกัน?
เลเวลริเวอร์เซนต์

คำตอบ:


7

C - ใกล้ 0.5 วินาที

โปรแกรมที่ไร้เดียงสานี้ให้คำตอบทั้งหมดในครึ่งวินาทีกับแล็ปท็อปอายุ 4 ปีของฉัน ไม่มีมัลติเธรดไม่มีการขัดจังหวะ

Windows 10, Visual Studio 2010, CPU core 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);
}

Holy Vampiric Jesus อันศักดิ์สิทธิ์ที่แสนหวาน ฉันเดิมพันที่ทำให้คอมไพเลอร์ของคุณมีความสุขจริงๆ XD
Xirema

@ edc65, FYI คุณสามารถแทนที่int inuse[16];ด้วย just int inuse;และจากนั้นใช้ตัวดำเนินการระดับบิตเพื่อจัดการ มันไม่ได้ดูเหมือนจะเพิ่มความเร็วที่มาก แต่มันจะช่วยให้เล็ก ๆ น้อย ๆ
Andrew Epstein

@AndrewEpstein มันอาจช้าลงได้ - bitshift vs indexing
edc65

@ edc65 ฉันใช้เสรีภาพในการใช้dumbbenchเพื่อทดสอบเวอร์ชันดั้งเดิมของคุณเทียบกับรุ่น bithift นี่คือผลลัพธ์: การจัดทำดัชนี: 0.2253 +/- 5.7590e-05 Bitshifting: 0.2093 +/- 6.6595e-05 ดังนั้นความเร็วประมาณ 16ms บนเครื่องของฉัน คำสั่งที่ฉันใช้คือ:dumbbench --precision=.01 -vvv --initial=500 ./solve
Andrew Epstein

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 ดังนั้นคุณสามารถประหยัดอย่างน้อย 18ms บนเครื่องของฉัน ฉันวิ่งทั้งสองรุ่นมากกว่า 500 ครั้งโดยมีคนผิดโยนออกโดยใช้dumbbench
Andrew Epstein

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 นาที

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.