Discrete mathematics
The discrete structures behind data structures: sets, relations, functions and their types, Cartesian products, tuples, sequences, multisets and permutations.
Learning goals
- Use set operations and relate them to Java collections
- Classify relations and functions
- Count products, sequences and arrangements
Prerequisites
Sets
A set is an unordered collection of distinct elements. Operations: union A ∪ B, intersection A ∩ B, difference A \ B and subset A ⊆ B. HashSet implements a set; addAll, retainAll and removeAll are union, intersection and difference.
The size of a power set (all subsets) of an n-element set is 2ⁿ, which is why subset enumeration is exponential.
1Set<Integer> a = new HashSet<>(List.of(1, 2, 3)), b = new HashSet<>(List.of(2, 3, 4));2Set<Integer> union = new HashSet<>(a); union.addAll(b); // {1, 2, 3, 4}3Set<Integer> inter = new HashSet<>(a); inter.retainAll(b); // {2, 3}4Set<Integer> diff = new HashSet<>(a); diff.removeAll(b); // {1}
Relations
A relation on a set is a set of ordered pairs. It is reflexive if every x relates to itself, symmetric if x ~ y implies y ~ x, and transitive if x ~ y and y ~ z imply x ~ z. An equivalence relation has all three and splits the set into disjoint classes, which is exactly what union-find maintains.
A partial order (reflexive, antisymmetric, transitive) underlies sorting and dependency graphs.
| Relation | Reflexive | Symmetric | Transitive | Kind |
|---|---|---|---|---|
| = on integers | yes | yes | yes | equivalence |
| ≤ on integers | yes | no | yes | partial order |
| < on integers | no | no | yes | strict order |
| connected in a graph | yes | yes | yes | equivalence |
| friend of (social network) | no | yes | no | neither |
Functions
A function f: A → B assigns every element of A (the domain) exactly one element of B (the codomain). A Java method that returns a value and has no side effects is a function; a Map is a finite function from keys to values.
Hash functions map keys to bucket numbers; because there are more keys than buckets, collisions are unavoidable.
1Function<Integer, Integer> square = x -> x * x;2int y = square.apply(7); // 493Map<String, Integer> lengthOf = Map.of("cat", 3, "horse", 5); // a function on a finite domain
Injective/surjective/bijective
A function is injective (one-to-one) if different inputs give different outputs, surjective (onto) if every element of the codomain is hit, and bijective if both. A bijection has an inverse and means the two sets have the same size.
Perfect hashing aims for an injective hash on a known key set. Sorting is a bijection from positions to positions. A hash function from all strings to 32-bit ints cannot be injective.
| Function | Injective | Surjective |
|---|---|---|
| f(x) = 2x on integers | yes | no (odd numbers missed) |
| f(x) = x² on integers | no (f(2) = f(-2)) | no |
| f(x) = x + 1 on integers | yes | yes (bijective) |
| s.length() on strings | no | onto the non-negative integers |
Cartesian product
The Cartesian product A × B is the set of all ordered pairs (a, b), with |A × B| = |A| · |B|. Nested loops enumerate it: the grid of cells in a matrix is rows × columns, and the edges of a complete directed graph are V × V.
The size multiplies, so the product of k sets with n elements each has nᵏ elements.
1int[] rows = {0, 1, 2}, cols = {0, 1};2int count = 0;3for (int r : rows) for (int c : cols) count++; // 3 * 2 = 6 pairs
Tuples
A tuple is a fixed-size ordered list of values that may have different types, such as a point (x, y) or an edge (u, v, w). In Java use a record, which gives you equals and hashCode so tuples can be keys.
Order matters: (1, 2) ≠ (2, 1).
1record Edge(int u, int v, int w) {}2Edge e = new Edge(0, 3, 12);3boolean same = e.equals(new Edge(0, 3, 12)); // true
Sequences
A sequence is an ordered list that may contain repeats; position matters. Arrays, lists and strings are sequences. A subsequence keeps the relative order but may skip elements, whereas a substring is contiguous. A sequence of n elements has 2ⁿ subsequences (including the empty one) but only n(n + 1)/2 non-empty substrings.
Longest common subsequence and longest increasing subsequence are classic dynamic programming problems.
| Of "abc" | Count | Examples |
|---|---|---|
| Substrings (non-empty) | 6 | a, ab, abc, b, bc, c |
| Subsequences (including empty) | 8 | "", a, b, c, ab, ac, bc, abc |
Multisets
A multiset (bag) is like a set that tracks how many times each element occurs. In Java use a Map<T, Integer> of counts, or a TreeMap when you need sorted keys. Anagram checks and frequency problems compare multisets.
Two strings are anagrams exactly when their multisets of letters are equal.
1static Map<Character, Integer> bag(String s) {2Map<Character, Integer> m = new HashMap<>();3for (char c : s.toCharArray()) m.merge(c, 1, Integer::sum);4return m;5}6boolean anagram = bag("listen").equals(bag("silent")); // true
Permutations
A permutation is an ordering of items. There are n! orderings of n distinct items, and P(n, k) = n! / (n - k)! ordered selections of k of them. Brute-force search over permutations is feasible only for n up to about 10.
Choose n and k in the animation and compare the count with the list of arrangements.
1static void permute(int[] a, int k, List<int[]> out) {2if (k == a.length) { out.add(a.clone()); return; }3for (int i = k; i < a.length; i++) {4swap(a, k, i); permute(a, k + 1, out); swap(a, k, i);5}6}7static void swap(int[] a, int i, int j) { int t = a[i]; a[i] = a[j]; a[j] = t; }
Permutations nPr and combinations nCr
Interactive · edit the inputitems
current choice
found so far (0 of 12)
Choose 2 of 4 items where order matters: n × (n-1) × … = 4! / 2! = 12.
1static void pick(int[] items, int r, boolean ordered, int start,2List<Integer> current, boolean[] used, List<List<Integer>> out) {3if (current.size() == r) { out.add(new ArrayList<>(current)); return; }4for (int i = ordered ? 0 : start; i < items.length; i++) {5if (used[i]) continue; // each item at most once6used[i] = true;7current.add(items[i]);8pick(items, r, ordered, i + 1, current, used, out);9current.remove(current.size() - 1); // backtrack10used[i] = false;11}12}
Live state
n
4
r
2
expected
12
Execution trace (dry run table)Show 30 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L1 | n=4 r=2 expected=12 | Choose 2 of 4 items where order matters: n × (n-1) × … = 4! / 2! = 12. |
| 2 | L7 | picked=1 size=1 r=2 | Pick 1: choice 1 of 2. There were 4 options for the first slot, 3 for the second, and so on (multiplication principle). |
| 3 | L7 | picked=2 size=2 r=2 | Pick 2: choice 2 of 2. |
| 4 | L3 | found=1 expected=12 | Complete: 1, 2. That is arrangement #1. |
| 5 | L7 | picked=3 size=2 r=2 | Pick 3: choice 2 of 2. |
| 6 | L3 | found=2 expected=12 | Complete: 1, 3. That is arrangement #2. |
| 7 | L7 | picked=4 size=2 r=2 | Pick 4: choice 2 of 2. |
| 8 | L3 | found=3 expected=12 | Complete: 1, 4. That is arrangement #3. |
| 9 | L7 | picked=2 size=1 r=2 | Pick 2: choice 1 of 2. There were 4 options for the first slot, 3 for the second, and so on (multiplication principle). |
| 10 | L7 | picked=1 size=2 r=2 | Pick 1: choice 2 of 2. |
| 11 | L3 | found=4 expected=12 | Complete: 2, 1. That is arrangement #4. |
| 12 | L7 | picked=3 size=2 r=2 | Pick 3: choice 2 of 2. |
| 13 | L3 | found=5 expected=12 | Complete: 2, 3. That is arrangement #5. |
| 14 | L7 | picked=4 size=2 r=2 | Pick 4: choice 2 of 2. |
| 15 | L3 | found=6 expected=12 | Complete: 2, 4. That is arrangement #6. |
| 16 | L7 | picked=3 size=1 r=2 | Pick 3: choice 1 of 2. There were 4 options for the first slot, 3 for the second, and so on (multiplication principle). |
| 17 | L7 | picked=1 size=2 r=2 | Pick 1: choice 2 of 2. |
| 18 | L3 | found=7 expected=12 | Complete: 3, 1. That is arrangement #7. |
| 19 | L7 | picked=2 size=2 r=2 | Pick 2: choice 2 of 2. |
| 20 | L3 | found=8 expected=12 | Complete: 3, 2. That is arrangement #8. |
| 21 | L7 | picked=4 size=2 r=2 | Pick 4: choice 2 of 2. |
| 22 | L3 | found=9 expected=12 | Complete: 3, 4. That is arrangement #9. |
| 23 | L7 | picked=4 size=1 r=2 | Pick 4: choice 1 of 2. There were 4 options for the first slot, 3 for the second, and so on (multiplication principle). |
| 24 | L7 | picked=1 size=2 r=2 | Pick 1: choice 2 of 2. |
| 25 | L3 | found=10 expected=12 | Complete: 4, 1. That is arrangement #10. |
| 26 | L7 | picked=2 size=2 r=2 | Pick 2: choice 2 of 2. |
| 27 | L3 | found=11 expected=12 | Complete: 4, 2. That is arrangement #11. |
| 28 | L7 | picked=3 size=2 r=2 | Pick 3: choice 2 of 2. |
| 29 | L3 | found=12 expected=12 | Complete: 4, 3. That is arrangement #12. |
| 30 | L12 | found=12 formula=12 return=12 | 12 arrangements listed, matching 4! / 2! = 12. The formula gives the count without listing them: that is the point of counting principles. |
Practice ladder
Now try it yourself
Easy
- List all subsets of {a, b, c}
- How many cells are in a 4 × 7 grid?
Medium
- Is “divides” on positive integers reflexive, symmetric, transitive?
- Count the substrings and subsequences of a string of length 5
Advanced
- Show that a hash function from 32-bit strings of length 8 to 32-bit ints cannot be injective
Key takeaways
- Java sets and maps model mathematical sets and functions.
- Equivalence relations split a set into classes, the idea behind union-find.
- A product multiplies sizes, subsets give 2ⁿ, orderings give n!.