3

This is such an obscure problem that I suspect I'll have to do it at a different level in my code...but hopefully the hive mind that is Stack Overflow can help...

I have a long, which if expressed as a binary string will have exactly five bits set. For example,

long l = 341; // as a bit string, "101010101"

I'm seeking an array containing all ten possible longs which exactly three of those bits set. To continue the example,

long[] results = {
  101010000,
  101000100,
  101000001,
  100010100,
  100010001,
  100000101,
    1010100,
    1010001,
    1000101,
      10101
}

Here's what the appropriate method signature might look like:

public long[] toThreeBitCombinations(long l) {
    // what goes here?
}

(The problem domain is poker; enumerating all the possible board card combinations in a Omaha Poker hand. Yep, there are other ways to approach this, but I am testing out this approach, as dealing with bits is so much quicker than most other alternatives.)

Steve McLeod
  • 51,737
  • 47
  • 128
  • 184
  • 1
    Note that an integer literal that begins with a `0` is considered octal. – Sotirios Delimanolis Sep 28 '13 at 17:07
  • @SotiriosDelimanolis good point...thanks – Steve McLeod Sep 28 '13 at 17:08
  • Also, `Long` has a `bitCount` method that `Returns the number of one-bits in the two's complement binary representation of the specified long value.` – Sotirios Delimanolis Sep 28 '13 at 17:10
  • Surely there are loads of existing questions on SO about combination generation? Are they not applicable here? – Oliver Charlesworth Sep 28 '13 at 17:11
  • It can be done using a [`compress_right`](http://programming.sirrida.de/bit_perm.html#c_e), Gosper's Hack, and a lot of `expand_right`'s. Unfortunately, `expand_right` is very expensive without hardware support (which is currently only present in Haswell). I'll think about an other way – harold Sep 28 '13 at 17:35
  • Every poker-hand-as-bitmap trick you can imagine has already been done in http://pokersource.sourceforge.net/ , so you might look there for clues (it's in C). My own https://github.com/lcrocker/ojcardlib library (also in C) is callable from Java, but uses cards-as-ints representation rather than bitmaps. – Lee Daniel Crocker Sep 30 '13 at 09:03

3 Answers3

2

Well, I got it. I think. I constructed a version of Gosper's Hack for fragmented fields that I'm not entirely sure about, but it worked for this case.

static long next(long v, long m)
{
    long t = v | (v - 1 & m);
    long t1 = (((t | ~m) + 1) & m);
    int c = Long.numberOfTrailingZeros(v) + 2; // *
    long w = t1 | (((~t & t1) - 1 & m) >>> c);
    return w;
}

I'm not sure why the 2 in the line marked with an asterisk is a 2 instead of a 1.

Anyway, if you do x = next(x, 0x155) in a loop (start with x = 0x15 of course) you get those ten things you listed.

harold
  • 61,398
  • 6
  • 86
  • 164
  • Thanks harold. I'll try this now... do you have an implementation of "numberOfTrailingZeros"? If not, I'll write one. – Steve McLeod Sep 28 '13 at 18:04
  • @SteveMcLeod oh sorry, went wrong in the copying. Should be `Long.numberOfTrailingZeros` – harold Sep 28 '13 at 18:05
  • Start with x=0x15? or 0x155? – Steve McLeod Sep 28 '13 at 18:08
  • Start with x=0x15, the mask is 0x155 – harold Sep 28 '13 at 18:10
  • How do I know to start with 0x15? It works, but I don't know why you picked this as the starting value of x – Steve McLeod Sep 28 '13 at 18:11
  • It is the lowest value from the set, do you want suggestions on how to compute that for a variable mask? – harold Sep 28 '13 at 18:15
  • I'd love suggestions...I wrote something to do it just now, but not being an amateur at bit-fiddling, I'm curious to see a pro do it :) – Steve McLeod Sep 28 '13 at 18:21
  • You could apply `m &= m - 1` (to remove the rightmost set bit) three times, and then xor the "`m` with three fewer bits set" with the original `m` to get just those three bits. For high or variable counts .. just loop it I guess. `expand_right` would also work, but not really worth it without hardware support, even though it avoids that loop. – harold Sep 28 '13 at 18:35
  • This seems to only work if the mask contains alternating one and zero bits. – Falk Hüffner Mar 13 '15 at 22:30
  • @FalkHüffner the bits don't align properly otherwise. Well it can actually work in a couple of other cases if the +2 is changed back to +1, not many cases though. – harold Mar 13 '15 at 22:59
