Skip to content

Commit 0c7b787

Browse files
authored
Create Bits-and-Subsets.md
1 parent b68afd9 commit 0c7b787

File tree

1 file changed

+247
-0
lines changed

1 file changed

+247
-0
lines changed

Notes/Bits-and-Subsets.md

Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
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 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 81 / 2 = 40
49+
40 mod 2 = 0 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 40 / 2 = 20
50+
20 mod 2 = 0 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 20 / 2 = 10
51+
10 mod 2 = 0 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 10 / 2 = 5
52+
5 mod 2 = 1 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 5 / 2 = 2
53+
2 mod 2 = 0 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 2 / 2 = 1
54+
1 mod 2 = 1 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 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_ = { &nbsp; }
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+
| { &nbsp; } | 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

Comments
 (0)