অন্তর্বাস থেকে সি ++ এবং লাইব্রেরি
সংক্ষিপ্তসার: একটি নতুন পদ্ধতির, কোনও নতুন সমাধান নয় , খেলতে একটি দুর্দান্ত প্রোগ্রাম এবং পরিচিত সমাধানগুলির স্থানীয় অ-অসম্ভবতার কিছু আকর্ষণীয় ফলাফল। ওহ, এবং কিছু সাধারণভাবে দরকারী পর্যবেক্ষণ।
একটি স্যাট
ভিত্তিক পদ্ধতি ব্যবহার করে, আমি সম্পূর্ণরূপে
পাতলা দেয়াল এবং বিপরীত কোণগুলিতে স্থির শুরু এবং প্রস্থান অবস্থানের পরিবর্তে অবরুদ্ধ কোষগুলির সাথে 4x4 ম্যাজগুলির জন্য একই ধরণের সমস্যাটি সমাধান করতে পারি
। তাই আমি আশা করি এই সমস্যার জন্য একই ধারণা ব্যবহার করতে সক্ষম হবেন। তবে, অন্য সমস্যার জন্য আমি কেবল 2423 ম্যাজ ব্যবহার করেছি (ইতিমধ্যে এটি দেখা গেছে 2083 যথেষ্ট) এবং এটির দৈর্ঘ্যের 29 টি সমাধান রয়েছে, স্যাট এনকোডিং কয়েক মিলিয়ন ভেরিয়েবল ব্যবহার করেছে এবং সমাধানের জন্য কয়েক দিন লেগেছিল।
সুতরাং আমি সিদ্ধান্তটি দুটি গুরুত্বপূর্ণ উপায়ে পরিবর্তন করার সিদ্ধান্ত নিয়েছি:
- স্ক্র্যাচ থেকে কোনও সমাধান অনুসন্ধান করার জন্য জোর করবেন না, তবে সমাধানের স্ট্রিংয়ের একটি অংশ ঠিক করার অনুমতি দিন। (ইউনিট ক্লজ যুক্ত করে যেভাবেই করা সহজ, তবে আমার প্রোগ্রামটি এটি আরামদায়ক করে তোলে))
- শুরু থেকে সমস্ত ম্যাজ ব্যবহার করবেন না। পরিবর্তে, ক্রমবর্ধমান একবারে একটি সমাধান না করা গোলকধাঁধা যুক্ত করুন। কিছু ম্যাজগুলি সুযোগ দ্বারা সমাধান করা যেতে পারে বা ইতিমধ্যে বিবেচিত বিষয়গুলি সমাধান করা হলে সেগুলি সর্বদা সমাধান করা হয়। পরবর্তী ক্ষেত্রে, এর অর্থটি আমাদের জানা ছাড়াই এটি কখনই যুক্ত হবে না।
আমি কম ভেরিয়েবল এবং ইউনিট ক্লজগুলি ব্যবহার করতে কিছু অপ্টিমাইজেশনও করেছি।
প্রোগ্রামটি @ orlp এর উপর ভিত্তি করে। একটি গুরুত্বপূর্ণ পরিবর্তন ছিল ম্যাজস নির্বাচন:
- প্রথমত, ম্যাজগুলি তাদের প্রাচীরের কাঠামো এবং কেবল শুরুর অবস্থান দ্বারা দেওয়া হয়। (তারা পৌঁছনীয় পজিশনগুলিও সঞ্চয় করে)) ফাংশনটি
is_solution
পরীক্ষা করে যাচাই করে যে সমস্ত অ্যাক্সেসযোগ্য পজিশনে পৌঁছেছে।
- (অপরিবর্তিত: এখনও কেবল 4 বা তার থেকে কম পৌঁছনীয় পজিশনযুক্ত ম্যাসেজ ব্যবহার করছে না But তবে তাদের বেশিরভাগ নীচের পর্যবেক্ষণ দ্বারা যে কোনও উপায়ে ফেলে দেওয়া হবে))
- যদি কোনও গোলকধাঁধা তিনটি শীর্ষ কক্ষের কোনও ব্যবহার না করে, তবে এটি স্থানান্তরিত করা গোলকধাঁধার সমতুল্য। সুতরাং আমরা এটি ড্রপ করতে পারেন। একইভাবে এমন একটি গোলকধাঁধার জন্য যা তিনটি বাম কোষের কোনওটিই ব্যবহার করে না।
- অ্যাক্সেসযোগ্য অংশগুলি সংযুক্ত রয়েছে কিনা তাতে কিছু যায় আসে না, তাই আমরা দৃ ins়ভাবে জোর দিয়ে বলছি যে প্রতিটি অপ্রাপ্য কোষটি প্রাচীর দ্বারা সম্পূর্ণরূপে ঘিরে রয়েছে।
- বড় একক পাথ ধাঁধাঁর সাবমাইজ হওয়া একটি একক পাথ ধাঁধাটি সর্বদা সমাধান করা হয় যখন বড়টি সমাধান করা হয়, সুতরাং আমাদের এটির দরকার নেই। সর্বাধিক size আকারের প্রতিটি একক পাথ ধাঁধাঁটি একটি বৃহত্তর অংশের অংশ (এখনও 3x3-এ ফিট করে) তবে সেখানে 8 টি একক পাথ ম্যাসেজ নেই that সরলকিয়ার জন্য, আসুন মাত্র 8 এর চেয়ে কম সাইজের একক পাথ ম্যাসেজগুলি ফেলে দিন ( প্রোগ্রামের।)
এইভাবে, আমি প্রারম্ভিক অবস্থানগুলি সহ মোট 10772 মেজ পাই।
প্রোগ্রামটি এখানে:
#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.h
liblgl.a
../lingeling-<version>
-I
-L
প্রোগ্রাম এক বাধ্যতামূলক কমান্ড লাইন আর্গুমেন্ট লাগে, একটি স্ট্রিং এর মধ্যে রয়েছে N
, E
, S
, W
(স্থির দিকনির্দেশের জন্য) অথবা *
। আপনি 78 একটি স্ট্রিং দিয়ে আকার 78 একটি সাধারণ সমাধান জন্য অনুসন্ধান সুতরাং পারে *
গুলি (উদ্ধৃতির মধ্যে), অথবা একটি সমাধান জন্য অনুসন্ধান দিয়ে শুরু NEWS
ব্যবহারের NEWS
অনেক হিসাবে দ্বারা অনুসরণ *
আপনি অতিরিক্ত ধাপের জন্য চান হিসাবে সে। প্রথম পরীক্ষা হিসাবে, আপনার পছন্দসই সমাধানটি নিন এবং কয়েকটি অক্ষরের সাথে প্রতিস্থাপন করুন *
। "কিছু" এর আশ্চর্যজনকভাবে উচ্চ মানের জন্য এটি দ্রুত সমাধান খুঁজে পায়।
প্রোগ্রামটি জানায় যে এটি কোন ধাঁধাঁটি যুক্ত করে, প্রাচীরের কাঠামো এবং সূচনা অবস্থান দ্বারা বর্ণিত, এবং পৌঁছনীয় পজিশন এবং দেয়ালের সংখ্যাও দেবে। ম্যাজগুলি এই মানদণ্ড অনুসারে বাছাই করা হয় এবং প্রথম অমীমাংসিত এক যুক্ত করা হয়। অতএব সর্বাধিক সংযোজন করা মেজেগুলি রয়েছে (9/4)
তবে কখনও কখনও অন্যরাও উপস্থিত হন।
আমি length৯ দৈর্ঘ্যের জ্ঞাত সমাধানটি নিয়েছি এবং সংলগ্ন 26 টি অক্ষরের প্রতিটি গ্রুপের জন্য 25 টি অক্ষর দিয়ে সেগুলি প্রতিস্থাপন করার চেষ্টা করেছি। আমি শুরু থেকে এবং শেষ থেকে 13 টি অক্ষর অপসারণ করার চেষ্টা করেছি এবং এগুলিকে শুরুতে যে কোনও 13 এবং শেষে 12 টি দ্বারা প্রতিস্থাপন করেছি এবং বিপরীতে। দুর্ভাগ্যক্রমে, এটি সমস্ত অসন্তুষ্টির বাইরে এসেছিল। সুতরাং, আমরা এটি সূচক হিসাবে নিতে পারি যে দৈর্ঘ্য 79৯টি সর্বোত্তম? না, আমি একইভাবে 80 সমাধানের দৈর্ঘ্য 79 এর দৈর্ঘ্যের উন্নতি করার চেষ্টা করেছি এবং এটিও সফল হয়নি।
অবশেষে, আমি একটি সমাধানের সূচনার সাথে অন্যটির শেষের সাথে এবং একই সাথে প্রতিসাম্যগুলির মধ্যে একটি দ্বারা পরিবর্তিত একটি সমাধানের সাথে একত্রিত করার চেষ্টা করেছি। এখন আমি আকর্ষণীয় ধারণাগুলির বাইরে চলে যাচ্ছি, সুতরাং আমি নতুন সিদ্ধান্তগুলি না নিয়ে সত্ত্বেও আমার কাছে যা আছে তা আপনাকে দেখানোর সিদ্ধান্ত নিয়েছি।