【问题标题】:I'm unable to compare two lists in VB.Net我无法比较 VB.Net 中的两个列表
【发布时间】:2020-11-15 17:43:18
【问题描述】:

我有一个项目来制作骰子游戏 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

【问题讨论】:

标签: arrays vb.net dice


【解决方案1】:

所以你有一个名为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

【讨论】:

  • 谢谢,伙计!我在代码中实现后让它工作,我仍然需要了解它是如何工作的,所以我不会因为抄袭而被标记,但我应该没问题
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-19
相关资源
最近更新 更多