【问题标题】:MasterMind scoring algorithm in C# using LINQ使用 LINQ 在 C# 中的 MasterMind 评分算法
【发布时间】:2010-12-16 23:57:19
【问题描述】:

我正在寻找一种优雅的方法来计算 C# 中 MasterMind 游戏中的猜测分数,最好使用 LINQ。

在 MasterMind 中,密码生成器使用数字 1 到 6 生成 4 位密码。一个数字可以多次使用。例如,密码是:

int[] secret = { 1, 2, 3, 1 };

密码破解者试图通过猜测来破解密码。在这个例子中,猜测是:

int[] guess  = { 1, 1, 2, 2 };

(代码和猜测现在都存储在数组中,但其他集合类型也可以)。

然后,代码制作者通过宣布“黑人”和“白人”的数量来“评分”这个猜测。猜中的每个数字在数值和位置上都是正确的,将获得一个黑色。放置在错误位置的每个正确数字都会获得白色。在此示例中,得分为 1 个黑色(位置 1 中的“1”)和 2 个白色(位置 2 和 3 中的“1”和“2”)。

回到问题:我正在寻找一种优雅的方法来计算 C# 中的猜测分数,最好使用 LINQ。到目前为止,我已经提出了一个计算黑人数量的语句:

int blacks = new int[] { 0, 1, 2, 3 }.Count(i => (guess[i] == secret[i]));

我打算按照白人的数量是匹配的总数 (3) 减去黑人的数量。所以我尝试了:

int whites = guess.Intersect(secret).Count() - blacks;

但是,唉,IEnumerable.Intersect() 生成 { 1, 2 } 而不是 { 1, 1, 2 },因为它只查看不同的数字。所以它计算 whites = 1 而不是 2。

我想不出另一种计算“白人”的方法,除了使用“C”风格的嵌套循环。你能?最好使用 LINQ - 我喜欢使用 LINQ 在代码中表达算法的方式。执行速度不是问题。

【问题讨论】:

  • “C 风格的嵌套循环”到底有什么问题,这看起来像是一个家庭作业。
  • 如果猜测是“1111”,您的秘密值的得分是多少?
  • @Ramhound - 我的 C 风格解决方案需要 15 行。我正在掌握 LINQ,并且确信 LINQ 可以用更少的行来做到这一点,同时更具可读性。顺便说一句,当我在学校时,C 是一门现代语言 :-)
  • @Vilx - 2 个黑人 0 个白人。有关 MasterMind 游戏的详细说明,请参阅 wikipedia 上的 Mastermind_(board_game)。

标签: c# linq


【解决方案1】:
var black = guess
        .Zip(secret, (g, s) => g == s)
        .Count(z => z);

var white = guess
        .Intersect(secret)
        .Sum(c =>
            System.Math.Min(
                secret.Count(x => x == c),
                guess.Count(x => x == c))) - black;

给定:

int[] secret = { 1, 2, 3, 1 };
int[] guess  = { 1, 1, 2, 2 };

然后:

black == 1 && white == 2

【讨论】:

  • 我给这个答案4个黑人。计算白色的方式正是我的想法,但无法用代码表达。另外,它看起来像是关于集合的代码,而不是数据库查询。谢谢,谜! (顺便说一句,但我的“C 风格”代码速度是原来的两倍 :-)
  • 一个非常优雅的解决方案。这个特定游戏的算法是否有资源?我正在寻找类似的解决方案,但在 Objective-C 中
【解决方案2】:

这是一种方法(假设我已经正确理解了问题):

  1. 找到黑色分数 - 这很容易;只需将序列压缩并计算匹配的相应元素的数量即可。

  2. 找出两个序列之间“共同元素”的数量 - 这必须是白色和黑色分数的总和。

  3. 找出白色分数 - 简单地说就是 2. 和 1 之间的差异。


// There must be a nicer way of doing this bit
int blackPlusWhite = secret.GroupBy(sNum => sNum)
                           .Join(guess.GroupBy(gNum => gNum),
                                 g => g.Key,
                                 g => g.Key,
                                (g1, g2) => Math.Min(g1.Count(), g2.Count()))
                           .Sum();   

int black = guess.Zip(secret, (gNum, sNum) => gNum == sNum)
                 .Count(correct => correct); 

int white = blackPlusWhite - black;

编辑:混合黑白。

编辑:(OP 不在 .NET 4 上)在 .NET 3.5 中,您可以使用以下方法计算黑色:

int black = Enumerable.Range(0, secret.Count)
                      .Count(i => secret[i] == guess[i]); 

【讨论】:

  • 不错的@Ani,我希望我的 linq 技能是这个 l337
  • @Fred K:是的。将放入 .NET 3.5 等效项。
  • @FredK - 从 social.msdn.microsoft.com/Forums/en-US/rx/threads 下载反应式框架,您将在 3.5 SP1 中获得各种新的 IEnumerable<> 扩展,包括 Zip。您甚至可以获得并行任务库的反向移植。
【解决方案3】:

阿尼的回答很好。这是进行分组和加入的更好(更清晰)的方法。

ILookup<int, int> guessLookup = guess.ToLookup(i => i);

int blackPlusWhite
(
  from secretNumber in secret.GroupBy(i => i)
  let secretCount = secretNumber.Count()
  let guessCount = guessLookup[secretNumber.Key].Count()
  select Math.Min(secretCount, guessCount)
).Sum()

int black = Enumerable.Range(0, secret.Count).Count(i => guess[i] == secret[i]);

int white = blackPlusWhite - black;

【讨论】:

    猜你喜欢
    • 2020-06-30
    • 1970-01-01
    • 2021-12-23
    • 2013-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-08
    相关资源
    最近更新 更多