Discrete Mathematics Fundamentals
A Complete 10-Chapter Maths for Programmers Course
Table of Contents
- Why Discrete Math Matters for Programmers
- Propositional Logic: Statements, Connectives & Truth Tables
- Predicate Logic & Quantifiers
- Sets & Set Operations
- Relations: Properties, Equivalence Relations & Partial Orders
- Functions: Injective, Surjective & Bijective
- Proof Techniques: Direct, Contrapositive & Contradiction
- Mathematical Induction
- Combinatorics: Counting, Permutations & Combinations
- Capstone: Applying Discrete Math to Real Programming Problems
Why Discrete Math Matters for Programmers
Discrete Mathematics Fundamentals
Chapter 1 · Why Discrete Math Matters for Programmers
A computer is, at every level, a discrete machine — a bit is either 0 or 1, never something in between; a list holds a specific, countable number of elements; a loop runs a specific, countable number of times; a program executes one distinct instruction, then the next. Discrete mathematics is the branch of math built specifically to reason about exactly this kind of world — countable, distinct, all-or-nothing — which is exactly why it sits underneath so much of computer science, whether or not the connection is ever made explicit.
Discrete vs. Continuous — What the Word Actually Means
"Discrete" and "continuous" describe two fundamentally different kinds of quantity, and most of mathematics falls cleanly into one camp or the other.
| Discrete | Continuous | |
|---|---|---|
| Values | Distinct, separate, countable — you can list them one by one | Smoothly varying — between any two values, infinitely many more exist |
| Example quantity | The number of items in a shopping cart | A person's exact height |
| Core math tools | Logic, sets, combinatorics, graph theory | Calculus, real analysis |
| Where it shows up in code | Loop counts, array indices, boolean conditions, database rows | Physics simulations, gradient descent in machine learning |
Both matter for programming — a machine learning course will eventually need calculus and linear algebra (both on this subject's own future list). This particular course is about the discrete side specifically: the math of things that are counted, not measured.
Five Concrete Connections to Code
Every topic in this course maps onto something you've almost certainly already used, whether or not it was ever named this way:
| Discrete math topic | Where it actually shows up |
|---|---|
| Propositional & predicate logic (Ch.2–3) | Every if statement, every boolean expression, every WHERE clause in SQL — all of it is propositional logic wearing different syntax |
| Sets (Ch.4) | Python's set, JavaScript's Set, SQL's DISTINCT and UNION — all directly implementing set operations |
| Relations & functions (Ch.5–6) | A database table is a relation in the mathematical sense; a hash map is a function from keys to values |
| Proof technique & induction (Ch.7–8) | Arguing a recursive function terminates correctly, or that a loop invariant genuinely holds on every pass, is a proof by induction whether or not anyone calls it one |
| Combinatorics (Ch.9) | "How many possible states could this system be in" underlies complexity analysis, test-case coverage, and cryptographic key-space size |
What This Course Won't Cover
Several genuinely related topics are deliberately left for their own future courses under this same Maths for Programmers subject, rather than folded in here as extra chapters:
- Graph Theory — networks, trees, and pathfinding get their own dedicated course, even though a graph is technically also a kind of relation (Chapter 5's own territory)
- Boolean Algebra & Digital Logic — logic gates and circuit-level reasoning get their own dedicated course, even though propositional logic (Chapter 2) is the direct mathematical foundation underneath them
- Algorithms & Complexity — Big-O analysis gets its own dedicated course, even though combinatorics (Chapter 9) is exactly the counting math that analysis depends on
Where This Course Is Headed
| Chapter | Topic |
|---|---|
| 2 | Propositional Logic — Statements, Connectives & Truth Tables |
| 3 | Predicate Logic & Quantifiers |
| 4 | Sets & Set Operations |
| 5 | Relations — Properties, Equivalence Relations & Partial Orders |
| 6 | Functions — Injective, Surjective & Bijective |
| 7 | Proof Techniques — Direct, Contrapositive & Contradiction |
| 8 | Mathematical Induction |
| 9 | Combinatorics — Counting, Permutations & Combinations |
| 10 | Capstone — Applying Discrete Math to Real Programming Problems |
Hands-On Exercises
Classify each of the following as discrete or continuous, and briefly justify each answer: (a) the number of users currently logged into a website, (b) the exact CPU temperature at a given instant, (c) the number of rows returned by a SQL query, (d) the amount of time a function took to execute.
📄 View solutionA colleague claims "math for programmers" should just mean calculus and linear algebra, since that's what machine learning courses always cover first. Using this chapter's own five connections, explain what specifically would be missing from a programmer's toolkit if discrete math were skipped entirely.
📄 View solutionFor each of the following real code artifacts, name which discrete math topic from this chapter's own table it most directly maps to, and explain the connection in one or two sentences: (a) a Python set used to remove duplicate values from a list, (b) a SQL table with a UNIQUE constraint on an email column, (c) an if statement with three chained and/or conditions.
Chapter 1 Quick Reference
- Discrete = countable, distinct values; continuous = smoothly varying values — computers are fundamentally discrete machines
- Five direct connections: logic → conditionals, sets → collection types, relations → databases/hash maps, proof/induction → correctness arguments, combinatorics → complexity/keyspace counting
- Deliberately out of scope here: Graph Theory, Boolean Algebra & Digital Logic, and Algorithms & Complexity each get their own future course
- This course stays tightly scoped to logic, sets, relations, and combinatorics — the direct foundation those three future courses will each build on
- Next chapter: Propositional Logic — statements, connectives, and truth tables
Propositional Logic: Statements, Connectives & Truth Tables
Discrete Mathematics Fundamentals
Chapter 2 · Propositional Logic: Statements, Connectives & Truth Tables
Chapter 1 claimed that "every if statement, every boolean expression ... is propositional logic wearing different syntax." This chapter makes that literal — the formal system for reasoning about statements that are simply true or false, and the exact operations that combine them: the same operations as &&, ||, and !, just written with mathematical symbols instead of code syntax.
What Counts as a Proposition
A proposition is a statement that is unambiguously either true or false — never both, never neither, and never a matter of opinion.
| Statement | Is it a proposition? |
|---|---|
5 > 3 | Yes — true |
| "Paris is the capital of France" | Yes — true |
| "What time is it?" | No — a question, not a statement that's true or false |
x > 3 | No — not yet. Its truth depends on a value x hasn't been given. This is a predicate, and Chapter 3 is entirely about it. |
The Five Core Connectives
Propositions combine using connectives — and every one of them already has a direct equivalent in code you've used before.
| Connective | Symbol | Meaning | Code equivalent |
|---|---|---|---|
| NOT | ¬p | True exactly when p is false | not p / !p |
| AND | p ∧ q | True only when both are true | p and q / p && q |
| OR | p ∨ q | True when at least one is true | p or q / p || q |
| XOR | p ⊕ q | True when exactly one is true, not both | p != q (on booleans) / p ^ q |
| IMPLIES | p → q | False only when p is true and q is false | No direct operator — built from (not p) or q |
Truth Tables — Defining a Connective by Every Possible Case
A truth table lists every combination of inputs and the resulting output — the formal, complete definition of what a connective actually means.
| p | ¬p |
|---|---|
| T | F |
| F | T |
| p | q | p ∧ q | p ∨ q | p ⊕ q | p → q |
|---|---|---|---|---|---|
| T | T | T | T | F | T |
| T | F | F | T | T | F |
| F | T | F | T | T | T |
| F | F | F | F | F | T |
if not condition: return works the way it does: when the precondition doesn't hold, the rest of the logic is vacuously satisfied, because there was never a case to check in the first place.
De Morgan's Laws — Simplifying Negated Conditions
Two of the most practically useful identities in this entire chapter, both directly checkable against the truth tables above:
| Law | Statement |
|---|---|
| De Morgan's (AND) | ¬(p ∧ q) ≡ ¬p ∨ ¬q |
| De Morgan's (OR) | ¬(p ∨ q) ≡ ¬p ∧ ¬q |
In plain terms: negating an AND flips it to an OR of the negations, and negating an OR flips it to an AND of the negations — the connective itself flips, not just the individual pieces.
not a and not b looks like a natural way to write "not both" but it's actually the truth table for "neither" — a genuinely different condition. De Morgan's Law is exactly the tool that tells you the correct rewrite of not (a and b) is (not a) or (not b), not (not a) and (not b).
Logical Equivalence
Two propositions are logically equivalent (written ≡) when they produce the same output for every possible combination of inputs — verified by comparing their truth tables row by row. De Morgan's Laws above are exactly this: two differently-written expressions that are provably, always, the same thing. This matters directly for refactoring — if two boolean expressions are logically equivalent, replacing one with the other can never change what your program does.
Short-Circuit Evaluation — A Runtime Detail, Not a Logic Change
Python and JavaScript's and/or (&&/||) don't always evaluate both sides — if the left side of and is already false, the right side is never even run, since the overall result is already determined. This is called short-circuit evaluation. It doesn't change the truth table at all — the logical result is identical either way — but it does matter if the right-hand side has a side effect (like calling a function), since that side effect might never actually happen.
Hands-On Exercises
Build a complete truth table for the compound proposition (p ∨ q) ∧ ¬p, and state in plain English what condition it actually captures.
A function contains the condition if not (is_valid and is_authorized):. Using De Morgan's Law, rewrite this without a negated compound expression, and explain why your rewrite is guaranteed to behave identically for every possible combination of is_valid and is_authorized.
Determine whether p → q and q → p are logically equivalent, by building both truth tables and comparing them row by row. If they're not equivalent, give a concrete real-world example of a true "if p then q" statement whose reverse is false.
Chapter 2 Quick Reference
- A proposition is a statement that's unambiguously true or false — a statement with an unknown variable is a predicate instead (Chapter 3)
- Five connectives: NOT (¬), AND (∧), OR (∨), XOR (⊕), IMPLIES (→) — each with a direct code equivalent
- A truth table defines a connective completely, by listing the output for every combination of inputs
- p → q is true whenever p is false, regardless of q — the source of "vacuous truth"
- De Morgan's Laws: ¬(p ∧ q) ≡ ¬p ∨ ¬q, and ¬(p ∨ q) ≡ ¬p ∧ ¬q — the connective itself flips
- Logically equivalent expressions can always be swapped for each other without changing program behavior
- Short-circuit evaluation is a runtime detail — it doesn't change the logical truth table, only whether side effects on the right-hand side actually run
- Next chapter: Predicate logic and quantifiers — what happens once a statement has a variable in it
Predicate Logic & Quantifiers
Discrete Mathematics Fundamentals
Chapter 3 · Predicate Logic & Quantifiers
Chapter 2 flagged x > 3 as not a proposition — its truth depends on a variable that hasn't been given a value yet. This chapter covers exactly that case properly: predicates, and the two quantifiers that turn a predicate into an actual, honest-to-goodness proposition.
What a Predicate Actually Is
A predicate is a statement containing one or more variables, which becomes a genuine proposition — true or false — the moment those variables are given specific values. Written P(x), a predicate is a template for a proposition, not a proposition itself.
| Expression | What it is |
|---|---|
P(x): x > 3 | A predicate — no fixed truth value yet |
P(5) | A proposition — true |
P(2) | A proposition — false |
This is already exactly what a boolean-returning function is in code — a predicate, parameterized truth, written in a different notation:
The Universal Quantifier — ∀ ("for all")
∀x P(x) means P(x) is true for every value of x in whatever domain you're considering. This is exactly what Python's all() computes.
The Existential Quantifier — ∃ ("there exists")
∃x P(x) means P(x) is true for at least one value of x in the domain — not necessarily all, just one is enough. This is exactly what Python's any() computes.
∀x P(x) only makes sense once you've said what x ranges over — "for all x" in what set, exactly? In code, the domain is simply whatever you're iterating over — ages in the examples above. Leaving the domain unstated (in math or in code) is a genuine, common source of confusion.
Combining Quantifiers: "Every P Is a Q" vs. "Some P Is a Q"
In practice, quantifiers almost always pair with a specific second connective, and the pairing is easy to get backwards:
| English | Correct translation | Why this connective |
|---|---|---|
| "Every P is a Q" | ∀x (P(x) → Q(x)) | For any x, if it's a P, then it must also be a Q |
| "Some P is a Q" | ∃x (P(x) ∧ Q(x)) | There's an x that is both a P and a Q, at the same time |
∀x (P(x) ∧ Q(x)) for "every P is a Q" is a genuine, common error — it actually claims that every single x in the entire domain is both a P and a Q, which is a far stronger (and usually false) statement. "Every student who submitted on time gets full marks" does not mean every person in existence submitted on time and got full marks — it means if someone submitted on time, then they got full marks. ∀ pairs with →; ∃ pairs with ∧ — mixing these up is the single most common predicate-logic translation mistake.
Negating Quantified Statements
Predicate logic has its own version of Chapter 2's De Morgan's Laws — negating a quantifier flips it to the other quantifier:
| Statement | Negation | Plain English |
|---|---|---|
| ¬(∀x P(x)) | ≡ ∃x ¬P(x) | "Not all" means "at least one fails" |
| ¬(∃x P(x)) | ≡ ∀x ¬P(x) | "None exist" means "all fail" |
Nested Quantifiers — Order Genuinely Changes the Meaning
When two quantifiers appear together, swapping their order can produce a completely different statement:
| Statement | Meaning |
|---|---|
| ∀x ∃y Mother(y, x) | Every person x has some mother y — a possibly different y for each x |
| ∃y ∀x Mother(y, x) | There's one single y who is the mother of every person x |
The first is an ordinary true fact about the world. The second claims one person is literally everyone's mother — obviously false. Same symbols, same predicate, reversed order, opposite truth value. When both quantifiers are the same type (∀∀ or ∃∃), order doesn't matter; the moment they're mixed (∀∃ or ∃∀), it does.
Hands-On Exercises
Translate "every function in this module has a docstring" into quantified predicate logic, clearly stating the domain and which two predicates you're using. Then explain why using ∧ instead of → would be a translation mistake here.
📄 View solutionA test asserts not any(order.total < 0 for order in orders). Rewrite this using De Morgan's Law for quantifiers so it reads as an ∀ statement instead of a negated ∃ statement, and state in plain English what the rewritten version says.
For the two statements ∀x ∃y Likes(x, y) ("everyone likes someone") and ∃y ∀x Likes(x, y) ("there's someone everyone likes"), explain in your own words why these are genuinely different claims, and give an example of a group of people for which the first is true but the second is false.
📄 View solutionChapter 3 Quick Reference
- A predicate
P(x)becomes a proposition oncexis given a value — exactly a boolean-returning function in code - ∀x P(x) ("for all") ≡
all(P(x) for x in domain) - ∃x P(x) ("there exists") ≡
any(P(x) for x in domain) - A quantifier means nothing without a stated domain — in code, that's whatever you're iterating over
- "Every P is Q" → ∀x (P(x) → Q(x)); "Some P is Q" → ∃x (P(x) ∧ Q(x)) — ∀ pairs with →, ∃ pairs with ∧
- Negation flips the quantifier: ¬∀x P(x) ≡ ∃x ¬P(x); ¬∃x P(x) ≡ ∀x ¬P(x)
- Nested quantifier order matters when the two quantifiers differ (∀∃ vs. ∃∀) — same symbols, opposite meaning
- Next chapter: Sets and set operations