Finding all combinations of longs with certain bits set

心不动则不痛 提交于 2019-12-04 13:07:02

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.

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.

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