如何循环遍历例如 48 选择 5 的所有组合
我想构建我自己的扑克牌评估器,但在特定部分遇到问题。
如果两名玩家拿到两张牌,则牌堆中还剩下 48 张牌。在德州扑克中,随后会再发 5 张可能的公共牌(称为牌面)。我想枚举/循环所有 48 个选择 5 种可能的棋盘组合,并计算玩家 A 获胜的次数和玩家 B 获胜的次数以及他们平局的时间。
我不知道如何系统地循环遍历每 5 张卡片组合。有人有什么想法吗?这些卡片表示为 Card 类的数组,但我也可以将它们表示为位集(如果这样可以加快速度)。
我正在用 Java 做这个。
非常感谢
Possible Duplicate:
How to iteratively generate k elements subsets from a set of size n in java?
I want to build my own poker hand evaluator but am having trouble with a particular part.
If two players get dealt a two card hand, then there will be 48 cards left in the pack. In Texas Hold'em a further 5 possible community cards are then dealt (this is called the board). I want to enumerate / loop through all the 48 choose 5 possible combinations of boards and count the times Player A wins and the times Player B wins and when they tie.
I'm not sure how I can systematically loop through every 5 card combination. Does anyone have any ideas? The cards are represented as an array of class Card, but I could also represent them as a bitset if this makes it faster.
I'm doing this in Java.
Many thanks
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
(免责声明:我编写了一个非常快速的扑克牌评估器)
您不想每次在翻牌前两个玩家之间进行对决时都评估 C(48,5) (1 712 304) 手牌:大多数程序只是在翻牌前两个玩家之间的所有可能对决之间使用预先计算的查找表。
例如,假设您有“Ac Ad”与“7c 6c”,您只需查看包含以下内容的查找表:
1 333 573, 371 831, 6900
(其中 1 333 573 是“Ac Ad”获胜的次数,371 831 是“7c 6c”获胜的次数,6 900 是平局的次数(它们的总和为 1 712 304),为了获得一些空间,您可以丢弃 6 900,因为知道平局数始终为 C(48,5) - (赢 1 + 赢 2) 。(更多关于本答案末尾的查找表)
但是要回答你的问题:
如果您确实想循环遍历每个组合,您必须知道扑克牌评估器通常是那种需要非常非常快的程序。这些程序通常可以每秒评估数亿只手(您没看错:数亿只)。
当您需要如此高性能的“数字运算”时,您可以忘记“设计模式”和“面向对象”。你想要的是原始速度。
例如,下面的代码将通过最里面的循环 C(48,5) 次,并且速度相当快:
对于翻牌前的两个玩家来说,这可能是一个非常糟糕的主意:通过使用查找表,您会更快。
但是对于翻牌前的三名玩家(使用翻前牌桌是不切实际的,因为有太多的对局),您可能想要像这样循环,在 C(46,5) 手牌上,使用五个嵌套循环(当然您需要使用 i,j,k,l,m 从剩下的 46 张牌中取出正确的 5 张牌)。然后,一旦你拿到了 5 张牌,你就可以使用快速手牌评估器,为你提供 7 张牌中最好的一张(牌面的 5 张牌 + 每个玩家的两张牌)。
关于查找表:
大多数人使用近似的 169 vs 169 查找表(“Ac Kd”、“As Kh”、“Ad Ks”等全部变成“AK offsuit”,而 C( 52,2) 可能的起手牌分为 169 种起手牌)。维基百科文章解释了如何获得 169 个非等效起手牌:
http://en.wikipedia。 org/wiki/Texas_hold_%27em_starting_hands
当您考虑一只手时,它们是不等价的,但一旦正如您第一手牌与第二手牌一样,“169 vs 169”是一个近似值(这是一个相当好的说法)。
当然,你可以变得更喜欢。只有 C(52,2)(给出 1326)种真正不同的德州扑克起手牌,这意味着在现代计算机上构建完美的查找表(根本没有近似值)非常实用(C(1326,2) ain没那么大)如果你真的需要完美的数字。如果您可以接受近似值,请使用 169 与 169 表(它需要 C(169,2) 或 14 196 个条目)。
(disclaimer: I've written a very fast poker hand evaluator)
You do not want to evaluate C(48,5) (1 712 304) hands every time you have a matchup between two players preflop: most programs simply use a precomputed lookup table between all the possible matchups between two players preflop.
For example say you have "Ac Ad" vs "7c 6c", you simply go look in a lookup table that contains:
1 333 573, 371 831, 6900
(where 1 333 573 is the number of times "Ac Ad" wins, 371 831 is the number of times "7c 6c" wins and 6 900 is the number of ties (they sum to 1 712 304). To gain some room you can discard the 6 900, knowing that the number of ties shall always be C(48,5) - (wins 1 + wins 2).(more on the lookup table at the end of this answer)
But to answer your question:
If you do really want to loop trough every combination, you have to know that poker hand evaluators are typically the kind of program that need to be very, very fast. These programs can typically evaluate hundreds of millions of hands per second (you read correctly: hundreds of millions).
When you need such high-performance "number crunching" you can forget about "design patterns" and "OO". What you want is raw speed.
For example the following will pass through the innermost loop C(48,5) times and it is quite fast:
Once again for two players preflop it's probably a very bad idea: you're gonna be much faster by using a lookup table.
But for three players preflop (where it's impractical to use a preflop tables, there are too many matchups), you may want to loop like that, over C(46,5) hands, using the five nested loops (of course you need to use i,j,k,l,m to get the correct 5 cards out of the 46 cards that are left). Then, once you've got the 5 cards, you use a fast hand evaluator that gives you the best out of 7 (the 5 of the board + the two of each player).
Regarding the lookup table:
Most people use an approximated 169 vs 169 lookup table ("Ac Kd", "As Kh", "Ad Ks", etc. all become "AK offsuit" and the C(52,2) possible starting hands become grouped in 169 type of starting hands). The Wikipedia article explains how to get the 169 non-equivalent starting hands:
http://en.wikipedia.org/wiki/Texas_hold_%27em_starting_hands
They're non equivalent when you take one hand into account, but as soon as you do hand 1 vs hand 2 the "169 vs 169" is an approximation (a quite good one that said).
Of course you can get fancier. There are only C(52,2) (which gives 1326) real different Hold'em starting hands, which means it's very practical to build a perfect lookup table (no approximation at all) on modern computers (C(1326,2) ain't that big) if you really need perfect numbers. If you can live with approximation, go for the 169 vs 169 table (it would need C(169,2) or 14 196 entries).
A
) 初始化为前 5 个索引。 (0,1,2,3,4)A[k]++
A[k+1] = A[k] + 1
,...)。如果某个索引变得太大,请尝试在步骤 2 中使用较早的索引。A
中当前索引处的元素。作为迭代器实现:
相当于 C# 中的迭代器方法:
A
) to the first 5 indices. (0,1,2,3,4)A[k]++
A[k+1] = A[k] + 1
, ...). If some index would become too large, try with an earlier index in step 2.A
.Implemented as an iterator:
Equivalent in C#, using an Iterator method: