สรุป: วิธีการใหม่ไม่มีวิธีแก้ปัญหาใหม่โปรแกรมที่น่าเล่นและผลลัพธ์ที่น่าสนใจของการแก้ปัญหาที่รู้จักในท้องถิ่น โอ้และการสังเกตที่มีประโยชน์โดยทั่วไป
เมื่อใช้วิธีการแบบSAT 
ฉันสามารถแก้ปัญหา 
ที่คล้ายกันอย่างสมบูรณ์
 สำหรับเขาวงกต 4x4 ที่มีเซลล์ที่ถูกบล็อกแทนผนังบางและตำแหน่งเริ่มต้นและทางออกคงที่ในมุมตรงข้าม ดังนั้นฉันหวังว่าจะสามารถใช้ความคิดเดียวกันกับปัญหานี้ได้ อย่างไรก็ตามแม้ว่าปัญหาอื่น ๆ ที่ฉันใช้เพียง 2423 เขาวงกต (ในขณะเดียวกันก็พบว่า 2,083 มีเพียงพอ) และมันมีวิธีแก้ปัญหาความยาว 29 การเข้ารหัส SAT ใช้ตัวแปรนับล้านและใช้เวลาหลายวัน
ดังนั้นฉันตัดสินใจที่จะเปลี่ยนวิธีการในสองวิธีที่สำคัญ:
- อย่ายืนยันในการค้นหาโซลูชันตั้งแต่เริ่มต้น แต่อนุญาตให้แก้ไขส่วนของสตริงโซลูชัน (นั่นเป็นเรื่องง่ายที่จะทำต่อไปโดยการเพิ่มส่วนคำสั่ง แต่โปรแกรมของฉันทำให้มันสะดวกสบายในการทำ)
- อย่าใช้เขาวงกตทั้งหมดตั้งแต่ต้น แทนที่จะเพิ่มเขาวงกตที่ยังไม่ได้แก้ไขทีละคน เขาวงกตบางคนอาจได้รับการแก้ไขโดยบังเอิญหรือพวกเขาจะได้รับการแก้ไขเมื่อสิ่งที่พิจารณาแล้วได้รับการแก้ไข ในกรณีหลังมันจะไม่ถูกเพิ่มเข้ามาโดยที่เราไม่จำเป็นต้องรู้ถึงความหมาย
ฉันยังทำการปรับให้เหมาะสมเพื่อใช้ตัวแปรน้อยลงและส่วนคำสั่งหน่วย
โปรแกรมจะขึ้นอยู่กับ @ orlp ของ การเปลี่ยนแปลงที่สำคัญคือการเลือกเขาวงกต:
- ประการแรกเขาจะได้รับจากโครงสร้างผนังและตำแหน่งเริ่มต้นเท่านั้น (พวกเขายังเก็บตำแหน่งที่เข้าถึงได้) ฟังก์ชั่นis_solutionตรวจสอบว่าตำแหน่งที่เข้าถึงได้ทั้งหมดถึงหรือไม่
- (ไม่เปลี่ยนแปลง: ยังคงไม่ใช้เขาวงกตที่มีตำแหน่งที่เข้าถึงได้เพียง 4 หรือน้อยกว่า แต่ส่วนใหญ่จะถูกโยนทิ้งไปโดยการสังเกตต่อไปนี้)
- หากเขาวงกตไม่ได้ใช้เซลล์ใด ๆ ของทั้งสามเซลล์มันจะเทียบเท่ากับเขาวงกตที่เลื่อนขึ้น ดังนั้นเราสามารถวางมันลง เช่นเดียวกับเขาวงกตที่ไม่ได้ใช้เซลล์ซ้ายสามเซลล์ใด ๆ
- ไม่สำคัญว่าจะเชื่อมต่อชิ้นส่วนที่ไม่สามารถเข้าถึงได้ดังนั้นเรายืนยันว่าแต่ละเซลล์ที่ไม่สามารถเข้าถึงได้นั้นล้อมรอบด้วยกำแพงอย่างสมบูรณ์
- เขาวงกตเส้นทางเดียวที่เป็น submaze ของเขาวงกตเส้นทางเดียวที่ใหญ่กว่าจะได้รับการแก้ไขเสมอเมื่อมีการแก้ไขที่ใหญ่กว่าดังนั้นเราจึงไม่ต้องการมัน เส้นทางเขาวงกตขนาดเดียวแต่ละเส้นทางที่มากที่สุด 7 เป็นส่วนหนึ่งของเส้นทางที่ใหญ่กว่า (ยังเหมาะสมใน 3x3) แต่มีเส้นทางเส้นทางเดียวขนาด 8 เขาวงกตที่ไม่ได้เป็น สำหรับ simpliciy เราแค่ปล่อย mazes ขนาดเดียวให้น้อยกว่า 8 (และฉันยังคงใช้เฉพาะจุดสุดขีดเท่านั้นที่ต้องพิจารณาในฐานะตำแหน่งเริ่มต้นตำแหน่งทั้งหมดถูกใช้เป็นตำแหน่งทางออกซึ่งสำคัญสำหรับส่วน SAT เท่านั้น ของโปรแกรม)
ด้วยวิธีนี้ฉันได้รับ 1,0772 เขาวงกตรวมกับตำแหน่งเริ่มต้น
นี่คือโปรแกรม:
#include <algorithm>
#include <array>
#include <bitset>
#include <cstring>
#include <iostream>
#include <set>
#include <vector>
#include <limits>
#include <cassert>
extern "C"{
#include "lglib.h"
}
// reusing a lot of @orlp's ideas and code
enum { N = -8, W = -2, E = 2, S = 8 };
static const int encoded_pos[] = {8, 10, 12, 16, 18, 20, 24, 26, 28};
static const int wall_idx[] = {9, 11, 12, 14, 16, 17, 19, 20, 22, 24, 25, 27};
static const int move_offsets[] = { N, E, S, W };
static const uint32_t toppos = 1ull << 8 | 1ull << 10 | 1ull << 12;
static const uint32_t leftpos = 1ull << 8 | 1ull << 16 | 1ull << 24;
static const int unencoded_pos[] = {0,0,0,0,0,0,0,0,0,0,1,0,2,0,0,0,3,
                                    0,4,0,5,0,0,0,6,0,7,0,8};
