C#中的数独算法

我需要一个衬里(或靠近它)来validation给定的9个元素的数组不包含重复的数字1,2,3,…,9。 重复零不计数(它们代表空单元格)。

到目前为止我得到的最好的是:

var a = new int[9] {1,2,3,4,5,6,7,8,9}; var itIsOk = a.Join(a, i => i, j => j, (x, y) => x) .GroupBy(y => y).Where(g => g.Key > 0 && g.Count() > 1).Count() == 0; 

如果你不想解决我的问题:),你至少可以告诉上述算法是否正常工作吗?

并且,是的,已经读过这个 。

幸运的是,我不久前自己建造了一个数独求解器:)整个事情大概是200行的C#,它可以解决我在4秒或更短时间内找到的最棘手的难题。

由于使用.Count,性能可能不是很好,但它应该工作:

 !a.Any(i => i != 0 && a.Where(j => j != 0 && i == j).Count > 1) 

此外, j != 0部分并不是真正需要的,但它应该有助于事情运行得更快。

[编辑:] kvb的回答给了我另一个想法:

 !a.Where(i => i != 0).GroupBy(i => i).Any(gp => gp.Count() > 1) 

分组之前过滤0。 虽然基于IEnumerable的工作方式,但它可能无关紧要。

无论哪种方式,为了获得最佳性能,请使用新的IEnumerable扩展方法替换.Count > 1 ,如下所示:

 bool MoreThanOne(this IEnumerable enumerable, Predictate pred) { bool flag = false; foreach (T item in enumerable) { if (pred(item)) { if (flag) return true; flag = true; } } return false; } 

它可能无关紧要,因为数组限制为9个项目,但如果你调用它很多,它可能会加起来。

这比LINQ解决方案快大约50-250倍(取决于发现重复的早期时间):

 public static bool IsValid(int[] values) { int flag = 0; foreach (int value in values) { if (value != 0) { int bit = 1 << value; if ((flag & bit) != 0) return false; flag |= bit; } } return true; } 

!a.GroupBy(i => i).Any(gp => gp.Key != 0 && gp.Count() > 1)

为什么你需要一个复杂的Linq代码行,而不是在扩展方法中包含一个有效的测试实现并调用它?

 var a = new int[9] {1,2,3,4,5,6,7,8,9}; var itIsOk = a.HasNoNonZeroRepeats(); 

NoNonZeroRepeats的一个实现可能是使用short的9个最低位来指示数组中存在值,从而在没有动态内存使用的情况下进行O(长度(a))测试。 Linq很可爱,但除非你出于审美原因只使用它(你没有特别说你只用Linq作为练习来编写数独求解器),这似乎只是增加了复杂性。

这是一个老问题,但我最近指出使用Oracle的自定义SQL进行树状结构的1行解决方案。 我认为把它转换成Linq会很好。

您可以在我的博客上阅读更多关于如何在Linq的1行中解决数独的内容

这是代码:

 public static string SolveStrings(string Board) { string[] leafNodesOfMoves = new string[] { Board }; while ((leafNodesOfMoves.Length > 0) && (leafNodesOfMoves[0].IndexOf(' ') != -1)) { leafNodesOfMoves = ( from partialSolution in leafNodesOfMoves let index = partialSolution.IndexOf(' ') let column = index % 9 let groupOf3 = index - (index % 27) + column - (index % 3) from searchLetter in "123456789" let InvalidPositions = from spaceToCheck in Enumerable.Range(0, 9) let IsInRow = partialSolution[index - column + spaceToCheck] == searchLetter let IsInColumn = partialSolution[column + (spaceToCheck * 9)] == searchLetter let IsInGroupBoxOf3x3 = partialSolution[groupOf3 + (spaceToCheck % 3) + (int)Math.Floor(spaceToCheck / 3f) * 9] == searchLetter where IsInRow || IsInColumn || IsInGroupBoxOf3x3 select spaceToCheck where InvalidPositions.Count() == 0 select partialSolution.Substring(0, index) + searchLetter + partialSolution.Substring(index + 1) ).ToArray(); } return (leafNodesOfMoves.Length == 0) ? "No solution" : leafNodesOfMoves[0]; } 

怎么样:

 var itIsOk = a.Where(x => x > 0).Distinct().Count() == a.Where(x => x > 0).Count(); 

推理:首先创建一个没有0的枚举。 在剩余的数字中,如果其不同的列表与实际列表的长度相同,则没有重复。

或:如果唯一编号列表小于实际列表,则必须具有重复编号。

这是单线版。 a.Where(x => x> 0)列表可以被排除在外。

 var nonZeroList = a.Where(x => x > 0).ToList(); var itIsOk = nonZeroList.Distinct().Count() == nonZeroList.Count(); 

我通常对涉及捕获变量的解决方案不以为然,但我有一种写这个的冲动:

 bool hasRepeating = false; int previous = 0; int firstDuplicateValue = a .Where(i => i != 0) .OrderBy(i => i) .FirstOrDefault(i => { hasRepeating = (i == previous); previous = i; return hasRepeating; }); if (hasRepeating) { Console.WriteLine(firstDuplicateValue); } 

为简洁起见,如果不是性能,var itIsOk = a.Sum()== a.Distinct()。Sum();

以下内容简单快捷。

 if a.Select(i => Math.Pow(2, i - 1)).ToArray().Sum()==511 ...