Truth table
For every assignment of the variables, work out each subformula from the inside out, and finally say whether the formula is always true (a tautology), always false (a contradiction) or sometimes true.
A propositional formula's value depends only on its variables' values. With n variables there are 2^n
assignments; the truth table has a row for each.
- Time:
O(2^n · size) - Space:
O(2^n · size) for the whole table - Grimaldi, chapter 2
- Johnsonbaugh, chapter 1
(p → q) ↔ (¬q → ¬p): a conditional is equivalent to its contrapositive; all four rows are true: a tautology.
class Node:def __init__(self, op, left, right, text):self.op = opself.left = leftself.right = rightself.text = text # how the subformula is writtendef truth_table(formula):root = parse(formula)if root is None:return Nonenames = []columns = []collect(root, names, columns)names.sort()header = names + [c.text for c in columns]rows = [header]trues = 0for m in range(2 ** len(names)):env = {}for i in range(len(names)):env[names[i]] = (m >> (len(names) - 1 - i)) % 2 == 1row = []for name in names:row.append("T" if env[name] else "F")for c in columns:env[c.text] = value(c, env)row.append("T" if env[c.text] else "F")rows.append(row)last = env[root.text]if last:trues = trues + 1if trues == 2 ** len(names):kind = "tautology"elif trues == 0:kind = "contradiction"else:kind = "contingent"return kinddef value(c, env):# Each column only reads columns to its left: its operands were filled in first.if c.op == "¬":return not env[c.right.text]a = env[c.left.text]b = env[c.right.text]if c.op == "∧":return a & bif c.op == "∨":return a | bif c.op == "→":return (not a) | breturn a == bdef collect(node, names, columns):# Postorder, so every column comes after the columns it depends on; a repeated subformula gets one column.if node.op == "var":if node.text not in names:names.append(node.text)returnif node.left is not None:collect(node.left, names, columns)collect(node.right, names, columns)for c in columns:if c.text == node.text:returncolumns.append(node)def parse(text):# ~ not, & and, | or, > implies, = if and only if; from tightest to loosest. > groups to the right.toks = []for c in text:if c != " ":toks.append(c)pos = [0]root = parse_binary(toks, pos, 0)if root is None or pos[0] != len(toks):return Nonereturn rootLEVELS = [["=", "↔"], [">", "→"], ["|", "∨"], ["&", "∧"]]def parse_binary(toks, pos, level):if level == len(LEVELS):return parse_not(toks, pos)sym, op = LEVELS[level]left = parse_binary(toks, pos, level + 1)while left is not None and pos[0] < len(toks) and toks[pos[0]] == sym:pos[0] += 1right = parse_binary(toks, pos, level if sym == ">" else level + 1)if right is None:return Noneleft = Node(op, left, right, "(" + left.text + " " + op + " " + right.text + ")")return leftdef parse_not(toks, pos):if pos[0] >= len(toks):return Nonec = toks[pos[0]]pos[0] += 1if c == "~":inner = parse_not(toks, pos)if inner is None:return Nonereturn Node("¬", None, inner, "¬" + inner.text)if c == "(":inner = parse_binary(toks, pos, 0)if inner is None or pos[0] >= len(toks) or toks[pos[0]] != ")":return Nonepos[0] += 1return innerif c.isalpha():return Node("var", None, None, c)return None
Start
The table is still empty.
Recursion tree
Filled orange = running call · Orange outline = still on the stack · Grey = finished
Local variables
Orange = changed in this step
Call stack
- truth_table()line 9
Explanation
The idea
A propositional formula's value depends only on its variables' values. With n variables there are 2^n
assignments; the truth table has a row for each.
To keep it readable, besides the variables' columns there is a column for every subformula, from the innermost outwards. Each column only needs the columns before it.
The operators
| Typed | Operator | True when |
|---|---|---|
| ~ | ¬ | the operand is false |
| & | ∧ | both are true |
| \| | ∨ | at least one is true |
| > | → | the antecedent is false or the consequent true |
| = | ↔ | both have the same value |
Classifying
- Tautology: every row is true; rules of inference, for example.
- Contradiction: no row is true.
- Contingent: some rows.
Two formulas are equivalent if A ↔ B is a tautology.
The input
The variables are p, q, r and s. ~ binds tightest and = loosest; > groups to the right.