1

I have a bitwise mask represented as an integer. The mask and integer is limited to 32-bit integers.

I am interested in examining all subsets of the set bits of a given mask/integer, but I don't know of a good way to quickly find these subsets.

The solution that I've been using is

for(int j = 1; j <= mask; ++j)
{
  if(j & mask != 0)
  {
    // j is a valid subset of mask
  }
}

But this requires looping from j = 1 to mask, and I think there should be a faster solution than this.

Is there a faster solution than this?

My followup question is if I want to constrain the subset to be of a fixed size (i.e., a fixed number of set bits), is there a simple way to do that as well?

roulette01
  • 1,984
  • 2
  • 13
  • 26
  • Does this answer your question? [Interesting bitmask puzzle in C++](https://stackoverflow.com/questions/10304177/interesting-bitmask-puzzle-in-c) – harold Jun 15 '21 at 16:41
  • @harold I think so to the first question, but I had to add a followup question that is a bit different that constrains the subset to be a fixed size. – roulette01 Jun 15 '21 at 16:43
  • That's trickier, but possible in various ways. Are you OK with using platform specific bitwise operations such as [pdep](https://www.felixcloutier.com/x86/pdep)? That would make it easier (it's possible without that but hairy) – harold Jun 15 '21 at 16:47
  • @harold No, unfortunately. It has to be specific to the programming language. – roulette01 Jun 15 '21 at 16:51
  • @harold In the accepted answer in your link, the user links to https://www.chessprogramming.org/Traversing_Subsets_of_a_Set#Subsets_with_equal_Cardinality. There's actually a solution in here for "Subsets with equal Cardinality" so I think that may work, but haven't had the chance to look at it carefully yet. – roulette01 Jun 15 '21 at 16:53
  • The one under "Snoobing any Sets" is the one, though actually I think there's a version without a loop in it. If PDEP was allowed, the simple version of `snoob` could be used. – harold Jun 15 '21 at 17:07
  • `j & mask != 0` does not mean that `j` is subset of `mask`. You need `j & mask == j`. – user58697 Jun 15 '21 at 17:31
  • @user58697 Hmm, I don't think I see why `j & mask != 0` doesn't mean that `j` is a subset of `mask`. Can you please provide a counterexample? It seems to make sense in my head. Oh it seems that condition excludes when the subset is `0`? But that's the only situation wher eit doesn't work? – roulette01 Jun 15 '21 at 17:43
  • 1
    `mask = 0x110`, `j = 0x011`, `mask & j = 0x010`. – user58697 Jun 15 '21 at 17:46
  • 1
    A slightly simplified (but not loop-free) version of the "Snoobing any Sets" is here: https://stackoverflow.com/questions/19069488/finding-all-combinations-of-longs-with-certain-bits-set/29043170#29043170 – Falk Hüffner Jun 16 '21 at 16:57
  • `j` is a subset of `mask` iff `(j&mask) == j` – Matt Timmermans Jun 21 '21 at 03:22

3 Answers3

2

Iterate all the subset of state in C++:

for (int subset=state; subset>0; subset=(subset-1)&state) {}

This tip is usually used in Bit mask + dp question. The total time complexity is O(3^n) to iterate all the subset of all state, which is a great improvement from O(4^n) if using the code in this question.

YoungForest
  • 111
  • 1
  • 7
0

For a bitmask of length n the maximum number of subsets of set bits can be 2^n - 1. Hence we have to traverse over all the subsets if you want to examine them, and therefore the best complexity will be O(mask). The code cannot be improved further.

Ps: If you want to count the total number of subsets, then it can be solved using dynamic programming with much better time complexity.

potter1024
  • 210
  • 1
  • 10
  • I don't find your argument about complexity good. In the *worst case* it is like that, but typically you can do better, by a non-trivial factor. – harold Jun 15 '21 at 17:08
  • The time complexity always talks about the worst case – potter1024 Jun 15 '21 at 17:09
  • Yes, which is why it's useless to talk about time complexity here. – harold Jun 15 '21 at 17:10
  • You have to examine all the subsets, and let's say the mask is `11111` and now every `5-bit` number is it's subset except '00000'. So obviously you have to check every number from `00001` to `11111`. – potter1024 Jun 15 '21 at 17:14
  • How can anyone write a shortcut for traversing `n` numbers without actually traversing `n` numbers! – potter1024 Jun 15 '21 at 17:14
  • As you can see in the linked questions and articles, there are various ways to do so, that skip an exponential amount of work when the mask is not a trivial case like that. – harold Jun 15 '21 at 17:17
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/233809/discussion-between-harold-and-potter1024). – harold Jun 15 '21 at 17:24
0

Given x with a subset of the bits in mask, the next subset in order is ( (x|~mask) +1 ) & mask. This will wrap around to zero if x==mask.

I don't have a super fast way for subsets with a fixed number of bits.

Matt Timmermans
  • 53,709
  • 3
  • 46
  • 87