一副牌的所有非重复组合

时间:2017-02-11 06:37:36

标签: vb.net loops for-loop nested-loops

我正在尝试写一个扑克赔率计算器。它背后的想法是它暴力迫使它通过可能播放的所有可能的卡片组合。

我当前的逻辑(为了便于阅读而被删除)如下;

    For i = 0 To unknownCards - 1
            For j = 1 To 4
                'pick suit

                For k = 1 To 13
                    'pick number

                      'do other work here

                Next
            Next
        Next

然而这是错误的。它只会按顺序循环显示卡片。为了我的目的,卡的顺序并不重要(例如,我不想分别处理2,3,4和4,3,2),但重要的是我看到每个可能的独特组合。我只是无法绕过如何做到这一点?任何帮助或建议都会很棒。

P.S。我在VB.net中这样做

2 个答案:

答案 0 :(得分:2)

手牌数量为2,598,960手。此代码通过暴力生成所有可能的手。只需生成52张索引的组合比在你的循环中担心套装和排名更快/更容易/更好。正是@ElizabethSQGoodman所说的,其中有5个嵌套循环,每个循环都比我前面的循环高。

出于性能原因,我选择了一个字节来保存每张卡,并选择一个结构来握住手。然后,稍后,你可以根据规则找出每张卡的牌数:前13张牌是俱乐部,接下来的13张牌是钻石等(参见getHumanReadableHand())。在那里你也可以定义Ace高或低(但不是两个,抱歉!)。等级(A,2,3,...,J,Q,K)由索引模13确定。通过将13的整数除法确定为索引。

Module Module1

    Sub Main()
        Dim hands As New List(Of Hand)()
        For c0 As SByte = 0 To 51
            For c1 As SByte = c0 + 1 To 51
                For c2 As SByte = c1 + 1 To 51
                    For c3 As SByte = c2 + 1 To 51
                        For c4 As SByte = c3 + 1 To 51
                            Dim hand = New Hand
                            hand.Card0 = c0
                            hand.Card1 = c1
                            hand.Card2 = c2
                            hand.Card3 = c3
                            hand.Card4 = c4
                            hands.Add(hand)
                        Next c4
                    Next c3
                Next c2
            Next c1
        Next c0
        Console.WriteLine("There are {0} possible hands.", hands.Count)
        Dim rnd As New Random()
        Dim r = rnd.Next(hands.Count - 1)
        Console.WriteLine("Random hand: {0}", getHumanReadableHand(hands(r)))
        Console.WriteLine("Value: {0}", getHandValue(hands(r)))
        Console.ReadLine()
    End Sub

    Function getHumanReadableHand(hand As Hand) As String
        Static suits = {"C", "D", "H", "S"}
        Static ranks = {"A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"}
        Return String.Join(", ", hand.Cards.Select(Function(card) ranks(rank(card)) & suits(suit(card))))
    End Function

    Private Function rank(card As SByte) As SByte
        Return card Mod 13
    End Function

    Private Function suit(card As SByte) As SByte
        Return CSByte(card \ 13)
    End Function

    Function getHandValue(hand As Hand) As String
        Dim cards = hand.Cards
        If cards.Select(Function(card) rank(card)).Max() - cards.Select(Function(card) rank(card)).Min() = 4 AndAlso
            cards.Select(Function(card) rank(card)).Distinct().Count = 5 AndAlso
            cards.Select(Function(card) suit(card)).Distinct().Count = 1 Then
            Return "Straight Flush"
        ElseIf cards.OrderBy(Function(card) rank(card)).Take(4).Select(Function(card) rank(card)).Distinct().Count = 1 OrElse
            cards.OrderBy(Function(card) rank(card)).Skip(1).Take(4).Select(Function(card) rank(card)).Distinct().Count = 1 Then
            Return "Four of a Kind"
        ElseIf cards.Select(Function(card) rank(card)).Distinct().Count = 2 Then
            Return "Full House"
        ElseIf cards.Select(Function(card) suit(card)).Distinct().Count = 1 Then
            Return "Flush"
        ElseIf cards.Select(Function(card) rank(card)).Max() - cards.Select(Function(card) rank(card)).Min() = 4 AndAlso
            cards.Select(Function(card) rank(card)).Distinct().Count = 5 Then
            Return "Straight"
        ElseIf cards.OrderBy(Function(card) rank(card)).Take(3).Select(Function(card) rank(card)).Distinct().Count = 1 OrElse
            cards.OrderBy(Function(card) rank(card)).Skip(1).Take(3).Select(Function(card) rank(card)).Distinct().Count = 1 OrElse
            cards.OrderBy(Function(card) rank(card)).Skip(2).Take(3).Select(Function(card) rank(card)).Distinct().Count = 1 Then
            Return "Three of a Kind"
        ElseIf cards.Select(Function(card) rank(card)).Distinct().Count = 3 Then
            Return "Two Pairs"
        ElseIf cards.Select(Function(card) rank(card)).Distinct().Count = 4 Then
            Return "One Pair"
        Else
            Return "Garbage"
        End If
    End Function

    Structure Hand

        Public Property Card0 As SByte
        Public Property Card1 As SByte
        Public Property Card2 As SByte
        Public Property Card3 As SByte
        Public Property Card4 As SByte

        Public ReadOnly Property Cards As IEnumerable(Of SByte)
            Get
                Return New List(Of SByte)({Card0, Card1, Card2, Card3, Card4})
            End Get
        End Property

    End Structure

End Module

示例输出:

  

有2598960个可能的牌。   随机手:2C,5C,2D,5S,KS   
价值:两对

此代码大约需要60毫秒才能在我的机器上生成所有可能的指针。

答案 1 :(得分:0)

在评论澄清之后:蛮力获取所有牌局的名单,我知道这样做的最简单的方法是在牌上下订单(例如按号码排名然后适合:标准是俱乐部低2,黑桃王牌然后选择按升序排列的牌。这样,即使订单对您来说并不重要,但是排序可以确保您有一种明确的方式来决定独特的牌。

一个,简单易懂,但不是最佳的方法是嵌套5个循环:第一张卡的整个牌组,然后通过循环所有低于那张牌的牌来选择第二张牌,依此类推。 (你实际上可以选择从第一张牌开始的第一张牌,即三张牌,依此类推,但是当你完成时,确保每手牌有5张牌可能更容易。 )

我选择的语言是Python;如果你想要完整的清单,我会列出52张卡片,或者更好,但只需对卡片进行编号并使用范围;然后在列表上使用itertools.product 5次,只允许使用card_1< card_2< card_3< card_4< card_5。请参阅this question以了解相关信息。对于迭代器,我会查看他们记录的代码并修改它以仅允许升序。也许更有经验的编码器可以建议一个最佳的迭代器。