2

I also tried to adapt the standard algorithm for enumerating combinations of a complete set of bits. That algorithm finds the lowest group of 1-bits, moves the highest bit one to the left, and shifts the other ones to the bottom. So for our case, we need to find the k lowest set bits. I didn't have any idea how to do that without a loop, which assumes a fast "popcount" instruction is available (count the number of 1-bits):

unsigned next_combination(unsigned comb, unsigned set) {
    unsigned h = (-comb & (comb ^ set)) - 1;
    unsigned l = set;
    for (int i = 0; i < popcount(h & comb) - 1; ++i)
        l &= l - 1;
    comb = (set & h) ^ l;
    return comb;
}

Edit: I found a different approach that does without the popcount at the chess programming wiki: Traversing Subsets of a Set. It can be slightly simplified as follows:

unsigned next_combination(unsigned comb, unsigned set) {
    unsigned tmp = comb - 1;
    unsigned rip = set & ((tmp | comb) - set);
    for (comb = (tmp ^ rip) & comb; comb; rip ^= tmp, set ^= tmp) {
        tmp = set & -set;
        comb &= comb - 1;
    }
    return rip;
}

Since the loop is only executed once on average, this seems to be actually slightly faster on my machine, probably also because of the bad latency of popcount.

Falk Hüffner
  • 4,942
  • 19
  • 25
0

Here are a few fast solutions.

Constructing the array

public static final long[] toThreeBitCombinations(long e) {
    //   get lowest 1 bit; turn off that bit;
    final long a = e & -e; e ^= a;
    final long b = e & -e; e ^= b;
    final long c = e & -e; e ^= c;
    final long d = e & -e; e ^= d;

    final long ab = a | b;
    final long ae = a | e;
    final long be = b | e;
    final long cd = c | d;

    return new long[] { cd | e, be | d, ae | d, be | c, ae | c,
                        ab | e, b | cd, a | cd, ab | d, ab | c
                      };
}

This method produces the same output as you expect for your example input. If you want the array in ascending order instead:

public static final long[] toThreeBitCombinations(long e) {
    //   get lowest 1 bit; turn off that bit;
    final long a = e & -e; e ^= a;
    final long b = e & -e; e ^= b;
    final long c = e & -e; e ^= c;
    final long d = e & -e; e ^= d;

    final long ab = a | b;
    final long ae = a | e;
    final long be = b | e;
    final long cd = c | d;

    return new long[] { ab | c, ab | d, a | cd, b | cd, ab | e,
                        ae | c, be | c, ae | d, be | d, cd | e
                      };
}

As can be seen, the order is reversed.

We have, for the construction of the entire array:

ALU usage
  • 4 &
  • 14 |
  • 4 ^
  • 4 unary -
Variable memory usage
  • Maximum of 10 64-bit longs live simultaneously
Method calls
  • 1/10 of a call to this method per element

26 ALU instructions, 80 bytes used, and 1 method call for the entire array compare favorably with other solutions presented here. It also doesn't require the extra work of figuring out what combination-of-three-bits value to start those loops with.

Telling if an element is in the array without needing to construct the array

This is usually slower than constructing a ten-element array and using a linear search on it unless you're trashing arrays pretty quickly after constructing them.

public static final boolean inThreeBitCombinations(final long three, final long five) {
    return ((three & ~five) == 0) && (Long.bitCount(three) == 3);
}
Chai T. Rex
  • 2,972
  • 1
  • 15
  • 33