![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
using
System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Test
{
class Program
{
static void Main( string [] args)
{
CellMethod method = new CellMethod();
method.StartFillTable();
}
}
/// <summary>
/// 单元格
/// </summary>
class Cell
{
private List < int > candidate; // 候选数
public List < int > Candidate
{
get
{
if (candidate == null )
candidate = new List < int > () { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 };
return candidate;
}
set { candidate = value; }
}
private Dictionary < int , int > duplicateDel; // 重复删除候选数次数的记录,以便恢复
public Dictionary < int , int > DuplicateDel
{
get
{
if (duplicateDel == null )
duplicateDel = new Dictionary < int , int > ();
return duplicateDel;
}
set { duplicateDel = value; }
}
private int value = 0 ;
// 单元格的值
public int Value { get { return this .value; } set { this .value = value; } }
}
/// <summary>
/// 数独方法
/// </summary>
class CellMethod
{
private delegate bool RelativeCellMethod(Cell[,] table, int i, int j, int index);
private int [] nineCells = new int [ 9 ] { 0 , 0 , 0 , 3 , 3 , 3 , 6 , 6 , 6 }; // 处理九宫格的约束
int series;
Random random = new Random();
/// <summary>
/// 开始生成数独
/// </summary>
public void StartFillTable()
{
Cell[,] table = new Cell[ 9 , 9 ];
while ( true )
{
for ( int i = 0 ; i < 9 ; i ++ )
for ( int j = 0 ; j < 9 ; j ++ ) // 初始化数独表
table[i, j] = new Cell();
bool flag = FillCell(table, 0 ); // 填充数独表
if (flag) // 如果生成数独成功,则显示这个数独
Show(table);
Console.ReadKey();
}
}
/// <summary>
/// 为单元格设定值
/// </summary>
/// <param name="cell"> 单元格 </param>
/// <returns> 返回结果 </returns>
public bool SetValue(Cell cell)
{
if (cell.Value != 0 )
{
throw new InvalidOperationException( " 异常!不能重复对一个单元格进行赋值! " );
}
if (cell.Candidate.Count == 0 )
{
return false ;
}
// 随机选取单元格中的一个候选数为值,并移除该候选数
series = random.Next( 0 , cell.Candidate.Count - 1 );
cell.Value = cell.Candidate[series];
cell.Candidate.RemoveAt(series);
return true ;
}
/// <summary>
/// 处理相关单元格
/// </summary>
/// <param name="cellMethod"> 调用方法 </param>
/// <param name="table"> 表 </param>
/// <param name="index"> 索引 </param>
/// <returns> 返回结果 </returns>
private bool DealRelativeCell(RelativeCellMethod cellMethod, Cell[,] table, int index)
{
bool isSetUniqueCellMethod = false ;
if (cellMethod == SetUniqueCellCandidate)
isSetUniqueCellMethod = true ;
bool flag = true ;
Cell cell = table[index / 9 , index % 9 ];
for ( int i = 0 ; i < 9 ; i ++ )
{
// 同列单元格
if (i != index / 9 )
{ // 不能等于本单元格
// 如果任意一个赋值失败则直接跳出循环,以下同理
if (isSetUniqueCellMethod && ! flag) break ;
flag &= cellMethod(table, i, index % 9 , index);
}
// 同行单元格
if (i != index % 9 )
{ // 不能等于本单元格
if (isSetUniqueCellMethod && ! flag) break ;
flag &= cellMethod(table, index / 9 , i, index);
}
}
// 同九宫格单元格的剩余四个单元格
for ( int i = nineCells[index / 9 ]; i < nineCells[index / 9 ] + 3 ; i ++ )
{
for ( int j = nineCells[index % 9 ]; j < nineCells[index % 9 ] + 3 ; j ++ )
{
if (i != index / 9 && j != index % 9 )
{
if (isSetUniqueCellMethod && ! flag) break ;
flag &= cellMethod(table, i, j, index);
}
}
}
if (cellMethod == RemoveCellCandidate && flag)
{ // 如果都移除候选数成功,则判断有没有只剩一个候选数的为赋值的单元格,有则赋值上
RelativeCellMethod setCandidateMethod = new RelativeCellMethod(SetUniqueCellCandidate);
flag &= DealRelativeCell(setCandidateMethod, table, index);
}
if (cellMethod == RecoverCellCandidate)
{
cell.Value = 0 ;
}
return flag;
}
/// <summary>
/// 填充单元格
/// </summary>
/// <param name="table"> 表 </param>
/// <param name="index"> 索引 </param>
/// <returns> 返回结果 </returns>
private bool FillCell(Cell[,] table, int index)
{
RelativeCellMethod removeCandidateMethod = new RelativeCellMethod(RemoveCellCandidate);
RelativeCellMethod recoverCandidateMethod = new RelativeCellMethod(RecoverCellCandidate);
if (index >= 81 )
{ // 如果索引超出范围,则表示数独已成功生成,直接返回
return true ;
}
if (table[index / 9 , index % 9 ].Value != 0 )
{ // 如果索引的单元格已赋值,则直接跳到下一个索引赋值
return FillCell(table, index + 1 );
}
bool flag = true ;
List < int > nextCandidates = new List < int > ();
// 预先保存好改单元格的候选数序列,如果所有候选数都不成功,则把候选数全部还原之后再返回
nextCandidates.AddRange(table[index / 9 , index % 9 ].Candidate);
while (table[index / 9 , index % 9 ].Candidate.Count > 0 && flag)
{ // 如果单元格候选数个数大于0,且标记为真,则循环试探候选数
SetValue(table[index / 9 , index % 9 ]); // 为单元格赋值
flag &= DealRelativeCell(removeCandidateMethod, table, index); // 移除相关单元格的对应这个值的候选数
if ( ! flag)
{ // 如果移除候选数失败,则恢复候选数,并继续下个循环
DealRelativeCell(recoverCandidateMethod, table, index);
}
else
{ // 如果移除候选数成功,则继续试探填充下一个单元格
flag &= FillCell(table, index + 1 );
if ( ! flag)
{ // 如果填充下一个单元格失败,则恢复候选数,并继续下个循环
DealRelativeCell(recoverCandidateMethod, table, index);
}
else
{ // 如果填充下一个单元格成功,则直接返回(运行到这里肯定表示整个数独已成功生成!)
return true ;
}
}
flag = ! flag; // 把标志取反,继续下个循环
}
if (table[index / 9 , index % 9 ].Candidate.Count == 0 )
{ // 如果所有候选数都是过了且全部失败,恢复此单元格的候选数,并返回false
table[index / 9 , index % 9 ].Candidate.AddRange(nextCandidates);
return false ;
}
return flag;
}
/// <summary>
/// 移除单元格的候选数
/// </summary>
/// <param name="table"> 表 </param>
/// <param name="i"> 行 </param>
/// <param name="j"> 列 </param>
/// <param name="index"> 索引 </param>
/// <returns> 返回结果 </returns>
static private bool RemoveCellCandidate(Cell[,] table, int i, int j, int index)
{
int value = table[index / 9 , index % 9 ].Value;
bool flag = true ;
if (table[i, j].Candidate.Contains(value))
{ // 如果单元格候选数有此数,移除之
table[i, j].Candidate.Remove(value);
if (table[i, j].Candidate.Count == 0 && table[i, j].Value == 0 )
{ // 如果单元格移除此候选数之后,并未赋值且候选数量为0,则失败,回滚
flag = false ;
}
}
else if (table[i, j].DuplicateDel.ContainsKey(value))
{ // 如果单元格候选数没有此数,且在重复删除的字典里有此数,则重复删除字典此数对应的键值的值+1
table[i, j].DuplicateDel[value] ++ ;
}
else
{ // 如果单元格候选数没有此数,且在重复删除的字典里没有此数,则重复删除字典添加此数的键值,并赋值为1
table[i, j].DuplicateDel.Add(value, 1 );
}
return flag;
}
/// <summary>
/// 恢复单元格的候选数
/// </summary>
/// <param name="table"> 表 </param>
/// <param name="i"> 行 </param>
/// <param name="j"> 列 </param>
/// <param name="index"> 索引 </param>
/// <returns> 返回结果 </returns>
private bool RecoverCellCandidate(Cell[,] table, int i, int j, int index)
{
int value = table[index / 9 , index % 9 ].Value;
bool flag = true ;
if (table[i, j].DuplicateDel.ContainsKey(value))
{ // 如果在重复删除的字典里有此数,则重复删除字典此数对应的键值的值-1
if ( -- table[i, j].DuplicateDel[value] == 0 )
{
table[i, j].DuplicateDel.Remove(value);
}
}
else if ( ! table[i, j].Candidate.Contains(value))
{ // 如果单元格候选数没有此数,添加之
table[i, j].Candidate.Add(value);
}
return flag;
}
/// <summary>
/// 为候选数个数为一的单元格赋值
/// </summary>
/// <param name="table"> 表 </param>
/// <param name="i"> 行 </param>
/// <param name="j"> 列 </param>
/// <param name="index"> 索引 </param>
/// <returns> 返回结果 </returns>
private bool SetUniqueCellCandidate(Cell[,] table, int i, int j, int index)
{
RelativeCellMethod removeCandidateMethod = new RelativeCellMethod(RemoveCellCandidate);
RelativeCellMethod recoverCandidateMethod = new RelativeCellMethod(RecoverCellCandidate);
bool flag = true ;
if (table[i, j].Value == 0 && table[i, j].Candidate.Count == 1 )
{ // 如果单元格移除此候选数之后,候选数量为1,则直接为此单元格赋值剩下的候选数
int oldValue = table[i, j].Candidate[ 0 ]; // 保存该唯一候选数,以便如果失败恢复
flag &= SetValue(table[i, j]);
flag &= DealRelativeCell(removeCandidateMethod, table, i * 9 + j);
if ( ! flag)
{ // 如果移除候选数失败,则恢复候选数,回滚
DealRelativeCell(recoverCandidateMethod, table, i * 9 + j);
table[i, j].Candidate.Add(oldValue);
flag &= false ;
}
}
return flag;
}
/// <summary>
/// 显示数独
/// </summary>
/// <param name="table"> 表 </param>
private void Show(Cell[,] table)
{
for ( int i = 0 ; i < 9 ; i ++ )
{
for ( int j = 0 ; j < 9 ; j ++ )
{
Console.Write( " {0,2} " , table[i, j].Value);
}
Console.WriteLine();
}
Console.WriteLine( " ---------------------------------------------- " );
}
}
}
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Test
{
class Program
{
static void Main( string [] args)
{
CellMethod method = new CellMethod();
method.StartFillTable();
}
}
/// <summary>
/// 单元格
/// </summary>
class Cell
{
private List < int > candidate; // 候选数
public List < int > Candidate
{
get
{
if (candidate == null )
candidate = new List < int > () { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 };
return candidate;
}
set { candidate = value; }
}
private Dictionary < int , int > duplicateDel; // 重复删除候选数次数的记录,以便恢复
public Dictionary < int , int > DuplicateDel
{
get
{
if (duplicateDel == null )
duplicateDel = new Dictionary < int , int > ();
return duplicateDel;
}
set { duplicateDel = value; }
}
private int value = 0 ;
// 单元格的值
public int Value { get { return this .value; } set { this .value = value; } }
}
/// <summary>
/// 数独方法
/// </summary>
class CellMethod
{
private delegate bool RelativeCellMethod(Cell[,] table, int i, int j, int index);
private int [] nineCells = new int [ 9 ] { 0 , 0 , 0 , 3 , 3 , 3 , 6 , 6 , 6 }; // 处理九宫格的约束
int series;
Random random = new Random();
/// <summary>
/// 开始生成数独
/// </summary>
public void StartFillTable()
{
Cell[,] table = new Cell[ 9 , 9 ];
while ( true )
{
for ( int i = 0 ; i < 9 ; i ++ )
for ( int j = 0 ; j < 9 ; j ++ ) // 初始化数独表
table[i, j] = new Cell();
bool flag = FillCell(table, 0 ); // 填充数独表
if (flag) // 如果生成数独成功,则显示这个数独
Show(table);
Console.ReadKey();
}
}
/// <summary>
/// 为单元格设定值
/// </summary>
/// <param name="cell"> 单元格 </param>
/// <returns> 返回结果 </returns>
public bool SetValue(Cell cell)
{
if (cell.Value != 0 )
{
throw new InvalidOperationException( " 异常!不能重复对一个单元格进行赋值! " );
}
if (cell.Candidate.Count == 0 )
{
return false ;
}
// 随机选取单元格中的一个候选数为值,并移除该候选数
series = random.Next( 0 , cell.Candidate.Count - 1 );
cell.Value = cell.Candidate[series];
cell.Candidate.RemoveAt(series);
return true ;
}
/// <summary>
/// 处理相关单元格
/// </summary>
/// <param name="cellMethod"> 调用方法 </param>
/// <param name="table"> 表 </param>
/// <param name="index"> 索引 </param>
/// <returns> 返回结果 </returns>
private bool DealRelativeCell(RelativeCellMethod cellMethod, Cell[,] table, int index)
{
bool isSetUniqueCellMethod = false ;
if (cellMethod == SetUniqueCellCandidate)
isSetUniqueCellMethod = true ;
bool flag = true ;
Cell cell = table[index / 9 , index % 9 ];
for ( int i = 0 ; i < 9 ; i ++ )
{
// 同列单元格
if (i != index / 9 )
{ // 不能等于本单元格
// 如果任意一个赋值失败则直接跳出循环,以下同理
if (isSetUniqueCellMethod && ! flag) break ;
flag &= cellMethod(table, i, index % 9 , index);
}
// 同行单元格
if (i != index % 9 )
{ // 不能等于本单元格
if (isSetUniqueCellMethod && ! flag) break ;
flag &= cellMethod(table, index / 9 , i, index);
}
}
// 同九宫格单元格的剩余四个单元格
for ( int i = nineCells[index / 9 ]; i < nineCells[index / 9 ] + 3 ; i ++ )
{
for ( int j = nineCells[index % 9 ]; j < nineCells[index % 9 ] + 3 ; j ++ )
{
if (i != index / 9 && j != index % 9 )
{
if (isSetUniqueCellMethod && ! flag) break ;
flag &= cellMethod(table, i, j, index);
}
}
}
if (cellMethod == RemoveCellCandidate && flag)
{ // 如果都移除候选数成功,则判断有没有只剩一个候选数的为赋值的单元格,有则赋值上
RelativeCellMethod setCandidateMethod = new RelativeCellMethod(SetUniqueCellCandidate);
flag &= DealRelativeCell(setCandidateMethod, table, index);
}
if (cellMethod == RecoverCellCandidate)
{
cell.Value = 0 ;
}
return flag;
}
/// <summary>
/// 填充单元格
/// </summary>
/// <param name="table"> 表 </param>
/// <param name="index"> 索引 </param>
/// <returns> 返回结果 </returns>
private bool FillCell(Cell[,] table, int index)
{
RelativeCellMethod removeCandidateMethod = new RelativeCellMethod(RemoveCellCandidate);
RelativeCellMethod recoverCandidateMethod = new RelativeCellMethod(RecoverCellCandidate);
if (index >= 81 )
{ // 如果索引超出范围,则表示数独已成功生成,直接返回
return true ;
}
if (table[index / 9 , index % 9 ].Value != 0 )
{ // 如果索引的单元格已赋值,则直接跳到下一个索引赋值
return FillCell(table, index + 1 );
}
bool flag = true ;
List < int > nextCandidates = new List < int > ();
// 预先保存好改单元格的候选数序列,如果所有候选数都不成功,则把候选数全部还原之后再返回
nextCandidates.AddRange(table[index / 9 , index % 9 ].Candidate);
while (table[index / 9 , index % 9 ].Candidate.Count > 0 && flag)
{ // 如果单元格候选数个数大于0,且标记为真,则循环试探候选数
SetValue(table[index / 9 , index % 9 ]); // 为单元格赋值
flag &= DealRelativeCell(removeCandidateMethod, table, index); // 移除相关单元格的对应这个值的候选数
if ( ! flag)
{ // 如果移除候选数失败,则恢复候选数,并继续下个循环
DealRelativeCell(recoverCandidateMethod, table, index);
}
else
{ // 如果移除候选数成功,则继续试探填充下一个单元格
flag &= FillCell(table, index + 1 );
if ( ! flag)
{ // 如果填充下一个单元格失败,则恢复候选数,并继续下个循环
DealRelativeCell(recoverCandidateMethod, table, index);
}
else
{ // 如果填充下一个单元格成功,则直接返回(运行到这里肯定表示整个数独已成功生成!)
return true ;
}
}
flag = ! flag; // 把标志取反,继续下个循环
}
if (table[index / 9 , index % 9 ].Candidate.Count == 0 )
{ // 如果所有候选数都是过了且全部失败,恢复此单元格的候选数,并返回false
table[index / 9 , index % 9 ].Candidate.AddRange(nextCandidates);
return false ;
}
return flag;
}
/// <summary>
/// 移除单元格的候选数
/// </summary>
/// <param name="table"> 表 </param>
/// <param name="i"> 行 </param>
/// <param name="j"> 列 </param>
/// <param name="index"> 索引 </param>
/// <returns> 返回结果 </returns>
static private bool RemoveCellCandidate(Cell[,] table, int i, int j, int index)
{
int value = table[index / 9 , index % 9 ].Value;
bool flag = true ;
if (table[i, j].Candidate.Contains(value))
{ // 如果单元格候选数有此数,移除之
table[i, j].Candidate.Remove(value);
if (table[i, j].Candidate.Count == 0 && table[i, j].Value == 0 )
{ // 如果单元格移除此候选数之后,并未赋值且候选数量为0,则失败,回滚
flag = false ;
}
}
else if (table[i, j].DuplicateDel.ContainsKey(value))
{ // 如果单元格候选数没有此数,且在重复删除的字典里有此数,则重复删除字典此数对应的键值的值+1
table[i, j].DuplicateDel[value] ++ ;
}
else
{ // 如果单元格候选数没有此数,且在重复删除的字典里没有此数,则重复删除字典添加此数的键值,并赋值为1
table[i, j].DuplicateDel.Add(value, 1 );
}
return flag;
}
/// <summary>
/// 恢复单元格的候选数
/// </summary>
/// <param name="table"> 表 </param>
/// <param name="i"> 行 </param>
/// <param name="j"> 列 </param>
/// <param name="index"> 索引 </param>
/// <returns> 返回结果 </returns>
private bool RecoverCellCandidate(Cell[,] table, int i, int j, int index)
{
int value = table[index / 9 , index % 9 ].Value;
bool flag = true ;
if (table[i, j].DuplicateDel.ContainsKey(value))
{ // 如果在重复删除的字典里有此数,则重复删除字典此数对应的键值的值-1
if ( -- table[i, j].DuplicateDel[value] == 0 )
{
table[i, j].DuplicateDel.Remove(value);
}
}
else if ( ! table[i, j].Candidate.Contains(value))
{ // 如果单元格候选数没有此数,添加之
table[i, j].Candidate.Add(value);
}
return flag;
}
/// <summary>
/// 为候选数个数为一的单元格赋值
/// </summary>
/// <param name="table"> 表 </param>
/// <param name="i"> 行 </param>
/// <param name="j"> 列 </param>
/// <param name="index"> 索引 </param>
/// <returns> 返回结果 </returns>
private bool SetUniqueCellCandidate(Cell[,] table, int i, int j, int index)
{
RelativeCellMethod removeCandidateMethod = new RelativeCellMethod(RemoveCellCandidate);
RelativeCellMethod recoverCandidateMethod = new RelativeCellMethod(RecoverCellCandidate);
bool flag = true ;
if (table[i, j].Value == 0 && table[i, j].Candidate.Count == 1 )
{ // 如果单元格移除此候选数之后,候选数量为1,则直接为此单元格赋值剩下的候选数
int oldValue = table[i, j].Candidate[ 0 ]; // 保存该唯一候选数,以便如果失败恢复
flag &= SetValue(table[i, j]);
flag &= DealRelativeCell(removeCandidateMethod, table, i * 9 + j);
if ( ! flag)
{ // 如果移除候选数失败,则恢复候选数,回滚
DealRelativeCell(recoverCandidateMethod, table, i * 9 + j);
table[i, j].Candidate.Add(oldValue);
flag &= false ;
}
}
return flag;
}
/// <summary>
/// 显示数独
/// </summary>
/// <param name="table"> 表 </param>
private void Show(Cell[,] table)
{
for ( int i = 0 ; i < 9 ; i ++ )
{
for ( int j = 0 ; j < 9 ; j ++ )
{
Console.Write( " {0,2} " , table[i, j].Value);
}
Console.WriteLine();
}
Console.WriteLine( " ---------------------------------------------- " );
}
}
}