最近公司大肆流行数独之风,自从学会了编程之后,人就变得越来越懒了,能用计算机算的东西就不大愿意用人脑来算了,记得我以前写过一个数独求解的文章,现在看来,觉得当时写得有点烂,就把它重写了一下。
class Sudoku
{
int[,] Numbers { get; set; }
Sudoku(int[,] numbers)
{
this.Numbers = numbers.Clone() as int[,];
}
bool Process()
{
var point = GetAvaiblePoints().FirstOrDefault();
if (point == null) //所有空格都已经填充
return true;
foreach (var item in GetAvaibleNumbers(point))
{
Numbers[point.X, point.Y] = item;
if (Process())
return true;
}
//填遍所有可用数字(无可用数字),仍不能正确求解
Numbers[point.X, point.Y] = 0;
return false;
}
readonly int[] avaible = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
IEnumerable<int> GetAvaibleNumbers(Point p)
{
return avaible.Except(Horizontal(p))
.Except(Vertical(p))
.Except(Rectangle(p));
}
readonly int[] indexs = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
IEnumerable<int> Horizontal(Point p) { return indexs.Select(i => Numbers[i , p.Y]); }
IEnumerable<int> Vertical(Point p) { return indexs.Select(i => Numbers[p.X, i]); }
IEnumerable<int> Rectangle(Point p)
{
var x = p.X - p.X % 3;
var y = p.Y - p.Y % 3;
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
yield return Numbers[x + i, y + j];
}
IEnumerable<Point> GetAvaiblePoints()
{
return from x in indexs
from y in indexs
where Numbers[x, y] == 0
select new Point(x, y);
}
/// <summary>
///数独求解
/// </summary>
/// <param name="numbers"> 9*9数组,空格用0代替,已有值的范围1~9,本函数不对参数有效性检测,不要测试程序的健壮性</param>
/// <returns>返回一个可行的数独解,无解返回null</returns>
public static int[,] Process(int[,] numbers)
{
var soudu = new Sudoku(numbers);
if (soudu.Process())
return soudu.Numbers;
else
return null;
}
//坐标点,可以用Tuple<int,int>代替
class Point
{
public int X { get; set; }
public int Y { get; set; }
public Point(int x, int y)
{
this.X = x;
this.Y = y;
}
public override string ToString() { return X + "," + Y; }
}
}
两个算法没有什么本质上的区别,但这个更容易看懂,并且这个大量使用了linq,算法是更加容易改造(改顺序尝试为随机尝试,改线性求解为并发)的,有点函数式编程的影子,什么时候有空再用F#来写一下。