如何遍历所有组合,例如48选择5

可能重复:
如何从java中的一组大小n迭代生成k个元素子集?

我想建立自己的扑克手评估员,但我遇到了特定部分的问题。

如果两名牌手获得两张牌,那么将会有48张牌。 在Texas Hold’em中,再发出5张可能的社区牌(这称为牌局)。 我想枚举/循环所有48个选择5个可能的棋盘组合,并计算玩家A获胜的时间和玩家B获胜的时间以及他们并列的时间。

我不确定如何系统地遍历每5张卡组合。 有没有人有任何想法? 这些卡表示为类卡的数组,但如果它使它更快,我也可以将它们表示为bitset。

我是用Java做的。

非常感谢

(免责声明:我写了一个非常快速的扑克手评估员)

我想枚举/循环所有48个选择5个可能的棋盘组合,并计算玩家A获胜的时间和玩家B获胜的时间以及他们并列的时间。

每次在翻牌前两个玩家之间进行比赛时,你不想评估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)

(更多关于本答案末尾的查找表)

但要回答你的问题:

我不确定如何系统地遍历每5张卡组合。

如果你确实想要通过每个组合循环,你必须知道扑克手评估者通常是那种需要非常非常快的程序。 这些程序通常可以每秒评估数亿手(您正确阅读:数亿)。

当你需要这种高性能的“数字运算”时,你可以忘记“设计模式”和“OO”。 你想要的是原始速度。

例如,以下将通过最里面的循环C(48,5)次并且它非常快:

  for ( int i = 0; i < n; i++ ) { for ( int j = i + 1; j < n; j++ ) { for ( int k = j + 1; k < n; k++ ) { for (int l = k + 1; l < n; l++) { for (int m = l + 1; m < n; m++) { ... } } } } } 

翻牌前两位玩家再一次这可能是一个非常糟糕的主意:使用查找表你会快得多。

但是对于翻牌前的三名球员(使用翻牌前桌面是不切实际的,比赛太多了),你可能想要在C(46,5)手牌上循环,使用五个嵌套循环(当然你需要使用i,j,k,l,m从剩下的46张牌中获得正确的5张牌。 然后,一旦你获得了5张牌,你就可以使用一个快速手牌评估器,它可以给你7个中最好的(5个棋盘+每个玩家的2个)。

关于查找表:

大多数人使用近似的169对169查询表(“Ac Kd”,“As Kh”,“Ad Ks”等等都变为“AK offsuit”并且C(52,2)可能的起手分组为169型起手)。 维基百科的文章解释了如何获得169个非等效的起手牌:

http://en.wikipedia.org/wiki/Texas_hold_%27em_starting_hands

当你考虑到一只手时它们是不相等的,但是一旦你用手1对手2 ,“169 vs 169”就是近似值(这是一个非常好的说法)。

当然你可以变得更加漂亮。 只有C(52,2)(它给出了1326)真正不同的德州扑克起手牌,这意味着在现代计算机上构建一个完美的查找表(完全没有近似值)是非常实用的(C(1326,2)ain)如果你真的需要完美的数字,那就太大了。 如果你可以使用近似值,那么请选择169 vs 169表(它需要C(169,2)或14 196个条目)。

  1. 将数组( A )初始化为前5个索引。 (0,1,2,3,4)
  2. 将A中最后一个可能的索引移动到下一个位置。 A[k]++
  3. 将以下索引移动到以下连续位置。 ( A[k+1] = A[k] + 1 ,……)。 如果某个索引变得太大,请尝试使用步骤2中的早期索引。
  4. A中的当前指数处产生元素。
  5. 如果可能,请从步骤2开始重复。

实现为迭代器:

 public class CombinationIterator implements Iterable>, Iterator> { private List items; private int choose; private boolean started; private boolean finished; private int[] current; public CombinationIterator(List items, int choose) { if (items == null || items.size() == 0) { throw new IllegalArgumentException("items"); } if (choose <= 0 || choose > items.size()) { throw new IllegalArgumentException("choose"); } this.items = items; this.choose = choose; this.finished = false; } public Iterator> iterator() { return this; } public boolean hasNext() { return !finished; } public List next() { if (!hasNext()) { throw new NoSuchElementException(); } if (current == null) { current = new int[choose]; for (int i = 0; i < choose; i++) { current[i] = i; } } List result = new ArrayList(choose); for (int i = 0; i < choose; i++) { result.add(items.get(current[i])); } int n = items.size(); finished = true; for (int i = choose - 1; i >= 0; i--) { if (current[i] < n - choose + i) { current[i]++; for (int j = i + 1; j < choose; j++) { current[j] = current[i] - i + j; } finished = false; break; } } return result; } public void remove() { throw new UnsupportedOperationException(); } } 

使用Iterator方法在C#中等效:

 public IEnumerable> Combinations(IEnumerable items, int choose) { if (items == null) throw new ArgumentNullException("items"); var itemsList = items.ToList(); int n = itemsList.Count; if (n < 1) throw new ArgumentException("Must contain at least one item.", "items"); if (choose <= 0 || choose >= n) throw new ArgumentOutOfRangeException("choose"); var indices = Enumerable.Range(0, choose).ToArray(); bool moreWork = true; while (moreWork) { yield return indices.Select(i => itemsList[i]).ToList(); moreWork = false; for (int i = choose - 1; i >= 0; i--) { if (indices[i] < n - choose + i) { indices[i]++; for (int j = i + 1; j < choose; j++) { indices[j] = indices[i] - i + j; } moreWork = true; break; } } } }