Bitwise Set Operations

Prerequisites: bitwise operations and integers and bit sequences.

The binary representation of a number can be viewed as a set, where 00 means that an element is absent from the set and 11 means that it is present. For example, the set {1,3,4,8}\{1,3,4,8\} can be represented as (100011010)2(100011010)_2. The corresponding bitwise operations can then be viewed as operations on sets.

OperationSet representationBitwise representation
Intersectiona∩ba \cap baAND⁡ba \operatorname{AND} b
Uniona∪ba \cup baOR⁡ba \operatorname{OR} b
Complementaˉ\bar{a}NOT⁡a\operatorname{NOT} a (the universal set is all 1s in binary)
Differencea∖ba \setminus baAND⁡NOT⁡ba \operatorname{AND} \operatorname{NOT} b
Symmetric differencea△ba\triangle baXOR⁡ba \operatorname{XOR} b

Before introducing operations for enumerating subsets of a set, let us first look at some applications of bitwise operations.

Powers of 2 modulo

Taking a number modulo a nonnegative power of 22 is equivalent to taking its last several bits in binary, and is equivalent to applying AND with mod−1mod-1.

C++

int modPowerOfTwo(int x, int mod) { return x & (mod - 1); }

Python

def modPowerOfTwo(x, mod):
    return x & (mod - 1)

It follows that taking a nonnegative power of 22 modulo itself gives 00; in other words, if nn is a nonnegative power of 22, the result of ANDing nn with n−1n-1 is 00.

In fact, for a positive integer nn, n−1n-1 clears the lowest position of nn that contains 11, and changes all following bits to 11. Therefore, ANDing nn with n−1n-1 is equivalent to removing the lowest position of nn that contains 11.

This can be used to determine whether a number is a nonnegative power of 22. If a number nn has exactly one 11 in its binary representation, then nn is a nonnegative power of 22.

C++

bool isPowerOfTwo(int n) { return n > 0 && (n & (n - 1)) == 0; }

Python

def isPowerOfTwo(n):
    return n > 0 and (n & (n - 1)) == 0

Subset enumeration

Enumerating all subsets of a set represented by a binary number is equivalent to enumerating all submasks of the corresponding mask.

A mask is a string of binary digits used in an AND operation with a source operand to obtain a new operand after masking out some input bits of the source.

A 11 bit in the mask means that the corresponding bit of the source is retained, while a 00 bit means that the corresponding bit of the source is set to 00. Changing some of the mask's 11 bits to 00 gives a submask of the mask; the mask itself is also its own submask.

Given a mask mm, to iterate efficiently over all submasks of mm, denoted ss, one can use the following bitwise technique:

// enumerate the non-empty subsets of m in descending order
int s = m;
while (s > 0) {
  // s is a non-empty subset of m
  s = (s - 1) & m;
}

Or use the more compact for statement:

// enumerate the non-empty subsets of m in descending order
for (int s = m; s; s = (s - 1) & m)
// s is a non-empty subset of m

Neither of these code snippets processes the submask equal to 00. To process the submask equal to 00, another method can be used, for example:

// enumerate all subsets of m in descending order
for (int s = m;; s = (s - 1) & m) {
  // s is a subset of m
  if (s == 0) break;
}

We next prove that the code above visits every submask of mm without repetition and in descending order.

Suppose the current bitmask is ss and we want to visit the next bitmask. In mask ss, subtracting 11 is equivalent to removing the rightmost set bit of ss and changing all bits to its right to 11.

For s−1s-1 to become a new submask, all extra bits not contained in mask mm must be removed. These extra 11 bits can be removed with the bitwise operation (s - 1) & m.

These two operations are equivalent to cutting down s−1s-1 to determine the largest value available in arithmetic order, namely the next submask after ss in descending order.

Therefore, the algorithm generates all submasks of the mask in descending order, using only two operations per iteration.

The special case is s=0s=0. After executing s−1s-1, we obtain −1-1, whose bits are all 11. After the operation (s - 1) & m, the new ss is equal to mm. Therefore, if the loop does not terminate at s=0s=0, it cannot terminate at all.

Using popcount(m)\text{popcount}(m) to denote mm's binary count of 11 bits, this method enumerates the subsets in O(2popcount(m))O(2^{\text{popcount}(m)}) time over mm.

Enumerating submasks of all masks

In problems using subset DP, it is sometimes useful to enumerate all submasks of every mask:

for (int m = 0; m < (1 << n); ++m)
  // enumerate the non-empty subsets of m in descending order
  for (int s = m; s; s = (s - 1) & m)
// s is a non-empty subset of m

This enumerates every subset of every subset of a set of size nn.

We next prove that this operation has time complexity O(3n)O(3^n), where nn is the total number of bits in the mask, or equivalently the number of elements in the set.

Consider bit ii, the ii-th element of the set. There are three possibilities:

  • In mask mm it is 00, and therefore in submask ss it is 00; the element is in neither the larger nor the smaller subset.
  • In mm it is 11, but in ss it is 00; the element is only in the larger subset and not in the smaller subset.
  • In both mm and ss it is 11; the element is in both subsets.

There are nn bits in total, so there are 3n3^n different combinations.

Another proof is as follows.

If mask mm has kk 11s, then it has 2k2^k submasks. For a given kk, there are (nk)\dbinom{n}{k} possible masks mm, so the total number of masks is

∑k=0n(nk)2k\sum_{k=0}^n \dbinom{n}{k} 2^k

The sum above is the expansion of (1+2)n(1+2)^n by the binomial theorem, so there are 3n3^n different combinations.

References

This page is mainly translated from the blog post Перебор всех подмасок данной маски and its English translation Submask Enumeration. The Russian version is licensed under Public Domain + Leave a Link; the English version is licensed under CC-BY-SA 4.0.

Problems

This content comes from OI Wiki: Bitwise Set Operations. See the original for authors and revision history. AIOJ has adapted the formatting, in-site links, and resource paths.

Content is shared under CC BY-SA 4.0 and SATA license; additional terms are governed by the original notice. Thanks to the OI Wiki authors and contributors; per the SATA terms, please visit the OI Wiki repository and star it to show your support.