繁体   English   中英

C#中的数独算法

[英]Sudoku algorithm in C#

我需要一个衬垫(或靠近它)来验证给定的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;

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

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

这比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;
}

幸运的是,我不久前自己建造了一个数独求解器:)整个事情大约是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<T> enumerable, Predictate<T> pred)
{
    bool flag = false;
    foreach (T item in enumerable)
    {
       if (pred(item))
       {
          if (flag)
             return true;
          flag = true;
       }
    }
    return false;
}

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

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

这是一个老问题,但我最近指出使用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];
}

为什么你需要一个复杂的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作为练习来编写数独求解器),这似乎只是增加了复杂性。

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

怎么样:

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);
}

以下内容简单快捷。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM