Open this lesson in your favourite AI. It'll walk you through the why, explain the demo, and quiz you on the try-it list.
Most discrete probability problems reduce to counting. 'How many ways can event happen, divided by how many ways anything can happen' is the entire engine when outcomes are equally likely. The hard part is not the formulas — it is choosing the right sample space and avoiding double-counting. Builders ship probability bugs constantly because they multiply when they should choose, or order matters when they assumed it did not. Spending an hour on permutations vs combinations now prevents a class of off-by- errors forever.
A permutation is an ordered arrangement; a combination is an unordered selection. The permutation count is the combination count multiplied by — exactly the number of orderings of chosen items.
Use these three in order. Each builds on the one before.
In one paragraph, explain the difference between a permutation and a combination using the example of choosing 3 students from a class of 10 — once for a podium ranking, once for a committee. State both counts numerically.
Walk me through a derivation of $\binom{n}{k} = n! / (k!(n-k)!)$ starting from 'count the ordered selections, then divide by the redundant orderings.' Where does the $k!$ come from, exactly?
Count the number of distinct anagrams of MISSISSIPPI. Generalise to a multinomial coefficient $\binom{n}{n_1, n_2, \ldots, n_r}$ for an alphabet with letter multiplicities, and explain why this is the natural generalisation of the binomial coefficient.
math.comb and via Stirling's approximation . Compare the relative error.// main.go
package main
import "fmt"
func factorial(n int) int {
result := 1
for i := 2; i <= n; i++ {
result *= i
}
return result
}
// comb returns the number of ways to choose k items from n (order doesn't matter)
func comb(n, k int) int {
if k > n {
return 0
}
return factorial(n) / (factorial(k) * factorial(n-k))
}
// perm returns the number of ordered arrangements of k items from n
func perm(n, k int) int {
if k > n {
return 0
}
return factorial(n) / factorial(n-k)
}
func main() {
// 5-card poker hands from a 52-card deck
fmt.Println("hands (combinations) =", comb(52, 5)) // 2,598,960
fmt.Println("hands (permutations) =", perm(52, 5)) // 311,875,200
fmt.Println("ratio = 5! ?", perm(52, 5)/comb(52, 5) == factorial(5))
// Probability of a 4-of-a-kind
// Pick the rank for the quad (13), the kicker rank (12) and its suit (4)
fourOfAKind := 13 * 12 * 4
fmt.Printf("P(4 of a kind) = %f\n", float64(fourOfAKind)/float64(comb(52, 5))) // ~ 0.00024
// Tiny enumeration sanity check on a 5-card mini-deck
deck := []int{0, 1, 2, 3, 4}
count := 0
for i := 0; i < len(deck)-2; i++ {
for j := i + 1; j < len(deck)-1; j++ {
for k := j + 1; k < len(deck); k++ {
_ = []int{deck[i], deck[j], deck[k]}
count++
}
}
}
fmt.Println("C(5,3) =", count)
}go run main.go