int do_move(uint32_t walls, int pos, int move) {
  int idx = pos + move / 2;
  return walls & (1ull << idx) ? pos + move : pos;
}
struct Maze {
  uint32_t walls, reach;
  int start;
  Maze(uint32_t walls=0, uint32_t reach=0, int start=0):
    walls(walls),reach(reach),start(start) {}
  bool is_dummy() const {
    return (walls==0);
  }
  std::size_t size() const{
    return std::bitset<32>(reach).count();
  }
  std::size_t simplicity() const{  // how many potential walls aren't there?
    return std::bitset<32>(walls).count();
  }
};
bool cmp(const Maze& a, const Maze& b){
  auto asz = a.size();
  auto bsz = b.size();
  if (asz>bsz) return true;
  if (asz<bsz) return false;
  return a.simplicity()<b.simplicity();
}
uint32_t reachable(uint32_t walls) {
  static int fill[9];
  uint32_t reached = 0;
  uint32_t reached_relevant = 0;
  for (int start : encoded_pos){
    if ((1ull << start) & reached) continue;
    uint32_t reached_component = (1ull << start);
    fill[0]=start;
    int count=1;
    for(int i=0; i<count; ++i)
      for(int m : move_offsets) {
        int newpos = do_move(walls, fill[i], m);
        if (reached_component & (1ull << newpos)) continue;
        reached_component |= 1ull << newpos;
        fill[count++] = newpos;
      }
    if (count>1){
      if (reached_relevant)
        return 0;  // more than one nonsingular component
      if (!(reached_component & toppos) || !(reached_component & leftpos))
        return 0;  // equivalent to shifted version
      if (std::bitset<32>(reached_component).count() <= 4)
        return 0;  
      reached_relevant = reached_component;
    }
    reached |= reached_component;
  }
  return reached_relevant;
}
void enterMazes(uint32_t walls, uint32_t reached, std::vector<Maze>& mazes){
  int max_deg = 0;
  uint32_t ends = 0;
  for (int pos : encoded_pos)
    if (reached & (1ull << pos)) {
      int deg = 0;
      for (int m : move_offsets) {
        if (pos != do_move(walls, pos, m))
          ++deg;
      }
      if (deg == 1)
        ends |= 1ull << pos;
      max_deg = std::max(deg, max_deg);
    }
  uint32_t starts = reached;
  if (max_deg == 2){
    if (std::bitset<32>(reached).count() <= 7)
      return; // small paths are redundant
    starts = ends; // need only start at extremal points
  }
  for (int pos : encoded_pos)
    if ( starts & (1ull << pos))
      mazes.emplace_back(walls, reached, pos);
}
std::vector<Maze> gen_valid_mazes() {
  std::vector<Maze> mazes;
  for (int maze_id = 0; maze_id < (1 << 12); maze_id++) {
    uint32_t walls = 0;
    for (int i = 0; i < 12; ++i) 
      if (maze_id & (1 << i))
    walls |= 1ull << wall_idx[i];
    uint32_t reached=reachable(walls);
    if (!reached) continue;
    enterMazes(walls, reached, mazes);
  }
  std::sort(mazes.begin(),mazes.end(),cmp);
  return mazes;
};
bool is_solution(const std::vector<int>& moves, Maze& maze) {
  int pos = maze.start;
  uint32_t reached = 1ull << pos;
  for (auto move : moves) {
    pos = do_move(maze.walls, pos, move);
    reached |= 1ull << pos;
    if (reached == maze.reach) return true;
  }
  return false;
}
std::vector<int> str_to_moves(std::string str) {
  std::vector<int> moves;
  for (auto c : str) {
    switch (c) {
    case 'N': moves.push_back(N); break;
    case 'E': moves.push_back(E); break;
    case 'S': moves.push_back(S); break;
    case 'W': moves.push_back(W); break;
    }
  }
  return moves;
}
Maze unsolved(const std::vector<int>& moves, std::vector<Maze>& mazes) {
  int unsolved_count = 0;
  Maze problem{};
  for (Maze m : mazes)
    if (!is_solution(moves, m))
      if(!(unsolved_count++))
    problem=m;
  if (unsolved_count)
    std::cout << "unsolved: " << unsolved_count << "\n";
  return problem;
}
LGL * lgl;
constexpr int TRUELIT = std::numeric_limits<int>::max();
constexpr int FALSELIT = -TRUELIT;
int new_var(){
  static int next_var = 1;
  assert(next_var<TRUELIT);
  return next_var++;
}
bool lit_is_true(int lit){
  int abslit = lit>0 ? lit : -lit;
  bool res = (abslit==TRUELIT) || (lglderef(lgl,abslit)>0);
  return lit>0 ? res : !res;
}
void unsat(){
  std::cout << "Unsatisfiable!\n";
  std::exit(1);
}
void clause(const std::set<int>& lits){
  if (lits.find(TRUELIT) != lits.end())
    return;
  for (int lit : lits)
    if (lits.find(-lit) != lits.end())
      return;
  int found=0;
  for (int lit : lits)
    if (lit != FALSELIT){
      lgladd(lgl, lit);
      found=1;
    }
  lgladd(lgl, 0);
  if (!found)
    unsat();
}
void at_most_one(const std::set<int>& lits){
  if (lits.size()<2)
    return;
  for(auto it1=lits.cbegin(); it1!=lits.cend(); ++it1){
    auto it2=it1;
    ++it2;
    for(  ; it2!=lits.cend(); ++it2)
      clause( {- *it1, - *it2} );
  }
}
/* Usually, lit_op(lits,sgn) creates a new variable which it returns,
   and adds clauses that ensure that the variable is equivalent to the
   disjunction (if sgn==1) or the conjunction (if sgn==-1) of the literals
   in lits. However, if this disjunction or conjunction is constant True
   or False or simplifies to a single literal, that is returned without
   creating a new variable and without adding clauses.                    */ 
