检查扑克牌是否为顺子

3
我已经成功地创建了一种算法来检查扑克牌的等级。它的正确率达到了100%,但速度很慢。我一直在分析代码,发现检查顺子函数是其中最慢的部分之一。
所以我的问题是,有没有更好的方法来计算手牌是否可以组成顺子?
以下是一些细节:
7张牌,2张由玩家持有,5张从桌面抽取。A可以是高牌或低牌。
每张牌都被赋予一个值: 2 = 2 3 = 3 .. 9 = 9 T = 10 J = 11 Q = 12 K = 13 A = 14
脚本有一个包含所有7张牌的数组:
$cards = array(12,5,6,7,4,11,3);

现在我需要将其排序到一个数组中,其中:
  • 丢弃重复项
  • 将卡片从低到高排序
  • 仅返回5个连续的卡片,即(3,4,5,6,7)
它需要快速进行;循环和迭代非常耗费时间。这是我目前使用的方法,当它尝试分析15000手时,它会对脚本产生影响。
对于上述问题,我使用了:
  • 丢弃重复项(使用array_unique)
  • 将卡片从低到高排序(使用sort())
  • 仅返回5个连续的卡片(使用for循环来检查卡片的值)
有人有任何改进的例子吗?甚至可以用另一种语言来看看它是如何完成的吗?

我认为你在排序部分不会更快。 不过,我会把array_unique拿出来。由于你有很多组合,而且只有7张牌,我认为去除重复项的工作量比遍历它们要大。但这需要进行基准测试。 - β.εηοιτ.βε
1
当你检查5张牌是否连续时,是否向前跳过5张牌并向后检查它们以减少循环次数? - Maxime
你正在对数组进行两次排序:1. array_unique() 内部对数组进行了排序,然后按原始未排序的顺序返回值。2. 然后你重新对数组进行排序。我建议使用 @Philipp 的答案,因为它只涉及一次排序操作,并完全不需要使用 array_unique() - Sammitch
hofan41的位图方法可能是最好的,但不要忘记添加轮子修复。 - Lee Daniel Crocker
4个回答

4

不要使用数组去重和排序,考虑使用位掩码,将卡牌值设置为1。位掩码类似于Set数据结构,在检测连续元素方面具有额外的优势。

for ($i = 0; $i < count($cards); $i++) {
    $card = $cards[$i];
    // For each card value, set the bit
    if ($card == 14) {
        // If card is an ace, also set bit 1 for wheel
        $cardBitmask |= 0x2;
    }
    $cardBitmask |= (1 << $card);
}

// To compare, you simply write a for loop checking for 5 consecutive bits
for($i = 10; $i > 0; $i--)
{
    if ($cardBitmask & (0x1F << $i) == (0x1F << $i)) {
        // Straight $i high was found!
    }
} 

$cards.count() 不是有效的 PHP 语法。 - Sammitch
或者当设置位掩码时,如果检测到一个ace,您也可以将位1与位14一起设置。 - hofan41
是的。那么你的for循环将是for ($i = 10; i > 0; $i--)。(有10个不同的顺子)。 - Lee Daniel Crocker
非常感谢!我查看了位掩码及其工作原理,然后对值应用了一些回声,只是为了看看它们是如何工作的,现在我明白了。我想重新编写我的算法,完全使用位掩码。 - Patchesoft
不过,有没有使用十六进制向位掩码添加值的原因呢?例如 $mask |= 0x2; 而不是 $mask |= 2; - Patchesoft
显示剩余5条评论

1
考虑这个链接中的Java实现。我在此处包含它:
public static boolean isStraight( Card[] h )
{
  int i, testRank;

  if ( h.length != 5 )
     return(false);

  sortByRank(h);      // Sort the poker hand by the rank of each card      

  /* ===========================
     Check if hand has an Ace
     =========================== */
  if ( h[4].rank() == 14 )
  {
     /* =================================
        Check straight using an Ace
        ================================= */
     boolean a = h[0].rank() == 2 && h[1].rank() == 3 &&
                 h[2].rank() == 4 && h[3].rank() == 5 ;
     boolean b = h[0].rank() == 10 && h[1].rank() == 11 &&        
                 h[2].rank() == 12 && h[3].rank() == 13 ;

     return ( a || b );
  }
  else
  {
     /* ===========================================
        General case: check for increasing values
        =========================================== */
     testRank = h[0].rank() + 1;

     for ( i = 1; i < 5; i++ )
     {
        if ( h[i].rank() != testRank )
           return(false);        // Straight failed...

        testRank++;   // Next card in hand
     }

     return(true);        // Straight found !
  }
}

快速在Google上搜索“检查扑克顺子(desired_lang)”将给您其他实现。


1
你可以将卡片排序并在数组中循环-始终保存上一张卡片并将其与当前卡片进行比较。
$cards = array(12,5,6,7,4,11,3);
sort($cards);

$last = 0;
$count = 0;
$wheel = false;
foreach ($cards as $card) {
    if ($card == $last) {
        continue;
    } else if ($card == ++$last) {
        $count++;
    } else {
        if ($last == 6) $wheel = true;
        $count = 1;
        $last = $card;
    }

    if ($count == 5 || ($card == 14 && $wheel)) {
        echo "straight $last";
        $straight = range($last - 4, $last);
        break;
    }
}

就像这里的其他大多数答案一样,您也缺少了轮子(A2345)。 - Lee Daniel Crocker

0

你可以这样做,不需要排序或其他操作(假设2是2,14是Ace):

$cards = [12,5,6,7,4,11,3];

function _inc(&$i) {
    if ($i == 14) 
        $i = 2;
    else
        $i++;
    return $i;
}

$straight = false;
for($i = 2; $i <= 14; $i++) {
    $ind = $i;
    if (!in_array($ind, $cards)) continue;
    $s = [$ind, _inc($ind), _inc($ind), _inc($ind), _inc($ind)];    
    $straight = count(array_intersect($s, $cards)) == count($s);
    if ($straight) break;
}

print $straight;

您还缺少轮子(A2345)。 - Lee Daniel Crocker
你可能没有注意到 _inc 函数。我刚试了一下 $cards = [5,14,2,7,4,11,3]; ,它输出 true - Timofey
啊,是的。它对于 [ 12, 13, 14, 2, 3, 7, 7 ] 输出 true 吗?如果是这样,那么它仍然有问题,但是问题的性质不同了。 - Lee Daniel Crocker
是的,我没有提供完整的解决方案,但提供了算法。修复那个问题真的很简单(只有当$i == 14时才使用_inc)。 - Timofey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接