在剖析完Muduo网络库源码之后,我们试着完成一个高效的数独和八数码问题求解服务器。

先说说为什么要选择这两个问题?数独问题一直是陈硕老师很喜欢的问题,在muduo网络库中多次提到并有示例。八数码问题是我很喜欢的问题,所以在此综合完成求解数独和八数码问题的高效服务端程序。

编写这样一个看似简单的服务程序的技术含量远高于所谓的控件堆砌型开发,虽然有muduo网络库帮助我们处理网络事件,我们只需要关注setConnectionCallback() 和setMessageCallback() 事件,但是这两个问题都是cpu密集型任务,需要高效的算法,同时使用多核加速(ThreadPool)。我们采用Dancing Links算法求解数独问题,使用A* 算法+康托展开 求解八数码问题并打印操作路径。算法由我自己编写实现,同时通过online judge的测试数据(hdu 1043 ,poj  3074),正确性有一定保证。

关于这里面涉及的算法在此不再赘述,请算法基础薄弱的读者自行查看以下参考链接:

Dancing Links 算法:     http://wenku.baidu.com/link?url=ENP5vkf6Ws54iuVhEFFvnkLrgWuv_ukhVnfBUfszEifQjRzX-hXJa-laCcL6Bkos-X1SDV3uyV0vXSf2j95LrW_p_FYqkDW2Zk2LmzLCxQ3

A*算法:http://www.cnblogs.com/luxiaoxun/archive/2012/08/05/2624115.html

康托展开: http://baike.baidu.com/link?url=kBFlr76wrl2UsxzU_pNNJpCtIvOa8LSjM5_b6DI6XBhekB00ax8fR7YbkTQhbzt-dNK4v90VrlH1kujTqZazB_

(一)数独


输入的格式为81个数字,按照棋盘由左到右,由上到下来排列,前面可以加上用“:”分隔 的序号和标志,例如:

00000000000.....00000  或者  a:00000000000...00000,有解返回答案字符串,无解返回“No solution”

数独算法的核心代码类如下:

