问题
There is this great post https://stackoverflow.com/a/3143594/6589735 outlining an algorithm of ranking/unranking combinations. Also, there are concrete implementations in C++, e.g. here https://people.sc.fsu.edu/~jburkardt/cpp_src/combo/combo.cpp
I am in need of a very fast implementation in C++, that does rank/unrank combinations encoded as unsigned long long
on a x64 Haswell CPU.
My attempt is in great need of improvement.
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;
}
回答1:
I think you are already quite well on the way. Are you doing some poker related perhaps? (I just googled unranking algorithms when doing just that). Some considerations:
1) With small amount of items, binomials could be quicker to calculate manually than the memory accesses, but considering long long, this is highly unlikely in your case.
2) However, depending on compiler wits, you might save a memory access with calculating 2D index of the precalc table:
binCoef[(index<<6) + i]; // instead of binCoef[index][i]
3) Not sure how well the binCoef
fits in L1 cache during calculations, but you might save half of the lookup space with identity C(n, k) == C(n, n-k) with a cost of one additional conditional. Maybe worth testing?
4) In unrank, I think the internal while-loop is the slowest part. There are two obvious optimizations: Either implement a binary search to locate p, OR...
5) IF you are dealing with small values of card
, you might even consider full-blown lookup solution to unrank: there are C(52, 5) = 2.6 million poker hands, which is only a few MB of lookup.
6) Similarly, you might see if map<int, long long> rank
could be used to replace the rank algorithm altogether. A binary tree is O(log N) for lookup, and hashmaps might have even better performance. Just precalc it:
map<long long, int> rank5;
for(int i=0; i<N; i++) rank5[unrank(i, 5)] = i; //ranks of 5 card hands
If you will need large values of card
, then the lookup methods won't work, and I think you're pretty much stuck with the binary search optimization. Good luck!
来源:https://stackoverflow.com/questions/38590286/fast-ranking-unranking-of-combinations-64bit