首页 > 解决方案 > 我无法比较 VB.Net 中的两个列表

问题描述

我有一个项目来制作骰子游戏 Yahtzee。在五个骰子中,我必须按升序对骰子进行排序,然后检查它们是否按特定顺序排列。因此,Yahtzee 中的小直分数。例如,如果我得到 [1,2,3,4,6] 那么我有一个小顺子。但如果我有 [1,2,4,6,5],我不会。

这是我的代码(骰子列表是骰子的随机列表,这个评分函数确定它是否符合小直标准)

 Dim Sorted_List() As Integer
    Sorted_List(0) = Dice_List(0)
    Sorted_List(1) = Dice_List(1)
    Sorted_List(2) = Dice_List(2)
    Sorted_List(3) = Dice_List(3)
    Sorted_List(4) = Dice_List(4)
    Array.Sort(Sorted_List)
    Dim Fours_List(3) As Integer
    Fours_List(0) = Sorted_List(0)
    Fours_List(1) = Sorted_List(1)
    Fours_List(2) = Sorted_List(2)
    Fours_List(3) = Sorted_List(3)

    Dim smlStr1() As Integer = {1, 2, 3, 4}

    If (Fours_List Is smlStr1) Then
        lblSmallStraight.Text = "55"

    End If

标签: arraysvb.netdice

解决方案


所以你有一个数组DiceList,它保存了掷骰子的结果。我们可以很容易地使用 LINQ 对其进行排序:

Dim rolls = DiceList.OrderBy(Function(x) x)

但我们也应该删除重复项,因为它使问题更容易处理:

Dim rolls = DiceList.OrderBy(Function(x) x).Distinct().ToArray()

现在一种简单的查看方法可能只是将滚动的差异收集到一个字符串中:

Dim consec = ""
For i = 1 to Ubound(rolls)
  sum += (rolls(i) - rolls(i-1)).ToString()
Next i

然后问:

If consec.Contains("1111") Then 'its a large straight
Else If consec.Contains("111") Then 'there's a small straight

当然,您可能认为仅列出一些组合更简单:

Dim smallStraights = {"1234","2345","3456"}
Dim largeStraights = {"12345","23456"}

然后把你的卷变成一个字符串:

Dim rollsStr = string.Join("", rolls.Select(Function(x) x.ToString()))

并询问字符串是否包含任何直线:

If largeStraights.Any(Function(ls) rollsStr.Contains(ls)) Then '...
Else If smallStraights.Any(Function(ss) rollsStr.Contains(ss)) Then '...

请注意,最后一种语法有点奇怪。因为我们的直道在一个数组中,并且我们正在查询 rolls 字符串是否包含任何直道,所以我们不能从rollsStr.Contains(...).

相反,我们实际上需要询问“对于这个数组中的所有这些直线,是否有任何数组元素使得 rolls 字符串包含数组元素”?

使用循环它看起来像:

'smallStraights.Any(Function(ss) rollsStr.Contains(ss))
For Each ss In smallStraights
  If rollsStr.Contains(ss) Return True 'stop as soon as one is found
Next ss
Return False 'none found

那么这一切是如何运作的呢?

我们有一组卷:

{2, 1, 4, 3, 6}

作为人类,我们可以通过以下方式寻找连续的

  • 数数和来回跳跃(找到 1,向左移动到 2,向右移动三个位置到 3,向左移动到 4,向右移动到 6,算出我们连续有 4 个,但不是 5,调用它是一个直小)
  • 重新排列骰子,使它们按顺序显示,看看有多少骰子“比左边的多一个”

后一种方法是我开始的 - 我对数组进行排序并查看差异。因为重复会破坏我们的方法(如果你扔掉一个 2,一组 1,2,2,3,4 实际上是一个小顺子,但如果你保留它,你会有一个 1,0 的差异链,如果我们正在寻找 1,1,1 的差异链,1,1 和 0 会令人不安)我还将它们作为“排序骰子”步骤的一部分取出

在我们实施了排序骰子的方法,然后逐个检查它们,计算出与前一个骰子的差异之后,我们建立了一个描述差异的字符串。

构建一个字符串让我们的生活更轻松,因为有内置的方法可以询问一个字符串是否包含另一个字符串。如果我们的差异字符串是例如“0111”或“1112”,那么它确实包含“111”,这意味着存在小顺子(请记住,有5个骰子,但只有4个差异,因为算法是“this_dice减去previous_dice”即对于 5 个骰子 A、B、C、D、E,我们做 BA、CB、DC 和 ED - 5 个骰子有 4 个差异)

