代码
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("----------------------------------------------");
}
}
}