int lit_op(std::set<int> lits, int sgn){
  if (lits.find(sgn*TRUELIT) != lits.end())
    return sgn*TRUELIT;
  lits.erase(sgn*FALSELIT);
  if (!lits.size())
    return sgn*FALSELIT;
  if (lits.size()==1)
    return *lits.begin();
  int res=new_var();
  for(int lit : lits)
    clause({sgn*res,-sgn*lit});
  for(int lit : lits)
    lgladd(lgl,sgn*lit);
  lgladd(lgl,-sgn*res);
  lgladd(lgl,0);
  return res;
}
int lit_or(std::set<int> lits){
  return lit_op(lits,1);
}
int lit_and(std::set<int> lits){
  return lit_op(lits,-1);
}
using A4 = std::array<int,4>;
void add_maze_conditions(Maze m, std::vector<A4> dirs, int len){
  int mp[9][2];
  int rp[9];
  for(int p=0; p<9; ++p)
    if((1ull << encoded_pos[p]) & m.reach)
      rp[p] = mp[p][0] = encoded_pos[p]==m.start ? TRUELIT : FALSELIT;
  int t=0;
  for(int i=0; i<len; ++i){
    std::set<int> posn {};
    for(int p=0; p<9; ++p){
      int ep = encoded_pos[p];
      if((1ull << ep) & m.reach){
        std::set<int> reach_pos {};
        for(int d=0; d<4; ++d){
          int np = do_move(m.walls, ep, move_offsets[d]);
          reach_pos.insert( lit_and({mp[unencoded_pos[np]][t],
                                  dirs[i][d ^ ((np==ep)?0:2)]    }));
        }
        int pl = lit_or(reach_pos);
        mp[p][!t] = pl;
        rp[p] = lit_or({rp[p], pl});
        posn.insert(pl);
      }
    }
    at_most_one(posn);
    t=!t;
  }
  for(int p=0; p<9; ++p)
    if((1ull << encoded_pos[p]) & m.reach)
      clause({rp[p]});
}
void usage(char* argv0){
  std::cout << "usage: " << argv0 <<
    " <string>\n   where <string> consists of 'N', 'E', 'S', 'W' and '*'.\n" ;
  std::exit(2);
}
const std::string nesw{"NESW"};
int main(int argc, char** argv) {
  if (argc!=2)
    usage(argv[0]);
  std::vector<Maze> mazes = gen_valid_mazes();
  std::cout << "Mazes with start positions: " << mazes.size() << "\n" ;
  lgl = lglinit();
  int len = std::strlen(argv[1]);
  std::cout << argv[1] << "\n   with length " << len << "\n";
  std::vector<A4> dirs;
  for(int i=0; i<len; ++i){
    switch(argv[1][i]){
    case 'N':
      dirs.emplace_back(A4{TRUELIT,FALSELIT,FALSELIT,FALSELIT});
      break;
    case 'E':
      dirs.emplace_back(A4{FALSELIT,TRUELIT,FALSELIT,FALSELIT});
      break;
    case 'S':
      dirs.emplace_back(A4{FALSELIT,FALSELIT,TRUELIT,FALSELIT});
      break;
    case 'W':
      dirs.emplace_back(A4{FALSELIT,FALSELIT,FALSELIT,TRUELIT});
      break;
    case '*': {
      dirs.emplace_back();
      std::generate_n(dirs[i].begin(),4,new_var);
      std::set<int> dirs_here { dirs[i].begin(), dirs[i].end() };
      at_most_one(dirs_here);
      clause(dirs_here);
      for(int l : dirs_here)
        lglfreeze(lgl,l);
      break;
      }
    default:
      usage(argv[0]);
    }
  }
  int maze_nr=0;
  for(;;) {
    std::cout << "Solving...\n";
    int res=lglsat(lgl);
    if(res==LGL_UNSATISFIABLE)
      unsat();
    assert(res==LGL_SATISFIABLE);
    std::string sol(len,' ');
    for(int i=0; i<len; ++i)
      for(int d=0; d<4; ++d)
        if (lit_is_true(dirs[i][d])){
          sol[i]=nesw[d];
          break;
    }
    std::cout << sol << "\n";
    Maze m=unsolved(str_to_moves(sol),mazes);
    if (m.is_dummy()){
      std::cout << "That solves all!\n";
      return 0;
    }
    std::cout << "Adding maze " << ++maze_nr << ": " << 
      m.walls << "/" << m.start <<
      " (" << m.size() << "/" << 12-m.simplicity() << ")\n";
    add_maze_conditions(m,dirs,len);
  }
}  
อันดับแรกconfigure.shและmakeตัวlingelingแก้ไขจากนั้นคอมไพล์โปรแกรมด้วยบางสิ่งบางอย่าง
 g++ -std=c++11 -O3 -I ... -o m3sat m3sat.cc -L ... -llglซึ่ง...เป็นเส้นทางที่lglib.hresp ดังนั้นทั้งสองอาจจะยกตัวอย่างเช่นliblgl.a
