C#中怎么实现一个数独求解算法-创新互联

C#中怎么实现一个数独求解算法,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。

成都创新互联公司坚持“要么做到,要么别承诺”的工作理念,服务领域包括:成都网站制作、网站设计、外贸网站建设、企业官网、英文网站、手机端网站、网站推广等服务,满足客户于互联网时代的掇刀网站设计、移动媒体设计的需求,帮助企业找到有效的互联网解决方案。努力成为您成熟可靠的网络建设合作伙伴!

1、先寻找并填写那些数单元格。在部分数独中有些单元格会因为同行、列、宫内题目已知数的限制,实际只有一个数可以填,这种单元格就应该趁早填好,因为没有尝试的必要,不提前处理掉还会影响之后求解的效率。在填写数字后,同行、列、宫的候选数就会减少,可能会出现新的数单元格,那么继续填写,直到没有数单元格为止。


2、检查是否已经完成游戏,也就是所有单元格都有数字。部分简单数独一直填数单元格就可以完成游戏。

3、按照单元格从左到右、从上到下,数字从小到大的顺序尝试填写有多个候选数的单元格,直到全部填完或者发现有单元格候选数为空。如果出现无候选数的单元格说明之前填错数导致出现死路,就需要悔步清除上一个单元格填过的数,换成下一个候选数继续尝试。如果清除后发现没有更大的候选数可填,说明更早之前就已经填错了,要继续悔步并换下一个候选数。有可能需要连续悔多步,一直悔步直到有更大的候选数可填的单元格。如果一路到最开始的单元格都没法填,说明这个数独有问题,无解。

代码(包括数独求解器,求解过程信息,答案存储三个主要类):

数独求解器

public class SudokuSolver {  ///

