有这篇伟大的帖子 https://stackoverflow.com/a/3143594/6589735 概述排名/排名组合的算法。 此外,在C ++中有一些具体的实现,例如这里https://people.sc.fsu.edu/~jburkardt/cpp_src/combo/combo.cpp
我需要在C ++中实现非常快的实现,它在x64 Haswell CPU上对unsigned long long
编码的排名/解包组合进行排序/取消。
我的尝试非常需要改进。
unsigned long long rank(unsigned long long comb, int card)
{
unsigned long long rank = 0;
for (int i = 1; i <= card; i++)
{
unsigned long index;
_BitScanForward64(&index, comb);
rank += binCoef[index][i]; // the binCoef table is precomputed
comb &= (comb - 1);
}
return rank;
}
unsigned long long unrank(unsigned long long rank, int card)
{
unsigned long long comb = 0;
unsigned long long m = rank;
for (int i = card - 1; i >= 0; i--)
{
int p = i;
while (binCoef[p + 1][i + 1] <= m)
p++;
m -= binCoef[p][i + 1];
comb |= (1 << p);
}
return comb;
}
答案 0 :(得分:1)
我认为你已经很顺利了。您是否正在做一些与扑克相关的事情? (当我这样做时,我只是用Google搜索了无法解决的算法)。一些注意事项:
1)对于少量项目,二项式可以比内存访问更快地手动计算,但考虑到很长时间,这在你的情况下是不太可能的。
2)但是,根据编译器的智能,您可以通过计算precalc表的2D索引来保存内存访问:
binCoef[(index<<6) + i]; // instead of binCoef[index][i]
3)在计算过程中不确定binCoef
在L1缓存中的适用程度,但是您可以使用标识C(n,k)== C(n,nk)保存一半的查找空间一个额外的条件。也许值得测试?
4)在unrank中,我认为内部while循环是最慢的部分。有两个明显的优化:要么实现二进制搜索来定位p,要么......
5)如果你正在处理card
的小值,你甚至可以考虑全面的查找解决方案来解决:有C(52,5)= 260万扑克牌,这只是少数几个查找MB。
6)同样,您可能会看到是否可以使用map<int, long long> rank
来完全替换排名算法。二叉树是用于查找的O(log N),并且哈希映射可能具有更好的性能。只是预先确认:
map<long long, int> rank5;
for(int i=0; i<N; i++) rank5[unrank(i, 5)] = i; //ranks of 5 card hands
如果您需要较大的card
值,那么查找方法将无法正常工作,我认为您几乎完全坚持使用二进制搜索优化。祝你好运!