../lingeling-<version>หรือเพียงวางไว้ในไดเรกทอรีเดียวกันและทำได้โดยไม่ต้องมีตัวเลือก-Iและ-L
โปรแกรมที่ใช้อาร์กิวเมนต์หนึ่งที่บังคับใช้บรรทัดคำสั่งสตริงประกอบด้วยN, E, S, W(สำหรับเส้นทางคงที่) *หรือ ดังนั้นคุณสามารถค้นหาวิธีแก้ปัญหาทั่วไปขนาด 78 โดยให้สตริงที่ 78 *s (ในเครื่องหมายคำพูด) หรือค้นหาวิธีแก้ปัญหาที่เริ่มต้นด้วยการNEWSใช้NEWSตามด้วย*s มากเท่าที่คุณต้องการสำหรับขั้นตอนเพิ่มเติม *ในฐานะที่เป็นการทดสอบครั้งแรกที่ใช้วิธีการแก้ปัญหาที่คุณชื่นชอบและแทนที่ตัวอักษรบางตัวที่มี นี่เป็นวิธีการแก้ปัญหาที่รวดเร็วสำหรับมูลค่าที่สูงอย่างน่าประหลาดใจ
โปรแกรมจะบอกให้ทราบว่าเขาวงกตตัวใดเพิ่มเข้ามาอธิบายด้วยโครงสร้างผนังและตำแหน่งเริ่มต้นรวมทั้งบอกจำนวนตำแหน่งและกำแพงที่สามารถเข้าถึงได้ เขาวงกตจะถูกจัดเรียงตามเกณฑ์เหล่านี้และอันที่ยังไม่ได้แก้ไขแรกจะถูกเพิ่ม ดังนั้นเขาวงกตที่เพิ่มเข้ามาส่วนใหญ่จึงมี(9/4)แต่บางครั้งก็มีบางคนปรากฏเช่นกัน
ฉันใช้วิธีแก้ปัญหาที่รู้จักที่มีความยาว 79 และสำหรับแต่ละกลุ่มของตัวอักษร 26 ตัวที่อยู่ติดกันพยายามที่จะแทนที่ด้วย 25 ตัวอักษร ฉันพยายามลบตัวอักษร 13 ตัวออกจากจุดเริ่มต้นและตอนท้ายและแทนที่ด้วยตัวอักษร 13 ตัวที่จุดเริ่มต้นและอีก 12 ตัวท้ายและในทางกลับกัน น่าเสียดายที่มันไม่น่าพอใจ ดังนั้นเราสามารถทำสิ่งนี้เป็นตัวบ่งชี้ว่าความยาว 79 นั้นเหมาะสมหรือไม่ ไม่ฉันพยายามปรับปรุงโซลูชัน 80 ความยาวให้ยาว 79 เช่นเดียวกันและนั่นก็ไม่ประสบความสำเร็จเช่นกัน
ในที่สุดฉันพยายามรวมจุดเริ่มต้นของโซลูชันหนึ่งกับอีกโซลูชันหนึ่งและกับโซลูชันหนึ่งที่เปลี่ยนรูปโดยสมมาตรอันใดอันหนึ่ง ตอนนี้ฉันหมดความคิดที่น่าสนใจดังนั้นฉันตัดสินใจที่จะแสดงสิ่งที่ฉันมีถึงแม้ว่ามันจะไม่นำไปสู่การแก้ปัญหาใหม่