然后我们可能会意识到实际上不做差异的事情更容易,而只是订购骰子,删除重复的骰子并寻找意味着存在顺子的小骰子组合。这意味着我们实际上是在拿我们的{ 1,2,3,4,6} 滚动,将它们变成一串“12346”,然后查看是否可以找到“1234”、“2345”或“3456”——小的直道。如果我们在寻找大顺子之后这样做,并且只有在我们没有找到大顺子的情况下,那么对于“12345”的滚动集合,我们不会意外地将其声明为小顺子(因为“12345”包含“ 1234") 当它真的是一个大顺子时

为什么选择一个而不是另一个?好吧,寻找有限数量的小/大顺子(只有 5 个)是可行的,因为只有 5 个。如果 Yahtzee 有 100 个面骰子,顺子可能是 1-2-3-4、2-3- 4-5, 3-4-5-6, 4-5-6-7, 5-6-7-8 .. 一直到 97-98-99-100 那么使用差异方法是有意义的,因为差异方法不是列出 98 个小顺子的组合,而是总是将我们正在寻找的多样性减少到“111”;如果我们做差异,1-2-3-4 或 97-98-99-100 的小顺子都会变成 1-1-1-1

所以剩下的就是让您的代码将您的数字列表转换为单个字符串,然后使用包含。这比问“这个数字列表是否包含另一个数字列表”要容易得多,所以我们(ab)使用字符串作为数字的数据容器,因为这意味着它们不再是必须分开的东西交叉协调;它们是包含模式的单个字符串,随着时间的推移,我们在编程语言中开发了很多方法,在字符串中寻找模式

您当然可以有一组数字 {1,2,3,4,6} 并询问“这组数字是否包含另一组数字 {1,2,3,4}”,但看起来有点更像这样(在编程 101 术语中,不使用 LINQ 或 Sets 等)

Dim rolls = {1,2,3,4,6} 'note: these values must be unique
Dim straight = {1,2,3,4} 'note: these values must be unique

Dim numFound = 0
For Each r in rolls
  For Each s in straight
    If r == s Then 
      numFound += 1
    End If
  Next s
Next r
If numFound = straight.Length Then 'if we found all the numbers in the straight
  Console.Write("All the numbers of the straight exist in the rolls")
End If

我们仍然存在重复失败此方法的问题,因此我们需要对我们的卷进行重复数据删除。我们可以通过添加一点来做到这一点:

Dim numFound = 0
Dim prevR = -1
For Each r in rolls
  If r = prevR Then Continue 'skip this one, it's a duplicate of the previous roll
  prevR = r ' remember the current roll for next time
  For Each s in straight
    If r == s Then 
      numFound += 1
    End If
  Next s
Next r

我们还有一个问题是需要对卷进行排序,因为我们只检查前一卷。如果我们要使用未分类的卷,那么我们需要检查所有以前的卷以查看当前卷是否已经发生:

For i = LBound(rolls) to UBound(rolls)
  Dim r = rolls(i)

  'Check ALL the previous rolls
  Dim seenBefore = False
  For p = i - 1 To LBound(rolls) Step -1
    Dim prevR = rolls(p)
    If prevR = r Then seenBefore = True
  Next p
  If seenBefore Then Continue 'skip this one, it's a duplicate of a previous roll

  For Each s in straight
    If r == s Then 
      numFound += 1
    End If
  Next s
Next r

您可以看到问题是如何在我们每次考虑时开始增长的/尝试使用上一次迭代解决另一个问题/错误。总而言之,我们现在有一种机制来检查一组数字是否存在于另一组数字中,但与我们之前的步骤相比,它相当冗长:

Dim rolls = {1,3,2,5,4}
Dim rollsString = string.Join("", rolls.OrderBy(...).Distinct()) 'turn the rolls into a string like "12345"

If rollsString.Contains("1234") Then 'rolls contains the small straight 1234

请注意,这些广泛使用 LINQ,您可能没有学过 LINQ。您可能需要一种方法来使用您已经知道的实现算法“排序、唯一、查找直线”,但同样您也可以自己学习(获得关于 SO 的帮助是可能的,尽管很多人只想在尽可能短的时间内给你答案以试图赢得分数)并向老师证明你的算法和你的解决方案是正确的。

如果您不想使用 LINQ,可以查看讨论的其他一些内容并组合解决方案。可以通过以下方式对列表进行重复数据删除:

Dim uniqueList as New List(Of Integer) 'must be a list, not an array
For Each i in listWithDuplicates
  If Not uniqueList.Contains(i) Then uniqueList.Add(i)
Next i

推荐阅读