5

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

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

而且,是的,一个已经读过这个

4

9 回答 9

17

这比 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;
}
于 2009-04-07T01:02:03.140 回答
10

幸运的是,不久前我自己构建了一个数独求解器 :) 整个过程大约有 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 的工作方式,但它可能无关紧要。

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

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 个项目,因此它可能不会太重要,但如果你经常调用它,它可能会加起来。

于 2009-04-06T21:05:39.900 回答
3

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

于 2009-04-06T21:10:51.993 回答
2

为什么你想要一行错综复杂的 Linq 代码,而不是在扩展方法中封装测试的有效实现并调用它?

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

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

于 2009-04-06T21:58:53.237 回答
2

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

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

这是代码:

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];
}
于 2009-11-28T22:37:41.987 回答
2

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

于 2010-11-07T01:38:44.627 回答
1

怎么样:

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();
于 2009-04-06T21:47:11.907 回答
1

我通常对涉及捕获变量的解决方案皱眉头,但我有一种冲动要写这个:

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);
}
于 2009-04-07T00:56:05.710 回答
0

以下简单快速。

if a.Select(i => Math.Pow(2, i - 1)).ToArray().Sum()==511 ...
于 2016-05-24T19:11:30.660 回答