 /// 题目面板  ///  public SudokuBlock[][] SudokuBoard { get; }  public SudokuSolver(byte[][] board)  {   SudokuBoard = new SudokuBlock[board.Length][];   //初始化数独的行   for (int i = 0; i < board.Length; i++)   {    SudokuBoard[i] = new SudokuBlock[board[i].Length];    //初始化每行的列    for (int j = 0; j < board[i].Length; j++)    {     SudokuBoard[i][j] = new SudokuBlock(      board[i][j] > 0      , board[i][j] <= 0 ? new BitArray(board.Length) : null      , board[i][j] > 0 ? (byte?)board[i][j] : null      , (byte)i      , (byte)j);    }   }  }  ///  /// 求解数独  ///  /// 获得的解  public IEnumerable<(SudokuState sudoku, PathTree path)> Solve(bool multiAnswer = false)  {   //初始化各个单元格能填入的数字   InitCandidate();   var pathRoot0 = new PathTree(null, -1, -1, -1); //填写路径树,在非递归方法中用于记录回退路径和其他有用信息,初始生成一个根   var path0 = pathRoot0;   //循环填入能填入的数字只有一个的单元格,每次填入都可能产生新的数单元格,直到没有数单元格可填   while (true)   {    if (!FillUniqueNumber(ref path0))    {     break;    }   }   //检查是否在填数单元格时就已经把所有单元格填满了   var finish = true;   foreach (var row in SudokuBoard)   {    foreach (var cell in row)    {     if (!cell.IsCondition && !cell.IsUnique)     {      finish = false;      break;     }    }    if (!finish)    {     break;    }   }   if (finish)   {    yield return (new SudokuState(this), path0);    yield break;   }   var pathRoot = new PathTree(null, -1, -1, -1); //填写路径树,在非递归方法中用于记录回退路径和其他有用信息,初始生成一个根   var path = pathRoot;   var toRe = new List<(SudokuState sudoku, PathTree path)>();   //还存在需要试数才能求解的单元格,开始暴力搜索   int i = 0, j = 0;   while (true)   {    (i, j) = NextBlock(i, j);    //正常情况下返回-1表示已经全部填完    if (i == -1 && j == -1 && !multiAnswer)    {     var pathLast = path;//记住最后一步     var path2 = path;     while(path2.Parent.X != -1 && path2.Parent.Y != -1)     {      path2 = path2.Parent;     }     //将暴力搜索的第一步追加到数单元格的填写步骤的最后一步之后,连接成完整的填数步骤     path0.Children.Add(path2);     path2.Parent = path0;     yield return (new SudokuState(this), pathLast);     break;    }    var numNode = path.Children.LastOrDefault();    //确定要从哪个数开始进行填入尝试    var num = numNode == null     ? 0     : numNode.Number;    bool filled = false; //是否发现可以填入的数    //循环查看从num开始接下来的候选数是否能填(num是最后一次填入的数,传到Candidate[]的索引器中刚好指向 num + 1是否能填的存储位,对于标准数独,候选数为 1~9,Candidate的索引范围就是 0~8)    for (; !SudokuBoard[i][j].IsCondition && !SudokuBoard[i][j].IsUnique && num < SudokuBoard[i][j].Candidate.Length; num++)    {     //如果有可以填的候选数,理论上不会遇见没有可以填的情况,这种死路情况已经在UpdateCandidate时检查了     if (SudokuBoard[i][j].Candidate[num] && !path.Children.Any(x => x.Number - 1 == num && !x.Pass))     {      filled = true; //进来了说明单元格有数可以填      //记录步骤      var node = new PathTree(SudokuBoard[i][j], i, j, num + 1, path);      path = node;      //如果更新相关单元格的候选数时发现死路(更新函数会在发现死路时自动撤销更新)      (bool canFill, (byte x, byte y)[] setList) updateResult = UpdateCandidate(i, j, (byte)(num + 1));      if (!updateResult.canFill)      {       //记录这条路是死路       path.SetPass(false);      }      //仅在确认是活路时设置填入数字      if (path.Pass)      {       SudokuBoard[i][j].SetNumber((byte)(num + 1));       path.SetList = updateResult.setList;//记录相关单元格可填数更新记录,方便在回退时撤销更新      }      else //出现死路,要进行回退,重试这个单元格的其他可填数字      {       path.Block.SetNumber(null);       path = path.Parent;      }      //填入一个候选数后跳出循环,不再继续尝试填入之后的候选数      break;     }    }    if (!filled)//如果没有成功填入数字,说明上一步填入的单元格就是错的,会导致后面的单元格怎么填都不对,要回退到上一个单元格重新填    {     path.SetPass(false);     path.Block.SetNumber(null);     foreach (var pos in path.SetList)     {      SudokuBoard[pos.x][pos.y].Candidate.Set(path.Number - 1, true);     }     path = path.Parent;     i = path.X < 0 ? 0 : path.X;     j = path.Y < 0 ? 0 : path.Y;    }   }  }  ///  /// 初始化候选项  ///  private void InitCandidate()  {   //初始化每行空缺待填的数字   var rb = new List();   for (int i = 0; i < SudokuBoard.Length; i++)   {    var r = new BitArray(SudokuBoard.Length);    r.SetAll(true);    for (int j = 0; j < SudokuBoard[i].Length; j++)    {     //如果i行j列是条件(题目)给出的数,设置数字不能再填(r[x] == false 表示 i 行不能再填 x + 1,下标加1表示数独可用的数字,下标对应的值表示下标加1所表示的数是否还能填入该行)     if (SudokuBoard[i][j].IsCondition || SudokuBoard[i][j].IsUnique)     {      r.Set(SudokuBoard[i][j].Number.Value - 1, false);     }    }    rb.Add(r);   }   //初始化每列空缺待填的数字   var cb = new List();   for (int j = 0; j < SudokuBoard[0].Length; j++)   {    var c = new BitArray(SudokuBoard[0].Length);    c.SetAll(true);    for (int i = 0; i < SudokuBoard.Length; i++)    {     if (SudokuBoard[i][j].IsCondition || SudokuBoard[i][j].IsUnique)     {      c.Set(SudokuBoard[i][j].Number.Value - 1, false);     }    }    cb.Add(c);   }   //初始化每宫空缺待填的数字(目前只能算标准 n×n 数独的宫)   var gb = new List();   //n表示每宫应有的行、列数(标准数独行列、数相同)   var n = (int)Sqrt(SudokuBoard.Length);   for (int g = 0; g < SudokuBoard.Length; g++)   {    var gba = new BitArray(SudokuBoard.Length);    gba.SetAll(true);    for (int i = g / n * n; i < g / n * n + n; i++)    {     for (int j = g % n * n; j < g % n * n + n; j++)     {      if (SudokuBoard[i][j].IsCondition || SudokuBoard[i][j].IsUnique)      {       gba.Set(SudokuBoard[i][j].Number.Value - 1, false);      }     }    }    gb.Add(gba);   }   //初始化每格可填的候选数字   for (int i = 0; i < SudokuBoard.Length; i++)   {    for (int j = 0; j < SudokuBoard[i].Length; j++)    {     if (!SudokuBoard[i][j].IsCondition)     {      var c = SudokuBoard[i][j].Candidate;      c.SetAll(true);      //当前格能填的数为其所在行、列、宫同时空缺待填的数字,按位与运算后只有同时能填的候选数保持1(可填如当前格),否则变成0      // i / n * n + j / n:根据行号列号计算宫号,      c = c.And(rb[i]).And(cb[j]).And(gb[i / n * n + j / n]);      SudokuBoard[i][j].SetCandidate(c);     }    }   }  }  ///  /// 求解开始时寻找并填入单元格可填的数,减少解空间  ///  /// 是否填入过数字,如果为false,表示能立即确定待填数字的单元格已经没有,要开始暴力搜索了  private bool FillUniqueNumber(ref PathTree path)  {   var filled = false;   for (int i = 0; i < SudokuBoard.Length; i++)   {    for (int j = 0; j < SudokuBoard[i].Length; j++)    {     if (!SudokuBoard[i][j].IsCondition && !SudokuBoard[i][j].IsUnique)     {      var canFillCount = 0;      var index = -1;      for (int k = 0; k < SudokuBoard[i][j].Candidate.Length; k++)      {       if (SudokuBoard[i][j].Candidate[k])       {        index = k;        canFillCount++;       }       if (canFillCount > 1)       {        break;       }      }      if (canFillCount == 0)      {       throw new Exception("有单元格无法填入任何数字,数独无解");      }      if (canFillCount == 1)      {       var num = (byte)(index + 1);       SudokuBoard[i][j].SetNumber(num);       SudokuBoard[i][j].SetUnique();       filled = true;       var upRes = UpdateCandidate(i, j, num);       if (!upRes.canFill)       {        throw new Exception("有单元格无法填入任何数字,数独无解");       }       path = new PathTree(SudokuBoard[i][j], i, j, num, path);       path.SetList = upRes.setList;      }     }    }   }   return filled;  }  ///  /// 更新单元格所在行、列、宫的其它单元格能填的数字候选,如果没有,会撤销更新  ///  /// 行号  /// 列号  /// 要剔除的候选数字  /// 更新候选数后,所有被更新的单元格是否都有可填的候选数字  private (bool canFill, (byte x, byte y)[] setList) UpdateCandidate(int row, int column, byte canNotFillNumber)  {   var canFill = true;   var list = new List(); // 记录修改过的单元格,方便撤回修改   bool CanFillNumber(int i, int j)   {    var re = true;    var _canFill = false;    for (int k = 0; k < SudokuBoard[i][j].Candidate.Length; k++)    {     if (SudokuBoard[i][j].Candidate[k])     {      _canFill = true;      break;     }    }    if (!_canFill)    {     re = false;    }    return re;   }   bool Update(int i, int j)   {    if (!(i == row && j == column) && !SudokuBoard[i][j].IsCondition && !SudokuBoard[i][j].IsUnique && SudokuBoard[i][j].Candidate[canNotFillNumber - 1])    {     SudokuBoard[i][j].Candidate.Set(canNotFillNumber - 1, false);     list.Add(SudokuBoard[i][j]);     return CanFillNumber(i, j);    }    else    {     return true;    }   }   //更新该行其余列   for (int j = 0; j < SudokuBoard[row].Length; j++)   {    canFill = Update(row, j);    if (!canFill)    {     break;    }   }   if (canFill) //只在行更新时没发现无数可填的单元格时进行列更新才有意义   {    //更新该列其余行    for (int i = 0; i < SudokuBoard.Length; i++)    {     canFill = Update(i, column);     if (!canFill)     {      break;     }    }   }   if (canFill)//只在行、列更新时都没发现无数可填的单元格时进行宫更新才有意义   {    //更新该宫其余格    //n表示每宫应有的行、列数(标准数独行列、数相同)    var n = (int)Sqrt(SudokuBoard.Length);    //g为宫的编号,根据行号列号计算    var g = row / n * n + column / n;    for (int i = g / n * n; i < g / n * n + n; i++)    {     for (int j = g % n * n; j < g % n * n + n; j++)     {      canFill = Update(i, j);      if (!canFill)      {       goto canNotFill;      }     }    }    canNotFill:;   }   //如果发现存在没有任何数字可填的单元格,撤回所有候选修改   if (!canFill)   {    foreach (var cell in list)    {     cell.Candidate.Set(canNotFillNumber - 1, true);    }   }   return (canFill, list.Select(x => (x.X, x.Y)).ToArray());  }  ///  /// 寻找下一个要尝试填数的格  ///  /// 起始行号  /// 起始列号  /// 找到的下一个行列号,没有找到返回-1  private (int x, int y) NextBlock(int i = 0, int j = 0)  {   for (; i < SudokuBoard.Length; i++)   {    for (; j < SudokuBoard[i].Length; j++)    {     if (!SudokuBoard[i][j].IsCondition && !SudokuBoard[i][j].IsUnique && !SudokuBoard[i][j].Number.HasValue)     {      return (i, j);     }    }    j = 0;   }   return (-1, -1);  }  public override string ToString()  {   static string Str(SudokuBlock b)   {    var n1 = new[] { "①", "②", "③", "④", "⑤", "⑥", "⑦", "⑧", "⑨" };    var n2 = new[] { "⑴", "⑵", "⑶", "⑷", "⑸", "⑹", "⑺", "⑻", "⑼" };    return b.Number.HasValue     ? b.IsCondition      ? " " + b.Number      : b.IsUnique       ? n1[b.Number.Value - 1]       : n2[b.Number.Value - 1]     : "▢";   }   return$@"{Str(SudokuBoard[0][0])},{Str(SudokuBoard[0][1])},{Str(SudokuBoard[0][2])},{Str(SudokuBoard[0][3])},{Str(SudokuBoard[0][4])},{Str(SudokuBoard[0][5])},{Str(SudokuBoard[0][6])},{Str(SudokuBoard[0][7])},{Str(SudokuBoard[0][8])}{Str(SudokuBoard[1][0])},{Str(SudokuBoard[1][1])},{Str(SudokuBoard[1][2])},{Str(SudokuBoard[1][3])},{Str(SudokuBoard[1][4])},{Str(SudokuBoard[1][5])},{Str(SudokuBoard[1][6])},{Str(SudokuBoard[1][7])},{Str(SudokuBoard[1][8])}{Str(SudokuBoard[2][0])},{Str(SudokuBoard[2][1])},{Str(SudokuBoard[2][2])},{Str(SudokuBoard[2][3])},{Str(SudokuBoard[2][4])},{Str(SudokuBoard[2][5])},{Str(SudokuBoard[2][6])},{Str(SudokuBoard[2][7])},{Str(SudokuBoard[2][8])}{Str(SudokuBoard[3][0])},{Str(SudokuBoard[3][1])},{Str(SudokuBoard[3][2])},{Str(SudokuBoard[3][3])},{Str(SudokuBoard[3][4])},{Str(SudokuBoard[3][5])},{Str(SudokuBoard[3][6])},{Str(SudokuBoard[3][7])},{Str(SudokuBoard[3][8])}{Str(SudokuBoard[4][0])},{Str(SudokuBoard[4][1])},{Str(SudokuBoard[4][2])},{Str(SudokuBoard[4][3])},{Str(SudokuBoard[4][4])},{Str(SudokuBoard[4][5])},{Str(SudokuBoard[4][6])},{Str(SudokuBoard[4][7])},{Str(SudokuBoard[4][8])}{Str(SudokuBoard[5][0])},{Str(SudokuBoard[5][1])},{Str(SudokuBoard[5][2])},{Str(SudokuBoard[5][3])},{Str(SudokuBoard[5][4])},{Str(SudokuBoard[5][5])},{Str(SudokuBoard[5][6])},{Str(SudokuBoard[5][7])},{Str(SudokuBoard[5][8])}{Str(SudokuBoard[6][0])},{Str(SudokuBoard[6][1])},{Str(SudokuBoard[6][2])},{Str(SudokuBoard[6][3])},{Str(SudokuBoard[6][4])},{Str(SudokuBoard[6][5])},{Str(SudokuBoard[6][6])},{Str(SudokuBoard[6][7])},{Str(SudokuBoard[6][8])}{Str(SudokuBoard[7][0])},{Str(SudokuBoard[7][1])},{Str(SudokuBoard[7][2])},{Str(SudokuBoard[7][3])},{Str(SudokuBoard[7][4])},{Str(SudokuBoard[7][5])},{Str(SudokuBoard[7][6])},{Str(SudokuBoard[7][7])},{Str(SudokuBoard[7][8])}{Str(SudokuBoard[8][0])},{Str(SudokuBoard[8][1])},{Str(SudokuBoard[8][2])},{Str(SudokuBoard[8][3])},{Str(SudokuBoard[8][4])},{Str(SudokuBoard[8][5])},{Str(SudokuBoard[8][6])},{Str(SudokuBoard[8][7])},{Str(SudokuBoard[8][8])}";  } }

大多数都有注释,配合注释应该不难理解,如有问题欢迎评论区交流。稍微说一下,重载ToString是为了方便调试和查看状态,其中空心方框表示未填写数字的单元格,数字表示题目给出数字的单元格,圈数字表示数单元格填写的数字,括号数字表示有多个候选数通过尝试(暴力搜索)确定的数字。注意类文件最上面有一个using static System.Math; 导入静态类,不然每次调用数学函数都要 Math. ,很烦。

求解过程信息

public class PathTree {  public PathTree Parent { get; set; }  public List Children { get; } = new List();  public SudokuBlock Block { get; }  public int X { get; }  public int Y { get; }  public int Number { get; }  public bool Pass { get; private set; } = true;  public (byte x, byte y)[] SetList { get; set; }  public PathTree(SudokuBlock block, int x, int y, int number)  {   Block = block;   X = x;   Y = y;   Number = number;  }  public PathTree(SudokuBlock block, int row, int column, int number, PathTree parent)   : this(block, row, column, number)  {   Parent = parent;   Parent.Children.Add(this);  }  public void SetPass(bool pass)  {   Pass = pass;  } }

其中记录了每个步骤在哪个单元格填写了哪个数字,上一步是哪一步,之后尝试过哪些步骤,这一步是否会导致之后的步骤出现死路,填写数字后影响到的单元格和候选数字(用来在悔步的时候恢复相应单元格的候选数字)。

答案存储

public class SudokuState {  public SudokuBlock[][] SudokuBoard { get; }  public SudokuState(SudokuSolver sudoku)  {   SudokuBoard = new SudokuBlock[sudoku.SudokuBoard.Length][];   //初始化数独的行   for (int i = 0; i < sudoku.SudokuBoard.Length; i++)   {    SudokuBoard[i] = new SudokuBlock[sudoku.SudokuBoard[i].Length];    //初始化每行的列    for (int j = 0; j < sudoku.SudokuBoard[i].Length; j++)    {     SudokuBoard[i][j] = new SudokuBlock(      sudoku.SudokuBoard[i][j].IsCondition      , null      , sudoku.SudokuBoard[i][j].Number      , (byte)i      , (byte)j);     if (sudoku.SudokuBoard[i][j].IsUnique)     {      SudokuBoard[i][j].SetUnique();     }    }   }  }  public override string ToString()  {   static string Str(SudokuBlock b)   {    var n1 = new[] { "①", "②", "③", "④", "⑤", "⑥", "⑦", "⑧", "⑨" };    var n2 = new[] { "⑴", "⑵", "⑶", "⑷", "⑸", "⑹", "⑺", "⑻", "⑼" };    return b.Number.HasValue     ? b.IsCondition      ? " " + b.Number      : b.IsUnique       ? n1[b.Number.Value - 1]       : n2[b.Number.Value - 1]     : "▢";   }   return$@"{Str(SudokuBoard[0][0])},{Str(SudokuBoard[0][1])},{Str(SudokuBoard[0][2])},{Str(SudokuBoard[0][3])},{Str(SudokuBoard[0][4])},{Str(SudokuBoard[0][5])},{Str(SudokuBoard[0][6])},{Str(SudokuBoard[0][7])},{Str(SudokuBoard[0][8])}{Str(SudokuBoard[1][0])},{Str(SudokuBoard[1][1])},{Str(SudokuBoard[1][2])},{Str(SudokuBoard[1][3])},{Str(SudokuBoard[1][4])},{Str(SudokuBoard[1][5])},{Str(SudokuBoard[1][6])},{Str(SudokuBoard[1][7])},{Str(SudokuBoard[1][8])}{Str(SudokuBoard[2][0])},{Str(SudokuBoard[2][1])},{Str(SudokuBoard[2][2])},{Str(SudokuBoard[2][3])},{Str(SudokuBoard[2][4])},{Str(SudokuBoard[2][5])},{Str(SudokuBoard[2][6])},{Str(SudokuBoard[2][7])},{Str(SudokuBoard[2][8])}{Str(SudokuBoard[3][0])},{Str(SudokuBoard[3][1])},{Str(SudokuBoard[3][2])},{Str(SudokuBoard[3][3])},{Str(SudokuBoard[3][4])},{Str(SudokuBoard[3][5])},{Str(SudokuBoard[3][6])},{Str(SudokuBoard[3][7])},{Str(SudokuBoard[3][8])}{Str(SudokuBoard[4][0])},{Str(SudokuBoard[4][1])},{Str(SudokuBoard[4][2])},{Str(SudokuBoard[4][3])},{Str(SudokuBoard[4][4])},{Str(SudokuBoard[4][5])},{Str(SudokuBoard[4][6])},{Str(SudokuBoard[4][7])},{Str(SudokuBoard[4][8])}{Str(SudokuBoard[5][0])},{Str(SudokuBoard[5][1])},{Str(SudokuBoard[5][2])},{Str(SudokuBoard[5][3])},{Str(SudokuBoard[5][4])},{Str(SudokuBoard[5][5])},{Str(SudokuBoard[5][6])},{Str(SudokuBoard[5][7])},{Str(SudokuBoard[5][8])}{Str(SudokuBoard[6][0])},{Str(SudokuBoard[6][1])},{Str(SudokuBoard[6][2])},{Str(SudokuBoard[6][3])},{Str(SudokuBoard[6][4])},{Str(SudokuBoard[6][5])},{Str(SudokuBoard[6][6])},{Str(SudokuBoard[6][7])},{Str(SudokuBoard[6][8])}{Str(SudokuBoard[7][0])},{Str(SudokuBoard[7][1])},{Str(SudokuBoard[7][2])},{Str(SudokuBoard[7][3])},{Str(SudokuBoard[7][4])},{Str(SudokuBoard[7][5])},{Str(SudokuBoard[7][6])},{Str(SudokuBoard[7][7])},{Str(SudokuBoard[7][8])}{Str(SudokuBoard[8][0])},{Str(SudokuBoard[8][1])},{Str(SudokuBoard[8][2])},{Str(SudokuBoard[8][3])},{Str(SudokuBoard[8][4])},{Str(SudokuBoard[8][5])},{Str(SudokuBoard[8][6])},{Str(SudokuBoard[8][7])},{Str(SudokuBoard[8][8])}";  } }

没什么好说的,就是保存答案的,因为有些数独的解不,将来有机会扩展求多解时避免相互覆盖。

还有一个辅助类,单元格定义

public class SudokuBlock {  ///

 /// 填入的数字  ///  public byte? Number { get; private set; }  ///  /// X坐标  ///  public byte X { get; }  ///  /// Y坐标  ///  public byte Y { get; }  ///  /// 候选数字,下标所示状态表示数字“下标加1”是否能填入  ///  public BitArray Candidate { get; private set; }  ///  /// 是否为条件(题目)给出数字的单元格  ///  public bool IsCondition { get; }  ///  /// 是否为游戏开始就能确定可填数字的单元格  ///  public bool IsUnique { get; private set; }  public SudokuBlock(bool isCondition, BitArray candidate, byte? number, byte x, byte y)  {   IsCondition = isCondition;   Candidate = candidate;   Number = number;   IsUnique = false;   X = x;   Y = y;  }  public void SetNumber(byte? number)  {   Number = number;  }  public void SetCandidate(BitArray candidate)  {   Candidate = candidate;  }  public void SetUnique()  {   IsUnique = true;  } }

测试代码

static void Main(string[] args)  {   //模板   //byte[][] game = new byte[][] {   // new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0},   // new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0},   // new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0},   // new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0},   // new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0},   // new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0},   // new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0},   // new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0},   // new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0},};   //这个简单,无需尝试,一直填数单元格,填完后剩下的单元格又有会变数单元格   //byte[][] game = new byte[][] {   // new byte[]{0, 5, 0, 7, 0, 6, 0, 1, 0},   // new byte[]{0, 8, 0, 0, 9, 0, 0, 6, 0},   // new byte[]{0, 6, 9, 0, 8, 0, 7, 3, 0},   // new byte[]{0, 1, 0, 0, 4, 0, 0, 0, 6},   // new byte[]{6, 0, 7, 1, 0, 3, 8, 0, 5},   // new byte[]{9, 0, 0, 0, 0, 8, 0, 2, 0},   // new byte[]{0, 2, 4, 0, 1, 0, 6, 5, 0},   // new byte[]{0, 7, 0, 0, 6, 0, 0, 4, 0},   // new byte[]{0, 9, 0, 4, 0, 2, 0, 8, 0},};   //可以填一部分数单元格,剩下一部分需要尝试,调试用   //byte[][] game = new byte[][] {   // new byte[]{7, 0, 0, 5, 0, 0, 0, 0, 2},   // new byte[]{0, 3, 0, 0, 0, 4, 6, 0, 0},   // new byte[]{0, 0, 2, 6, 0, 0, 0, 0, 0},   // new byte[]{2, 0, 0, 0, 7, 0, 0, 0, 5},   // new byte[]{5, 0, 0, 1, 0, 3, 0, 0, 6},   // new byte[]{3, 0, 0, 4, 0, 0, 0, 0, 9},   // new byte[]{0, 0, 0, 0, 0, 1, 5, 0, 0},   // new byte[]{0, 0, 7, 2, 0, 0, 0, 4, 0},   // new byte[]{4, 0, 0, 0, 0, 9, 0, 0, 7},};   //全部要靠尝试来填   byte[][] game = new byte[][] {    new byte[]{1, 0, 0, 2, 0, 0, 3, 0, 0},    new byte[]{0, 4, 0, 5, 0, 0, 0, 6, 0},    new byte[]{0, 0, 0, 7, 0, 0, 8, 0, 0},    new byte[]{3, 0, 0, 0, 0, 7, 0, 0, 0},    new byte[]{0, 9, 0, 0, 0, 0, 0, 5, 0},    new byte[]{0, 0, 0, 6, 0, 0, 0, 0, 7},    new byte[]{0, 0, 2, 0, 0, 4, 0, 0, 0},    new byte[]{0, 5, 0, 0, 0, 6, 0, 9, 0},    new byte[]{0, 0, 8, 0, 0, 1, 0, 0, 3},};   var su = new SudokuSolver(game);   var r = su.Solve();   var r1 = r.First();   static IEnumerable GetPath(PathTree pathTree)   {    List list = new List();    var path = pathTree;    while (path.Parent != null)    {     list.Add(path);     path = path.Parent;    }    return list.Reverse();   }   var p = GetPath(r1.path).Select(x => $"在 {x.X + 1} 行 {x.Y + 1} 列填入 {x.Number}");   foreach(var step in p)   {    Console.WriteLine(step);   }   Console.WriteLine(r1.sudoku);   Console.ReadKey();  }

关于C#中怎么实现一个数独求解算法问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注创新互联行业资讯频道了解更多相关知识。


当前标题:C#中怎么实现一个数独求解算法-创新互联
标题路径:http://azwzsj.com/article/csjhog.html