User:Jeff/Algorithms/Bit flipping

Back to ACM page

This article is about using bitmasks -- two's compliment integers represented internally as a binary array. Manipulating the individual bits of the integer allows us to use it as an extremely efficient vector or set<>.

Operations
In this section, I'll show how to convert code used on a vector to an integer bitmask.

Declaration
An integer has 32 bits, but the highest bit is the sign (positive/negative) bit for the number, which can make arithmetic confusing. If the bitmask will hold 30 elements or less, it should be declared as an int. If it needs to hold 31-63 elements, then an unsigned "long long" (64-bit) int should be used instead.

vector vec(20, false); int mask(0);

By initializing the integer to zero, all of its bits are set to false.

Index retrieval
The following code will test to see if bit n is on or off.

if (vec[n]) cout << "on"; if (!vec[n]) cout << "off"; if ((mask & (1 << n))) cout << "on"; if (!(mask & (1 << n))) cout << "off";

Assignment
Assignment works differently for bitmasks than it does for vectors. To change a bit from on to off or from off to on, that bit's value must be added to or subtracted from the bitmask. However, if code is run that tries to set a bit on that is already on, or vice versa, it will adversely affect the values of the other bits. Therefore, the value of the bit should be tested beforehand.

vec[n] = true; if (!(mask & (1 << n))) mask += 1 << n; vec[n] = false; if ((mask & (1 << n))) mask -= 1 << n;

To turn on a bit regardless of its current value, the |= operator can be used.

vec[n] = true; mask |= 1 << n;

To toggle the value of a bit, the ^= operator can be used.

vec[n] = !vec[n]; mask ^= 1 << n;

Subset generation
One common use for bitmasks is to exhaustively search all subsets of a given set. For example, a problem might give a list of objects, each of which has a weight and a value, and ask for the subset of those objects with the greatest value that can fit in a knapsack of a certain weight. This is known as the 0-1 knapsack problem, and in general there is no better algorithm than trying all possibilities.

int bestKnapsack(vector weights, vector values, int maxWeight) { int n(weights.size); int answer(0); for (int i(0); i < (1 << n); ++i) { // for each subset int weight(0), val(0); for (int j(0); j < n; ++j) { // for each object if (!(i & (1 << j))) continue; // this object isn't in this subset weight += weights[j]; val += values[j]; }    if (weight <= maxWeight) answer >?= val; }  return answer; }

In general, this technique can only be used when there are 20 or fewer elements in the set; otherwise it will be in danger of exceeding the time limit.