|
| 1 | +# Bits and Subsets |
| 2 | + |
| 3 | +## What is a bit? |
| 4 | + |
| 5 | +A _bit_ is the basic unit of information in computing. |
| 6 | + |
| 7 | +A bit can only have two values: **0** or **1** |
| 8 | + |
| 9 | +The two values for a bit can also be referred as _true_ or _false_, _on_ or _off_, etc. |
| 10 | + |
| 11 | +Numbers can be represented using bits: **base-2 (or binary)** |
| 12 | + |
| 13 | +## Binary Numbers |
| 14 | + |
| 15 | +Numbers that we see on a daily basis are said to be represented in _base 10_, called their _decimal representation_ (_e.g._, 32, -3949, 10000, 0) |
| 16 | + |
| 17 | +_Binary numbers_, which are expressed in _base-2_, are numbers that are composed of only **0s** and **1s**. |
| 18 | + |
| 19 | +16<sub>10</sub> == 10000<sub>2</sub> |
| 20 | + |
| 21 | +54<sub>10</sub> == 110110<sub>2</sub> |
| 22 | + |
| 23 | +### Converting from Binary to Decimal |
| 24 | + |
| 25 | +1. List out the powers of two above each bit, going right to left |
| 26 | +2. For each bit in the binary number, if the value of the bit is a one, add its corresponding power of two to a running sum |
| 27 | +3. After going through all of the bits, the running sum will be the _base-10_ representation of the number |
| 28 | + |
| 29 | +For example, let's convert 1001101<sub>2</sub> to a _base-10_ number: |
| 30 | + |
| 31 | +| 64 | 32 | 16 | 8 | 4 | 2 | 1 | |
| 32 | +|---|---|---|---|---|---|---| |
| 33 | +| 1 | 0 | 0 | 1 | 1 | 0 | 1 | |
| 34 | + |
| 35 | +For each bit that is a one, we will add their corresponding power of two to a running sum. |
| 36 | + |
| 37 | +**64** + 0 + 0 + **8** + **4** + 0 + **1** = 77 |
| 38 | + |
| 39 | +### Converting from Decimal to Binary |
| 40 | + |
| 41 | +1. Divide the number by two |
| 42 | +2. Write down the remainder (either **0** or **1**) |
| 43 | +3. If the number is non-zero, go back to step 1 |
| 44 | +4. The last remainder is the first bit in the _base-2_ representation of the number, the second to last remainder is the second bit, etc. |
| 45 | + |
| 46 | +For example, let's convert 81<sub>10</sub> to a _base-2_ number: |
| 47 | + |
| 48 | +81 mod 2 = 1 81 / 2 = 40 |
| 49 | +40 mod 2 = 0 40 / 2 = 20 |
| 50 | +20 mod 2 = 0 20 / 2 = 10 |
| 51 | +10 mod 2 = 0 10 / 2 = 5 |
| 52 | +5 mod 2 = 1 5 / 2 = 2 |
| 53 | +2 mod 2 = 0 2 / 2 = 1 |
| 54 | +1 mod 2 = 1 1 / 2 = 0 |
| 55 | + |
| 56 | +We then read the remainders in reverse order to get the _base-2_ representation of 81: **1010001<sub>2</sub>** |
| 57 | + |
| 58 | +## Bitwise Operations |
| 59 | + |
| 60 | +### NOT |
| 61 | + |
| 62 | +The bitwise _NOT_, or complement, is an operation that performs _negation_, which means bits that are zero become one, and those that are one become zero. |
| 63 | + |
| 64 | +For example, |
| 65 | +1011001 --> 0100110 |
| 66 | +1111111 --> 0000000 |
| 67 | +1000000 --> 0111111 |
| 68 | + |
| 69 | +In Java, the bitwise _NOT_ is represented by the '~' character. |
| 70 | + |
| 71 | +```java |
| 72 | +int result = ~32; // 31 |
| 73 | +``` |
| 74 | + |
| 75 | +### AND |
| 76 | + |
| 77 | +The bitwise _AND_ is an operation between two binary numbers that performs the _logical AND_ operation on each pair of the corresponding bits, which means that if both bits are one, the resulting bit is one, and any other combination of bits results in a zero bit. |
| 78 | + |
| 79 | +| | 1 | 0 | |
| 80 | +|---|---|---| |
| 81 | +| **1** | 1 | 0 | |
| 82 | +| **0** | 0 | 0 | |
| 83 | + |
| 84 | +In Java, the bitwise _AND_ is represented by the '&' character. |
| 85 | + |
| 86 | +```java |
| 87 | +int result = 102 & 53; // 36 |
| 88 | +``` |
| 89 | + |
| 90 | +For example, let's look at 102 & 53 |
| 91 | + |
| 92 | +### OR |
| 93 | + |
| 94 | +The bitwise _OR_ is an operation between two binary numbers that performs the _logical OR_ operation on each pair of corresponding bits, which means that if both bits are zero, the resulting bit is zero, and any other combination results of bits results in a one bit. |
| 95 | + |
| 96 | +| | 1 | 0 | |
| 97 | +|---|---|---| |
| 98 | +| **1** | 1 | 1 | |
| 99 | +| **0** | 1 | 0 | |
| 100 | + |
| 101 | +In Java, the bitwise _OR_ is represented by the '|' character. |
| 102 | + |
| 103 | +```java |
| 104 | +int result = 102 | 53; // 119 |
| 105 | +``` |
| 106 | + |
| 107 | +### XOR |
| 108 | + |
| 109 | +The bitwise _XOR_ is an operation between two binary numbers that performs the _logical XOR_ operation on each pair of corresponding bits, which means that if both bits are _different_, the resulting bit is one, and if the bits are the same, the resulting bit is zero. |
| 110 | + |
| 111 | +| | 1 | 0 | |
| 112 | +|---|---|---| |
| 113 | +| **1** | 0 | 1 | |
| 114 | +| **0** | 1 | 0 | |
| 115 | + |
| 116 | +In Java, the bitwise _XOR_ is represented by the '^' character. |
| 117 | + |
| 118 | +```java |
| 119 | +int result = 102 ^ 53; // 83 |
| 120 | +``` |
| 121 | + |
| 122 | +## Bit Shifting |
| 123 | + |
| 124 | +The _bit shift_ operation moves (or shifts) bits to the left or right in a binary number. |
| 125 | + |
| 126 | +In Java, a _left-shift_ is denoted by '<<', and a _right-shift_ is denoted by '>>'. |
| 127 | + |
| 128 | +You will also need to declare how many bits you want to shift a number by. |
| 129 | + |
| 130 | +For example: |
| 131 | +101<sub>2</sub> << 2 == 10100<sub>2</sub> |
| 132 | +100101<sub>2</sub> >> 3 == 100<sub>2</sub> |
| 133 | +1<sub>2</sub> << 5 == 100000<sub>2</sub> |
| 134 | + |
| 135 | +Let's look at the following pattern of bit shifts: |
| 136 | +1<sub>2</sub> << 0 == 1<sub>2</sub> == 1<sub>10</sub> |
| 137 | +1<sub>2</sub> << 1 == 10<sub>2</sub> == 2<sub>10</sub> |
| 138 | +1<sub>2</sub> << 2 == 100<sub>2</sub> == 4<sub>10</sub> |
| 139 | +1<sub>2</sub> << 3 == 1000<sub>2</sub> == 8<sub>10</sub> |
| 140 | + |
| 141 | +Based on this pattern, we can come up with the following rule: |
| 142 | + |
| 143 | +1<sub>2</sub> << k == 2<sup>k</sup> |
| 144 | + |
| 145 | +## Subsets |
| 146 | + |
| 147 | +Let _A_ = {2, 3, 5, 7}, and let _B_ be a subset of _A_. |
| 148 | + |
| 149 | +_B_ is a subset of _A_ if all elements in _B_ are also elements in _A_. |
| 150 | + |
| 151 | +The following are possible values for _B_: |
| 152 | +_B_ = {2, 3, 5} |
| 153 | +_B_ = {5, 7} |
| 154 | +_B_ = {2, 3, 5, 7} |
| 155 | +_B_ = { } |
| 156 | + |
| 157 | +### Number of Subsets |
| 158 | + |
| 159 | +For each element in _S_, we have two choices: |
| 160 | +- put the element in our subset |
| 161 | +- don't put the element in our subset |
| 162 | + |
| 163 | +Because we have two choices per element, the total number of subsets that we can have can be calculated with 2<sup>_N_</sup>, where _N_ is the number of elements in _S_. |
| 164 | + |
| 165 | +In order to create a subset, we either _take_ or _ignore_ an element in the set. Because there are two choices for each element, we can model a subset using a _binary string_ of length _N_. |
| 166 | + |
| 167 | +For a bit at some index _k_ in our binary string |
| 168 | +- if the bit at index _k_ is zero, then our subset _does not_ contain the _k_<sup>_th_</sup> element |
| 169 | +- if the bit at index _k_ is one, thjen our subset _does_ contain the _k_<sup>_th_</sup> element |
| 170 | + |
| 171 | +If we have a set _S_ = {2, 3, 5, 8}, there are four elements in this set, so we will need to create a binary string containing _N_ = 4 bits. |
| 172 | + |
| 173 | +| 0 | 1 | 2 | 3 | |
| 174 | +|---|---|---|---| |
| 175 | +| 2 | 3 | 5 | 8 | |
| 176 | + |
| 177 | +Let's look at different subsets and their binary string representations |
| 178 | + |
| 179 | +| Subset | Binary | Decimal | |
| 180 | +|---|---|---| |
| 181 | +| {3, 8} | 1010 | 10 | |
| 182 | +| {2, 3} | 0011 | 3 | |
| 183 | +| {2, 3, 5, 8} | 1111 | 15 | |
| 184 | +| { } | 0000 | 0 | |
| 185 | + |
| 186 | +### Subset Iteration |
| 187 | + |
| 188 | +How can we easily iterate over _all_ subsets of a set _S_ with _N_ elements? |
| 189 | + |
| 190 | +We know that an _empty_ subset has a binary string of 000...000<sub>2</sub> which is 0<sub>10</sub> |
| 191 | + |
| 192 | +We also know that a _full_ subset has a binary string of 111...111<sub>2</sub> which is 2<sup>_N_</sup> - 1 |
| 193 | + |
| 194 | +Now that we know all subsets fal in the range of [0, 2<sup>_N_</sup> - 1], we can iterate over all numbers in this range to get all of tyhe subsets of our set _S_. |
| 195 | + |
| 196 | +Recall that 1 << _N_ == 2<sup>_N_</sup> |
| 197 | + |
| 198 | +```java |
| 199 | +for ( int subset = 0; subset < ( 1 << N ); subset++ ) |
| 200 | +{ |
| 201 | + // This for-loop will find every subset of a set |
| 202 | +} |
| 203 | +``` |
| 204 | + |
| 205 | +Given a binary string, how can we check if the bit at index _k_ is a zero or one? |
| 206 | + |
| 207 | +| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
| 208 | +|---|---|---|---|---|---|---|---| |
| 209 | +| 1 | 0 | 0 | X | 0 | 1 | 1 | 1 | |
| 210 | + |
| 211 | +Let's say we want to see if the bit at _k_ = 4 is zero or one, we will use the bitwise _AND_ and left-shifting to get our answer. |
| 212 | + |
| 213 | +Since we are only worried about the bit at _k_ = 4, we can make a separate binary string that only has the bit at _k_ = 4 turned on. |
| 214 | + |
| 215 | +| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
| 216 | +|---|---|---|---|---|---|---|---| |
| 217 | +| 1 | 0 | 0 | X | 0 | 1 | 1 | 1 | |
| 218 | +| 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | |
| 219 | + |
| 220 | +We will then _AND_ these binary strings together, which will result in the binary string 000X0000 |
| 221 | + |
| 222 | +This will result in one of two things: |
| 223 | +- if X is zero, then the decimal representation of the binary string will be zero |
| 224 | +- if X is one, then the decimal representation of the binary string will be non-zero |
| 225 | + |
| 226 | +```java |
| 227 | +int leftShift = 1 << k; |
| 228 | +int result = subset & leftShift; |
| 229 | +if ( result == 0) |
| 230 | + // the bit at index k is zero |
| 231 | +else |
| 232 | + // the bit at index k is one |
| 233 | +``` |
| 234 | + |
| 235 | +We now know how to use the current value of subset to find which elements are in our subset: |
| 236 | + |
| 237 | +```java |
| 238 | +for ( int subset = 0; subset < ( 1 << N ); subset++ ) |
| 239 | +{ |
| 240 | + for ( int k = 0; k < N; k++ ) |
| 241 | + { |
| 242 | + int result = ( 1 << k ) & subset; |
| 243 | + if ( result != 0 ) |
| 244 | + // the item at index k is in the current subset |
| 245 | + } |
| 246 | +} |
| 247 | +``` |
0 commit comments