struct Node;
typedef Node Column;
struct Node
{Node* left;Node* right;Node* up;Node* down;Column* col;int name;int size;
};const int kMaxNodes = 1 + 81*4 + 9*9*9*4;
// const int kMaxColumns = 400;
const int kRow = 100, kCol = 200, kBox = 300;
extern const char kNoSolution[] = "NoSolution";class SudokuSolver
{public:SudokuSolver(int board[kCells]): inout_(board),cur_node_(0){stack_.reserve(100);root_ = new_column();root_->left = root_->right = root_;memset(columns_, 0, sizeof(columns_));bool rows[kCells][10] = { {false} };bool cols[kCells][10] = { {false} };bool boxes[kCells][10] = { {false} };for (int i = 0; i < kCells; ++i) {int row = i / 9;int col = i % 9;int box = row/3*3 + col/3;int val = inout_[i];rows[row][val] = true;cols[col][val] = true;boxes[box][val] = true;}for (int i = 0; i < kCells; ++i) {if (inout_[i] == 0) {append_column(i);}}for (int i = 0; i < 9; ++i) {for (int v = 1; v < 10; ++v) {if (!rows[i][v])append_column(get_row_col(i, v));if (!cols[i][v])append_column(get_col_col(i, v));if (!boxes[i][v])append_column(get_box_col(i, v));}}for (int i = 0; i < kCells; ++i) {if (inout_[i] == 0) {int row = i / 9;int col = i % 9;int box = row/3*3 + col/3;//int val = inout[i];for (int v = 1; v < 10; ++v) {if (!(rows[row][v] || cols[col][v] || boxes[box][v])) {Node* n0 = new_row(i);Node* nr = new_row(get_row_col(row, v));Node* nc = new_row(get_col_col(col, v));Node* nb = new_row(get_box_col(box, v));put_left(n0, nr);put_left(n0, nc);put_left(n0, nb);}}}}}bool solve(){if (root_->left == root_) {for (size_t i = 0; i < stack_.size(); ++i) {Node* n = stack_[i];int cell = -1;int val = -1;while (cell == -1 || val == -1) {if (n->name < 100)cell = n->name;elseval = n->name % 10;n = n->right;}//assert(cell != -1 && val != -1);inout_[cell] = val;}return true;}Column* const col = get_min_column();cover(col);for (Node* row = col->down; row != col; row = row->down) {stack_.push_back(row);for (Node* j = row->right; j != row; j = j->right) {cover(j->col);}if (solve()) {return true;}stack_.pop_back();for (Node* j = row->left; j != row; j = j->left) {uncover(j->col);}}uncover(col);return false;}private:Column* root_;int*    inout_;Column* columns_[400];std::vector<Node*> stack_;Node    nodes_[kMaxNodes];int     cur_node_;Column* new_column(int n = 0){assert(cur_node_ < kMaxNodes);Column* c = &nodes_[cur_node_++];memset(c, 0, sizeof(Column));c->left = c;c->right = c;c->up = c;c->down = c;c->col = c;c->name = n;return c;}void append_column(int n){assert(columns_[n] == NULL);Column* c = new_column(n);put_left(root_, c);columns_[n] = c;}Node* new_row(int col){assert(columns_[col] != NULL);assert(cur_node_ < kMaxNodes);Node* r = &nodes_[cur_node_++];//Node* r = new Node;memset(r, 0, sizeof(Node));r->left = r;r->right = r;r->up = r;r->down = r;r->name = col;r->col = columns_[col];put_up(r->col, r);return r;}int get_row_col(int row, int val){return kRow+row*10+val;}int get_col_col(int col, int val){return kCol+col*10+val;}int get_box_col(int box, int val){return kBox+box*10+val;}Column* get_min_column(){Column* c = root_->right;int min_size = c->size;if (min_size > 1) {for (Column* cc = c->right; cc != root_; cc = cc->right) {if (min_size > cc->size) {c = cc;min_size = cc->size;if (min_size <= 1)break;}}}return c;}void cover(Column* c){c->right->left = c->left;c->left->right = c->right;for (Node* row = c->down; row != c; row = row->down) {for (Node* j = row->right; j != row; j = j->right) {j->down->up = j->up;j->up->down = j->down;j->col->size--;}}}void uncover(Column* c){for (Node* row = c->up; row != c; row = row->up) {for (Node* j = row->left; j != row; j = j->left) {j->col->size++;j->down->up = j;j->up->down = j;}}c->right->left = c;c->left->right = c;}void put_left(Column* old, Column* nnew){nnew->left = old->left;nnew->right = old;old->left->right = nnew;old->left = nnew;}void put_up(Column* old, Node* nnew){nnew->up = old->up;nnew->down = old;old->up->down = nnew;old->up = nnew;old->size++;nnew->col = old;}
};string solveSudoku(const StringPiece& puzzle)
{assert(puzzle.size() == kCells);string result = kNoSolution;int board[kCells] = { 0 };bool valid = true;for (int i = 0; i < kCells; ++i){board[i] = puzzle[i] - '0';valid = valid && (0 <= board[i] && board[i] <= 9);}if (valid){SudokuSolver s(board);if (s.solve()){result.clear();result.resize(kCells);for (int i = 0; i < kCells; ++i){result[i] = static_cast<char>(board[i] + '0');}}}return result;
}


(二)八数码


输入的数据为9个字符组成的字符串,当前空格用x表示。有解返回操作字符串 l(左)、r(右)、u(上)、d(下)即最少步数移动路径,无解返回“No solution”。

这里要讲一下,除了使用A*算法进行启发式搜索之外,判重使用康托展开,可以有效的节约空间和提升效率(仅使用int记录状态即可)。

八数码算法核心代码:

struct Node
{int maze[3][3];int fun_h,fun_g;int pos_x,pos_y;int Hash;bool operator<(const Node nt)const{return fun_h+fun_g>nt.fun_h+nt.fun_g;}bool check(){if(pos_x>=0 && pos_x<3 && pos_y>=0 && pos_y<3)return true;return false;}
};const int MAXNUM=370000;
int HASH[9]={1,1,2,6,24,120,720,5040,40320};//0!~8!
int dest=46233;
int vis[MAXNUM];
int pre[MAXNUM];
int way[4][2]={{0,1},{0,-1},{1,0},{-1,0}};//4 waysclass EightSolver
{public:EightSolver(string s){memset(vis,-1,sizeof(vis));memset(pre,-1,sizeof(pre));int k=0;for(int i=0;i<3;i++)for(int j=0;j<3;j++){if((s[k]<='9'&&s[k]>='0')||s[k]=='x'){if(s[k]=='x'){begNode.maze[i][j]=0;begNode.pos_x=i;begNode.pos_y=j;}elsebegNode.maze[i][j]=s[k]-'0';}k++;}begNode.Hash=getHash(begNode);begNode.fun_g=0;begNode.fun_h=getH(begNode);vis[begNode.Hash]=1;}string getAns(){string ans="";int nxt=dest;while(pre[nxt]!=-1){switch(vis[nxt]){case 0:ans+='r';break;case 1:ans+='l';break;case 2:ans+='d';break;case 3:ans+='u';break;}nxt=pre[nxt];}reverse(ans.begin(),ans.end());return ans;}bool isOK(){std::vector<int> v;for(int i=0;i<3;i++)for(int j=0;j<3;j++)v.push_back(begNode.maze[i][j]);int sum=0;for(int i=0;i<9;i++)for(int j=i+1;j<9;j++)if(v[j]&&v[i]&&v[i]>v[j])sum++;return !(sum&1);}void aStar(){if(begNode.Hash==dest)return ;std::priority_queue<Node> que;que.push(begNode);while(!que.empty()){Node u=que.top();que.pop();for(int i=0;i<4;i++){Node v=u;v.pos_x+=way[i][0];v.pos_y+=way[i][1];if(v.check()){std::swap(v.maze[v.pos_x][v.pos_y],v.maze[u.pos_x][u.pos_y]);v.Hash=getHash(v);if(vis[v.Hash]==-1){vis[v.Hash]=i;v.fun_g++;pre[v.Hash]=u.Hash;v.fun_h=getH(v);que.push(v);}if(v.Hash==dest)return ;}}}}private:Node begNode;int getHash(Node &tmp){std::vector<int> v;for(int i=0;i<3;i++)for(int j=0;j<3;j++)v.push_back(tmp.maze[i][j]);int res=0;for(int i=0;i<9;i++){int k=0;for(int j=i+1;j<9;j++)if(v[j]<v[i])k++;res+=HASH[8-i]*k;}return res;}int getH(Node &tmp){int ans=0;for(int i=0;i<3;i++)for(int j=0;j<3;j++)if(tmp.maze[i][j])ans+=abs(i-(tmp.maze[i][j]-1)/3)+abs(j-(tmp.maze[i][j]-1)%3);return ans;}
};string solveEight(const StringPiece& puzzle)
{string board="";string constStr="No solution";for(int i=0;i<puzzle.size();i++){board+=puzzle[i];}EightSolver s(board);if(!s.isOK())return constStr;else{s.aStar();return s.getAns();}}

最后,这两个服务是融合的,开启服务器后,使用telnet命令访问,满足要求的字符串便会自动分类解析返回结果,效果图如下(Linux下):

由于是基于muduo网络库实现的,所以可以很轻松的实现高效可靠的并发访问~。

项目全部源码托管于 Github (https://github.com/Tachone/sukuEight),有兴趣的读者可以继续完善,同时强烈欢迎前端的同学做一些web界面,让这个求解程序更加人性化~

使用Muduo完成数独和八数码问题求解服务器相关推荐

  1. html八数码问题求解过程的可视化,(转贴)数字拼图问题(八数码)求解过程动态演示...

    一.题目说明:在一个3×3的九宫中有1-8这8个数及一个空格随机的摆放在 其中的格子里,如图1-1所示.现在要求实现这个问题:将其调整为 如图1-1右图所示的形式.调整的规则是:每次只能将与空格(上. ...

  2. 全局择优搜索、A*算法、宽度优先算法解决八数码问题

    1问题描述 使用盲目搜索中的宽度优先搜索算法或者使用启发式搜索中的全局择优搜索或A算法,对任意的八数码问题给出求解结果.例如:对于如下具体的八数码问题: 通过设计启发函数,编程实现求解过程,如果问题有 ...

  3. 八数码问题的A*算法

    问题描述:八数码问题即是找出一条状态路径,使初始状态(start)转换到目标状态(end),一般选取目标状态为:1 2 3 4 5 6 7 8 0(0代表空格) 0   1   2           ...

  4. 广州大学人工智能导论实验一(八数码问题)

    实验一 八数码问题 一. 实验目的 该课程的教学应贯彻理论与实践相结合的原则,为学生所学到的理论提供实践的场所,通过实验课程中具体问题的求解达到深入了解并掌握的目的. 二. 实验环境 微型计算机,操作 ...

  5. c语言八数码A星算法代码解析,八数码问题c语言a星算法详细实验报告含代码解析...

    八数码问题c语言a星算法详细实验报告含代码解析 (13页) 本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦! 14.9 积分 一.实验内容和要求 八数码问题:在3 ...

  6. 【人工智能】八数码问题:广度搜索、深度搜索

    应用广度搜索BFS和深度搜索DFS解决八数码问题,广度搜索和深度搜索都是盲目搜索,相关理论知识,算法过程:问题求解:状态空间图和盲目搜索. 参考:7种方法求解八数码问题 Python实现A*算法解决N ...

  7. 【八数码问题】基于状态空间法的知识表示与状态搜索:无信息搜索(BFS/DFS) 启发式搜索(A*)

    前言 一.问题引入 二.状态空间法 1. 知识及其表示 2. 状态空间法定义 3. 问题求解 三.基于状态空间搜索法解决八数码问题 1. 八数码问题的知识表示 2. 状态空间图搜索 1. 无信息搜索 ...

  8. 基于Python实现的AStar求解八数码问题

    资源下载地址:https://download.csdn.net/download/sheziqiong/86776612 资源下载地址:https://download.csdn.net/downl ...

  9. Prolog学习:数独和八皇后问题

    上一篇简单介绍了下Prolog的一些基本概念,今天我们来利用这些基本概念解决两个问题:数独和八皇后问题. 数独 数独是一个很经典的游戏: 玩家需要根据n×n盘面上的已知数字,推理出所有剩余空格的数字, ...

最新文章

  1. ZedGraph在项目中的应用
  2. DataSnap如何监控Tcp/IP客户端的连接情况
  3. systemd的程序自启动脚本编写
  4. linux永久改变字符集,Linux 下mysql永久更改字符集
  5. JavaScript 获得当前日期+时间
  6. torch将多个tensor张量合并为一个张量,只提高迷你批次的纬度
  7. MySql解决办法:2004:Can't create TCP/IP socket (24)
  8. c语言 字母常量,C语言常量的类型
  9. 1.7 新概念 量词
  10. PDF Expert|全能宝藏PDF编辑器
  11. nextpolish安装_使用nextpolish对三代组装进行polish
  12. 产品运营之道:用户反馈(一)
  13. 光纤中的多种光学模式芯径_「涨知识」你想知道的光纤常识都在这里了,看不看随你...
  14. 基于JAVA校园爱心志愿系统计算机毕业设计源码+数据库+lw文档+系统+部署
  15. vivado生成ltx文件命令_实验室自研工具Vivado Batch Mode Tool介绍!
  16. 2020年西式面点师(高级)考试题及西式面点师(高级)考试内容
  17. ROS意外崩掉解决方案
  18. 机器人相互教学让知识瞬间转移 AI迎来指数级进化 | 技术
  19. 士成兄的面试经历(放弃5家offer终去了华为)
  20. 多商户商城系统功能拆解02讲-平台端工作台

热门文章

  1. 互联网进化断代史第零纪--奇点纪
  2. 逻辑回归二分类算法python_机器学习第七周--二分类算法逻辑回归
  3. javac不是内部或外部命令解决方案
  4. 前端reduce的用法
  5. 2023年人不在湖北可以考电工证(高低压电工)吗?
  6. 视频教程-主流前端框架下ArcGIS API for JavaScript的开发-其他
  7. Squirrel SQL Client客户端工具
  8. 【引用】全球第一经典语录
  9. 法师VS战士PK小谈
  10. 插图:创建一个动物派对场景