前言
这一段时间在学习MATLAB。MATLAB是一个强大的数学软件,而其基本数据单位是矩阵。既然是学习,总要找一个比较容易入手且比较有趣的需求加以实现,我认为这样的学习方式是比较合理的。说到矩阵,我非常感兴趣的就是幻方,从幻方的巧妙之处让我联想到我喜欢玩的一个游戏,没错,就是这篇博客要说的数独(Sudoku)。
正文
按照惯例,我们先看一下成品效果。
下个数独游戏app直接上困难模式,如图。
将这些已知数字填到计算界面。
然后计算,秒出结果。
将结果数字填入到数独游戏app中,大功告成,因为演示截图耽搁,不然2分钟足够了。
做好的工具我发布到个人测试服务器了,有兴趣的可以去看看。解数独工具
下面说方法。
网上有很多解数独的算法了,思路也很明确,我就照本宣科将算法用C#的方式写出来。数独游戏的规则是根据9×9盘面上的已知数字,推理出所有剩余空格的数字,并满足每一行、每一列、每一个粗线宫(3*3)内的数字均含1-9。根据这个规则,我们可以先设计三个方法。
/// <summary>
/// 检查行是否能放入数字num
/// </summary>
/// <param name="_quesArr">数独二维数组</param>
/// <param name="i">行</param>
/// <param name="j">列</param>
/// <param name="num">数字</param>
/// <returns></returns>
private bool check_row(int[,] _quesArr,int i, int j, int num)
{
for (int col = 1; col <= 9; col++) {
if (_quesArr[i - 1, col - 1] == num)
{
return false;
}
}
return true;
}
//检查列是否能放入数字num
private bool check_col(int[,] _quesArr, int i, int j, int num) {
for (int row = 1; row <= 9; row++) {
if (_quesArr[row - 1, j - 1] == num)
{
return false;
}
}
return true;
}
//检查3*3宫格是否能放入数字num
private bool check3_mul_3(int[,] _quesArr, int i, int j, int num)
{
// 检查(i,j)能否放num
int block_row = (i - 1) / 3 + 1;
int block_col = (j - 1) / 3 + 1;
int block_row_to = block_row * 3;
int block_row_from = block_row_to - 2;
int block_col_to = block_col * 3;
int block_col_from = block_col_to - 2;
for (int row = block_row_from; row <= block_row_to; row++) {
for (int col = block_col_from; col <= block_col_to; col++) {
if (_quesArr[row - 1, col - 1] == num)
return false;
}
}
return true;
}
//同时满足三个条件的,可以在格子内放入num数字
private bool check(int[,] _quesArr,int i, int j, int num)
{
if(check_row(_quesArr,i, j,num) && check_col(_quesArr, i, j, num) && check3_mul_3(_quesArr, i, j, num))
return true;
else
return false;
}
这三个方法设计好后,基本就完成一半了,后面就需要递归调用check()检查每一个数字了。先贴代码。
int[,] _ansArr;
//按编号逐一填充,递归
public void Solve(int[,] _quesArr, int id)
{
if (id > 81)//当递归到9*9最后一个格子后,完成递归出结果
_ansArr = _quesArr;
else
{
int row = (id - 1) / 9 + 1;
int col = (id - 1) % 9 + 1;
if (_quesArr[row - 1, col - 1] != 0)//0代表空,需要填充
{
var temparr =new int[9,9];
temparr=(int[,])_quesArr.Clone();//注意引用类型的递归传递 需要克隆副本进行传递
Solve(temparr, id + 1);
}
else
{
for (int i = 1; i <= 9; i++)
{
if (check(_quesArr, row, col, i))//如果校验合格,将数字i放到指定行列
{
_quesArr[row - 1, col - 1] = i;
var temparr = new int[9, 9];
temparr = (int[,])_quesArr.Clone();
Solve(temparr, id + 1);
}
}//循环结束还没找到合适的数字,则说明前面的数字找错了,那前面的递归函数将继续找
}
}
}
大功告成,最后调用这个方法
//Sudoku构造器
public Sudoku(int[,] ques)
{
Solve(ques,1);
ques = _ansArr;
}
好了。二维数组由前端传递,做一个WebApi接口。
[HttpPost,Route("resultsudoku")]
public string ResultSudoku([FromBody]string ques)//二维数组
{
int[,] arr = (int[,])JsonConvert.DeserializeObject(ques, typeof(int[,]));
Sudoku shudu = new Sudoku(arr);
return JsonConvert.SerializeObject(arr);
}
有兴趣的点击上方链接食用吧。解数独工具