Boolean identities and laws
The identities, commutation, association, distribution, double negation and absorption, proved by truth table.
Do this lesson in the simulatorOrdinary algebra has rules you use without thinking: a + b = b + a, and a × (b + c) = ab + ac. Boolean algebra has rules too, some the same as ordinary algebra and some surprisingly different. With them you can rewrite an expression into a simpler one that is guaranteed to have the same truth table, which means a circuit with fewer gates, or a program condition that is easier to read.
The identities
Each identity can be checked by trying A = 0 and A = 1. OCR notation is on the left, AQA on the right.
| Identity | OCR | AQA | In words |
|---|---|---|---|
| AND with 0 | A ∧ 0 = 0 | A · 0 = 0 | anything AND false is false |
| AND with 1 | A ∧ 1 = A | A · 1 = A | true changes nothing |
| OR with 0 | A ∨ 0 = A | A + 0 = A | false changes nothing |
| OR with 1 | A ∨ 1 = 1 | A + 1 = 1 | anything OR true is true |
| idempotence | A ∧ A = A, A ∨ A = A | A · A = A, A + A = A | repeating yourself adds nothing |
| complement | A ∧ ¬A = 0 | A · A̅ = 0 | nothing is both true and false |
| complement | A ∨ ¬A = 1 | A + A̅ = 1 | everything is one or the other |
| double negation | ¬¬A = A | a double bar over A is A | two NOTs cancel |
Two of these are where Boolean algebra parts company with ordinary arithmetic. A + 1 = 1, whereas in arithmetic A + 1 is one more than A. And A · A = A, not A².
The laws
These are the laws your specification names. Each one comes in an AND version and an OR version.
Commutation: the order of the inputs does not matter. A ∧ B = B ∧ A and A ∨ B = B ∨ A
Association: when the operators are all the same, the brackets do not matter. (A ∧ B) ∧ C = A ∧ (B ∧ C) and (A ∨ B) ∨ C = A ∨ (B ∨ C)
Distribution: AND distributes over OR, like multiplying out brackets. A ∧ (B ∨ C) = (A ∧ B) ∨ (A ∧ C)
and, unlike ordinary algebra, OR also distributes over AND: A ∨ (B ∧ C) = (A ∨ B) ∧ (A ∨ C)
Double negation: ¬¬A = A.
Absorption: a term that already contains A adds nothing when combined with A. A ∨ (A ∧ B) = A and A ∧ (A ∨ B) = A
Absorption is not a new rule; it follows from the others. Here is the proof of the OR form, one law per step:
| Step | Expression | Reason |
|---|---|---|
| 1 | A ∨ (A ∧ B) | start |
| 2 | (A ∧ 1) ∨ (A ∧ B) | A = A ∧ 1 |
| 3 | A ∧ (1 ∨ B) | distribution, taking A out |
| 4 | A ∧ 1 | 1 ∨ B = 1 |
| 5 | A | A ∧ 1 = A |
In words: if A is 1 the whole thing is 1; if A is 0 then A ∧ B is 0 too; so the result is always just A.
A result that follows from these laws and is used constantly is:
A ∨ (¬A ∧ B) = (A ∨ ¬A) ∧ (A ∨ B) = 1 ∧ (A ∨ B) = A ∨ B
"A, or else not A but B" is simply "A or B".
Two ways to prove an equation
An exam may ask you to show that two expressions are equal. There are two ways.
- Algebra: rewrite one side into the other, one law per line, naming each law. This is what most simplification questions want.
- Truth tables: work out both expressions for every row. If every row matches, they are equal; if one row differs, they are not. This is proof by exhaustion: with n inputs there are only 2ⁿ cases, so you can check them all.
A computer is very good at the second way:
def show(name, f, g):
rows = []
for a in [0, 1]:
for b in [0, 1]:
rows.append((a, b, f(a, b), g(a, b)))
same = all(left == right for _, _, left, right in rows)
print(name, "same on every row" if same else "DIFFERENT")
for a, b, left, right in rows:
print(" ", a, b, left, right)
show("A OR (A AND B) vs A", lambda a, b: a | (a & b), lambda a, b: a)
show("A OR (NOT A AND B) vs A OR B", lambda a, b: a | ((1 - a) & b), lambda a, b: a | b)
show("A AND B vs A OR B", lambda a, b: a & b, lambda a, b: a | b)
Laws in robot code
The laws apply to conditions in programs as well as circuits. A student writes:
if (close and moving) or (close and turning):
Distribution takes out the shared close: close and (moving or turning). The condition tests close once rather than twice, and the intention is clearer. Absorption catches a common redundancy: if bumped or (bumped and fast): is just if bumped:.
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
drive(40, 0)
for step in range(30):
close = distance() < 30
moving = True
turning = False
if close and (moving or turning): # was (close and moving) or (close and turning)
stop()
print("stopped at", distance(), "cm")
break
wait(0.1)
Task: prove it by brute force
Write equivalent(f, g). Its inputs f and g are functions of three bits, f(a, b, c), each returning 0 or 1. It tries all eight rows in binary order (a, then b, then c, each 0 then 1) and returns the first row where f and g give different results, as a tuple (a, b, c), or None if they agree on every row.
Then, for each (name, f, g) in the list laws, in order, print one line: <name>: equivalent if they agree, or <name>: not equivalent at A=<a> B=<b> C=<c> using the row equivalent returned. That makes six lines.
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
laws = [
("distribution", lambda a, b, c: a & (b | c), lambda a, b, c: (a & b) | (a & c)),
("absorption", lambda a, b, c: a | (a & b), lambda a, b, c: a),
("association", lambda a, b, c: (a | b) | c, lambda a, b, c: a | (b | c)),
("OR over AND", lambda a, b, c: a | (b & c), lambda a, b, c: (a | b) & (a | c)),
("false friend", lambda a, b, c: a | (b & c), lambda a, b, c: (a | b) & c),
("half absorbed", lambda a, b, c: a & (a | b), lambda a, b, c: a | b),
]
def equivalent(f, g):
return None
Challenges
- The "false friend" looks like association but mixes two operators. Explain in one sentence why the brackets matter there.
- Prove A ∧ (A ∨ B) = A by algebra, naming the law at each step.
- Add a check that
fandgonly ever return 0 or 1, and use it to catch a function written with~.