Fast ranking/unranking of combinations (64bit)

六眼飞鱼酱① 提交于 2019-12-23 03:37:13

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!