# 项目地址

https://github.com/Leext/SudokuSolver

1. 生成给定数量的数独终局
2. 求解给定的数独

# 实现过程

## 实现

SudokuBoard 类：封装了数独棋盘，方法包括：棋盘构建，寻找可行解，计算可行解数量，寻找最小可行解格子

SudokuSolver 类：求解器类，方法包括：验证棋盘，深度优先搜索，生成棋盘，文件读取，求解数独

1. 计算所有格子的可行解数量
2. 如果没有可求解的，则回溯；如果获得一个解，则保存起来，达到一定数量后退出
3. 寻找可行解最少的格子作为待解格子
4. 获取该格子的所有可行解
5. 对于格子的每一个可行解，设置棋盘为该可行解
6. 递归搜索（回到1）

## 单元测试

void test()
{
SudokuBoard board = SudokuBoard(std::string("012345678000000000000000000000000000000000000000000000000000000000000000000000000"));
assert((1 << 8) == (board.getFeasible(0, 0) >> 1));
assert(1 == board.countFeasible(0, 0));
board = SudokuBoard(std::string("012345678900000000000000000000000000000000000000000000000000000000000000000000000"));
assert(0 == board.countFeasible(0, 0));
board = SudokuBoard(std::string("012300000400000000507000000000000000000000000600000000000000000600000000000000000"));
assert(2 == board.countFeasible(0, 0));
auto p = board.findFewest();
assert(0 == p.first && 0 == p.second);
board = SudokuBoard(std::string("000000010400000000020000000000050407008000300001090000300400200050100000000806000"));
SudokuSolver solver;
SudokuBoard *b = solver.solve(board);
assert(solver.check(*b));

board = SudokuBoard(std::string("000000010400000000020000000000050604008000300001090000300400200050100000000807000"));
b = solver.solve(board);
assert(solver.check(*b));

board = SudokuBoard(std::string("000000012003600000000007000410020000000500300700000600280000040000300500000000000"));
b = solver.solve(board);
assert(solver.check(*b));

board = SudokuBoard(std::string("000000012008030000000000040120500000000004700060000000507000300000620000000100000"));
b = solver.solve(board);
assert(solver.check(*b));

board = SudokuBoard(std::string("000000012040050000000009000070600400000100000000000050000087500601000300200000000"));
b = solver.solve(board);
assert(solver.check(*b));

board = SudokuBoard(std::string("000000012050400000000000030700600400001000000000080000920000800000510700000003000"));
b = solver.solve(board);
assert(solver.check(*b));

board = SudokuBoard(std::string("000000013000030080070000200000206000030000900000010000600500204000400700100000000"));
b = solver.solve(board);
assert(b == NULL);

solver.generate(board);
solver.generateN(3, board);
copeSolve("a.txt");
copeGenerate("10");
}


# 性能改进

bool *SudokuBoard::getBanArray(int x, int y)
{
bool *banArray = new bool[10];
for (int i = 0; i < 10; i++)
banArray[i] = false;
for (int i = 0; i < 9; i++)
banArray[_board[i][y]] = true;
for (int j = 0; j < 9; j++)
banArray[_board[x][j]] = true;
int start_x = x / 3 * 3;
int start_y = y / 3 * 3;
for (int i = start_x; i < start_x + 3; i++)
for (int j = start_y; j < start_y + 3; j++)
banArray[_board[i][j]] = true;
return banArray;
}

std::vector<int>& SudokuBoard::getSolveVector(int x, int y)
{
bool *banArray = getBanArray(x, y);
std::vector<int>* rtn = new std::vector<int>;
for (int i = 1; i < 10; i++)
if (!banArray[i])
rtn->push_back(i);
delete banArray;
return *rtn;
}


int SudokuBoard::getFeasible(int x, int y)
{
int bit = 0;
const int complete = 0x3fe;
for (int i = 0; i < 9; i++)
bit |= 1 << _board[i][y];
for (int j = 0; j < 9; j++)
bit |= 1 << _board[x][j];
int start_x = x / 3 * 3;
int start_y = y / 3 * 3;
for (int i = start_x; i < start_x + 3; i++)
for (int j = start_y; j < start_y + 3; j++)
bit |= 1 << _board[i][j];
return bit^complete;
}
int SudokuBoard::countFeasible(int x, int y)
{
// _board[x][y] must be 0
int bit = getFeasible(x, y) >> 1;
int count = 0;
while (bit)
{
bit &= (bit - 1);
count++;
}
return count;
}


# 代码说明

bool SudokuSolver::dfs(SudokuBoard& board)
{
std::pair<int, int>& target = board.findFewest();
if (target.first == -1) // end
{
_solveCount++;
solutions->push_back(board.toString());
return _solveCount >= _solveLimit;
}
if (target.second == -1) // no solution
return false;
int feasible = board.getFeasible(target.first, target.second);
for (int i = 1; i <= 10; i++)
{
if ((feasible >> i) & 1)
{
board.set(target, i);
if (dfs(board))
return true;
}
}
board.set(target, 0);
return false;
}



# PSP

PSP2.1 Personal Software Process Stages 预估耗时（分钟） 实际耗时（分钟）
Planning 计划
· Estimate · 估计这个任务需要多少时间 10 15
Development 开发
· Analysis · 需求分析 (包括学习新技术) 120 150
· Design Spec · 生成设计文档 30 30
· Design Review · 设计复审 (和同事审核设计文档) 0 0
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 0 0
· Design · 具体设计 30 60
· Coding · 具体编码 300 500
· Code Review · 代码复审 60 100
· Test · 测试（自我测试，修改代码，提交修改） 100 300
Reporting 报告
· Test Report · 测试报告 60 100
· Size Measurement · 计算工作量 30 30
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 60 60

# 感想

posted on 2017-09-26 17:29  Damocles  阅读(207)  评论(1编辑  收藏  举报