Senior 7 min · March 06, 2026

Permutations & Combinations — The r! Error in Interviews

336 vs 56: 3! factor error.

N
Naren · Founder
Plain-English first. Then code. Then the interview question.
About
 ● Production Incident 🔎 Debug Guide
Quick Answer
  • Permutations count ordered arrangements: P(n,r) = n! / (n-r)! — order changes the outcome, so Alice-then-Bob is different from Bob-then-Alice
  • Combinations count unordered selections: C(n,r) = n! / (r!(n-r)!) — order is irrelevant, so the set {Alice, Bob} is the same regardless of selection sequence
  • Diagnostic question before every problem: does swapping two chosen items produce a different valid outcome? Yes = permutation, no = combination
  • Never compute full factorials under pressure — P(8,3) = 8 × 7 × 6 = 336 by writing only the top r terms of n! and cancelling the rest
  • C(n,r) = C(n,n-r) — always compute from the smaller side; C(20,18) solved as C(20,2) = 190 takes three seconds
  • AND means multiply independent counts, OR means add mutually exclusive counts — this single rule resolves most compound counting problems without new formulas
  • Repetition-allowed problems are neither permutation nor combination — they use n^r for ordered selections and C(n+r-1, r) for unordered ones
Plain-English First

Imagine you have three friends — Alice, Bob, and Carol — and only two seats at your dinner table. Combinations ask 'which two friends do I invite?' — here, Alice+Bob is the same outcome as Bob+Alice because you are selecting a group, not assigning positions. Permutations ask 'who sits where?' — Alice in seat 1 and Bob in seat 2 is a completely different arrangement from Bob in seat 1 and Alice in seat 2, because the seats carry meaning. Combinations are about selection alone. Permutations are about selection plus arrangement. That single distinction — does the arrangement carry meaning? — is the only thing you need to unlock every problem in this category. Everything else is just applying the right formula.

Permutations and combinations show up in more places than most engineers realize — password-strength calculations, lottery odds, generating test data sets, cryptography key-space analysis, scheduling problems, and any time someone asks 'how many ways can these items be arranged or selected?' They are not just academic curiosities from a discrete math course. When an interviewer asks you to count arrangements or selections, they are probing whether you can model a problem mathematically before writing a single line of code — because brute-force enumeration scales catastrophically while a formula runs in O(1) and requires no additional memory.

The core problem both concepts solve is counting without actually listing. If you have 20 candidates for 3 roles and you start enumerating every possibility by hand, you will be there for hours. A formula gives you the exact count in seconds. More importantly, understanding the structure of the formula tells you whether order matters — and that reasoning is what separates a senior engineer's answer from a junior one. Interviewers at top companies are not testing whether you memorized the formula. They are testing whether you understand why the formula has the shape it does.

After working through this guide you will be able to identify whether a problem needs permutations or combinations before touching a formula, apply the correct formula confidently with a mental calculation shortcut that works on a whiteboard without a calculator, handle the edge cases that trip up prepared candidates under pressure, write code that generates the actual permutations or combinations when a problem requires the full list rather than just a count, and decompose compound counting problems into independent sub-problems using the AND-multiply and OR-add rules. These are the skills that move a counting answer from technically correct to genuinely impressive.

The Factorial Foundation — Why Everything Starts With n!

Before permutations or combinations make sense, you need to feel what a factorial means — not just know that n! is defined as n × (n-1) × ... × 1. Factorial n is the number of ways to arrange n distinct items in a sequence. Three books on a shelf: the first slot has 3 choices, the second has 2 remaining, the third has 1. Multiply them: 3 × 2 × 1 = 6. That is 3!.

The cascading multiplication is not arbitrary. Each step you commit one item to a position, which removes it from the pool for subsequent positions. That shrinking pool is exactly what 'without replacement' means — and it is the default assumption in virtually every interview problem unless the problem explicitly says 'repetition is allowed.' When you see a problem that does allow repetition, the formulas change: ordered selections with repetition become n^r, not n!/(n-r)!.

Two edge cases you must have in your reflexes. First: 0! = 1, not 0. There is exactly one way to arrange zero items: do nothing. This is not a convention someone invented for convenience — it is required for the formulas to produce correct results at their boundaries. C(n,0) = n!/(0! × n!) = 1, which must be true because there is exactly one way to choose nothing from a group. If 0! were 0, every combination formula would divide by zero. Second: 1! = 1, which is obvious but worth confirming mentally when you substitute numbers. These two base cases are responsible for the majority of interview edge case failures in this topic area.

io/thecodeforge/math/FactorialCalculator.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package io.thecodeforge.math;

import java.math.BigInteger;

public class FactorialCalculator {

    /**
     * Computes n! recursively for n up to 20 (fits within long).
     *
     * Base cases:
     *   0! = 1 — exactly one way to arrange nothing (do nothing)
     *   1! = 1 — trivially one arrangement
     *
     * Do NOT use this for n > 20long overflows silently at 21!.
     * For n > 20, use factorialBig() below.
     *
     * The negative-input guard is production hygiene — in an interview
     * context mention it verbally rather than adding a full exception.
     */
    public static long factorial(int n) {
        if (n < 0) throw new IllegalArgumentException("Factorial undefined for negative input: " + n);
        if (n == 0 || n == 1) return 1L;
        return (long) n * factorial(n - 1);
    }

    /**
     * BigInteger version for n > 20.
     * Iterative (no stack overflow risk for large n).
     * Slower than long arithmetic but correct for arbitrarily large n.
     */
    public static BigInteger factorialBig(int n) {
        if (n < 0) throw new IllegalArgumentException("Factorial undefined for negative input: " + n);
        BigInteger result = BigInteger.ONE;
        for (int i = 2; i <= n; i++) {
            result = result.multiply(BigInteger.valueOf(i));
        }
        return result;
    }

    public static void main(String[] args) {
        // Show growth rate — critical for understanding overflow risk
        System.out.println("Factorial growth and type boundaries:");
        for (int i = 0; i <= 12; i++) {
            System.out.printf("%2d! = %,d%n", i, factorial(i));
        }
        System.out.println();
        System.out.println("13! = " + factorialBig(13) + "  <- overflows int (max ~2.1B)");
        System.out.println("20! = " + factorialBig(20) + "  <- still fits long");
        System.out.println("21! = " + factorialBig(21) + "  <- overflows long silently");
        System.out.println("25! = " + factorialBig(25) + "  <- BigInteger required");
    }
}
Output
Factorial growth and type boundaries:
0! = 1
1! = 1
2! = 2
3! = 6
4! = 24
5! = 120
6! = 720
7! = 5,040
8! = 40,320
9! = 362,880
10! = 3,628,800
11! = 39,916,800
12! = 479,001,600
13! = 6227020800 <- overflows int (max ~2.1B)
20! = 2432902008176640000 <- still fits long
21! = 51090942171709440000 <- overflows long silently
25! = 15511210043330985984000000 <- BigInteger required
Silent Overflow Is the Invisible Wrong Answer — Interviewers Notice
Java's int overflows at 13! and wraps silently to a wrong value without throwing any exception. Java's long overflows at 21! with the same silent behavior. An interviewer who sees you write int factorial for a problem involving n > 12 will flag it as a scale-awareness gap — not just a coding mistake. Always declare your type intent out loud: 'I'll use long here because n stays below 20' or 'I'll switch to BigInteger because the constraint allows n up to 50.' Stating this shows you thought about bounds, not just the happy path.
Production Insight
Factorial growth is faster than exponential — 20! is already 2.4 × 10^18, which sits right at long's ceiling. The practical consequence for interview code is that you should never compute n! directly when n could be large. For combination calculations specifically, the incremental division approach avoids computing full factorials entirely by interleaving multiplication and division in a single loop, keeping intermediate values within long range for n up to approximately 62. This is a much better approach than computing numerator and denominator factorials separately and then dividing.
For the formula approach to counting (not generating), you almost never need a full factorial. P(8,3) = 8 × 7 × 6. The 5! in the denominator cancels exactly against the 5! at the bottom of 8!. Writing out only the r numerator terms is faster, less error-prone, and avoids overflow entirely for moderate n.
Key Takeaway
Factorial is the atomic unit of counting — every permutation and combination formula is built from factorial divisions.
0! = 1 is not optional and not a convention to memorize blindly. It is the mathematical definition that makes edge cases like C(n,0) = 1 work correctly. If you cannot explain why 0! = 1, you cannot explain why C(5,0) = 1, and that gap will show in an interview.
Type overflow is the silent killer in factorial code. Always state your type selection rationale out loud, and prefer the incremental division approach for combinations to avoid computing full factorials at all.
Numeric Type Selection for Factorial Computation
IfComputing n! directly where n is at most 12
Useint is safe — 12! = 479,001,600 fits within int max of 2,147,483,647
IfComputing n! directly where 13 ≤ n ≤ 20
Uselong required — 20! = 2,432,902,008,176,640,000 fits within long max of 9.2 × 10^18
IfComputing n! directly where n > 20
UseBigInteger required — no overflow possible, but arithmetic is slower; acceptable for interview problems
IfComputing C(n,r) for n up to approximately 62
UseUse incremental division loop with long — multiply by (n-i+1) and divide by i in the same iteration, keeping all intermediate values small without computing full factorials

Permutations — When Order Is Everything

A permutation answers: in how many ways can I select r items from n distinct items where the order of selection matters?

Formula: P(n,r) = n! / (n-r)!

The intuition behind the formula is worth understanding, not just memorizing. You have n choices for the first position. After filling the first position, n-1 remain for the second. After that, n-2 remain for the third. You continue until r positions are filled, leaving n-r items unused. Multiplying the choices for each position gives n × (n-1) × (n-2) × ... × (n-r+1). The division by (n-r)! in the formula is just a compact notation for canceling the lower portion of n! — the part you never actually use.

Real example: assigning gold, silver, and bronze medals to 3 of 8 athletes. The medal type matters — gold is not silver — so Alice winning gold and Bob winning silver is a different outcome from Bob winning gold and Alice winning silver. P(8,3) = 8!/5! = 8 × 7 × 6 = 336.

Notice the mental calculation: you do not compute 8! = 40,320 and 5! = 120 and then divide. You simply write the top 3 terms of 8! and stop. 8 × 7 = 56, then 56 × 6 = 336. That takes about five seconds. This cancellation shortcut is what makes permutation problems solvable at a whiteboard without a calculator, and demonstrating it fluently signals mathematical maturity to interviewers.

Two special cases worth knowing explicitly. When r = n, P(n,n) = n! — you are arranging all items, and this is the maximum possible count for any permutation problem. When r = 0, P(n,0) = 1 — there is exactly one way to make no selection at all. Both follow directly from the formula and both appear in interview edge case questions.

io/thecodeforge/math/PermutationCalculator.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package io.thecodeforge.math;

import java.util.*;

public class PermutationCalculator {

    /**
     * Counts P(n,r) using the top-r-terms cancellation approach.
     *
     * This avoids computing n! and (n-r)! separately (which would overflow
     * for moderate n) by multiplying only the r terms that survive cancellation.
     *
     * P(n,r) = n × (n-1) × ... × (n-r+1)
     *
     * The loop runs from n down to (n-r+1), which is exactly r iterations.
     * Overflow risk: for n=20, r=20, this computes 20! which fits in long.
     * For n > 20, switch to BigInteger.
     *
     * Time: O(r), Space: O(1).
     */
    public static long countPermutations(int n, int r) {
        if (r < 0 || r > n) return 0L;
        if (r == 0) return 1L;
        long result = 1L;
        // Multiply the top r terms of n!: n × (n-1) × ... × (n-r+1)
        for (int i = n; i > n - r; i--) {
            result *= i;
        }
        return result;
    }

    /**
     * Generates all P(n,r) permutations of items using backtracking.
     *
     * Key difference from combination generation:
     *   - No startIndex parameter — every item is considered at every level
     *   - used[] array tracks which items are currently in the selection
     *   - Backtrack by removing from current and resetting used[i] = false
     *
     * Why used[] instead of startIndex?
     * Permutations allow [A,B] and [B,A] as distinct outputs — both must
     * appear. If we used startIndex (the combination approach), we would
     * skip B when A is first, preventing [B,A] from being generated.
     *
     * Time: O(P(n,r) × r) — P(n,r) permutations, each of size r to copy.
     * Space: O(r) recursion depth + O(P(n,r) × r) for stored results.
     */
    public static void generate(
        List<String> items,
        int r,
        List<String> current,
        boolean[] used,
        List<List<String>> results
    ) {
        if (current.size() == r) {
            results.add(new ArrayList<>(current)); // Copy — current is mutated
            return;
        }
        for (int i = 0; i < items.size(); i++) {
            if (!used[i]) {
                used[i] = true;
                current.add(items.get(i));
                generate(items, r, current, used, results);
                // Backtrack: undo the choice made above
                current.remove(current.size() - 1);
                used[i] = false;
            }
        }
    }

    public static void main(String[] args) {
        // Count-only: the formula approach, no generation
        System.out.println("P(8,3) = " + countPermutations(8, 3));  // 336
        System.out.println("P(10,0) = " + countPermutations(10, 0)); // 1 (edge case)
        System.out.println("P(5,5) = " + countPermutations(5, 5));  // 120 = 5!

        // Generate all P(3,2) permutations of [A, B, C]
        List<String> items = List.of("A", "B", "C");
        List<List<String>> results = new ArrayList<>();
        generate(items, 2, new ArrayList<>(), new boolean[items.size()], results);
        System.out.println("\nP(3,2) = " + results.size() + " permutations:");
        results.forEach(System.out::println);
    }
}
Output
P(8,3) = 336
P(10,0) = 1
P(5,5) = 120
P(3,2) = 6 permutations:
[A, B]
[A, C]
[B, A]
[B, C]
[C, A]
[C, B]
Interview Gold: The Cancellation Shortcut That Demonstrates Fluency
Never compute full factorials when the interviewer expects a quick answer. P(n,r) = n × (n-1) × ... × (n-r+1) — just the top r terms. For P(8,3): say '8 times 7 is 56, times 6 is 336' out loud while writing it. This takes about five seconds, requires no calculator, and shows the interviewer you understand the formula's structure rather than mechanically applying division. Candidates who reach for a calculator or start computing 8! = 40,320 signal that they have memorized the formula without internalizing the cancellation. That distinction matters at senior levels.
Production Insight
Permutation generation uses backtracking with a used[] boolean array — not a startIndex parameter. The absence of startIndex is what allows [B, A] to be generated after [A, B], because when B is the first choice, A is still available for the second position. This is the correct approach for generating all ordered arrangements.
Time complexity for generation is O(P(n,r) × r) — there are P(n,r) results and each requires O(r) work to copy into the results list. For n = 10 and r = 10, this means 10! = 3,628,800 results. Generating the full list is almost always unnecessary if the problem only asks for a count — use the formula and stop there. Generating all permutations is only justified when the problem explicitly requires the list or when you need to apply additional filtering that cannot be expressed as a closed-form count.
Key Takeaway
Permutation = ordered selection. The formula P(n,r) = n × (n-1) × ... × (n-r+1) computes directly without full factorial division.
The cancellation shortcut — writing only the top r terms — is what makes permutation calculations work at a whiteboard in seconds. Practice it until it is automatic.
Generation uses backtracking with used[] (not startIndex). Never generate the full list when the problem only asks for a count — the count is O(r) computation; generation is O(P(n,r) × r), which explodes for moderate n.
Permutation Problem Recognition and Formula Application
IfProblem assigns distinct roles or positions — gold/silver/bronze, first/second/third, president/VP/secretary
UseUse P(n,r) — order matters because gold ≠ silver; the position carries meaning independent of who holds it
IfProblem asks to arrange all n items — anagrams, full orderings, complete sequences
UseUse P(n,n) = n! — the special case where every item fills exactly one position
IfItems have repeated elements — letters in a word like SUCCESS, identical objects in a sequence
UseUse multinomial formula: n! / (k1! × k2! × ... × km!) where each ki is the count of each distinct element; repeated elements produce identical arrangements that the formula divides away
IfProblem asks only for a count, not the full list of arrangements
UseUse the formula P(n,r) = n × (n-1) × ... × (n-r+1) — O(r) computation, zero memory overhead, never generate the actual permutations

Combinations — When Order Doesn't Matter

A combination answers: in how many ways can I select r items from n distinct items where the order of selection is irrelevant?

Formula: C(n,r) = n! / (r! × (n-r)!)

The r! in the denominator is the only structural difference from the permutation formula — and it is the entire conceptual point. Every group of r selected items can be internally arranged in r! different sequences. Since we do not care about internal order, all r! of those sequences represent the same outcome and should be counted only once. Dividing by r! removes those duplicates. This is the precise mathematical meaning of 'order does not matter.'

Real example: a development team of 3 chosen from 8 candidates. You are building a group, not assigning roles, so a team of {Alice, Bob, Carol} is the same outcome regardless of the order in which you selected them. C(8,3) = (8 × 7 × 6) / (3 × 2 × 1) = 336 / 6 = 56.

Two properties worth having in your reflexes. Symmetry: C(n,r) = C(n,n-r). Choosing 3 items from 8 is identical in count to leaving 5 items unchosen — the items you pick and the items you reject are mirror images of each other. This means C(20,18) should never be computed as choosing 18 from 20. It should immediately collapse to C(20,2) = (20 × 19) / (2 × 1) = 190. The symmetry property is a free performance win and a demonstration of mathematical fluency.

Boundary values: C(n,0) = 1 (one way to choose nothing) and C(n,n) = 1 (one way to choose everything). These follow directly from the formula and are worth verifying once mentally so the edge case handling in your code is not a surprise.

io/thecodeforge/math/CombinationCalculator.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
package io.thecodeforge.math;

import java.util.*;

public class CombinationCalculator {

    /**
     * Counts C(n,r) using incremental division to prevent overflow.
     *
     * The key insight: instead of computing numerator = n×(n-1)×...×(n-r+1)
     * and denominator = r! separately and dividing at the end (which overflows
     * for moderate n), we interleave the division inside the loop.
     *
     * At step i:
     *   - Multiply by (n - i + 1)  [next numerator term]
     *   - Divide by i               [divide by next denominator term]
     *
     * This works because C(n,r) is always an integer — the intermediate
     * values remain exact integers throughout the loop.
     *
     * Symmetry optimization: C(n,r) = C(n,n-r).
     * If r > n/2, we flip to n-r which has fewer iterations.
     * C(20,18) becomes C(20,2) — 2 iterations instead of 18.
     *
     * Safe for long up to approximately n = 62 with incremental division.
     * For n > 62, use BigInteger variant.
     *
     * Time: O(min(r, n-r)), Space: O(1).
     */
    public static long countCombinations(int n, int r) {
        if (r < 0 || r > n) return 0L;
        if (r == 0 || r == n) return 1L; // C(n,0) = C(n,n) = 1
        if (r > n / 2) r = n - r;         // Symmetry: always use smaller r
        long result = 1L;
        for (int i = 1; i <= r; i++) {
            result *= (n - i + 1); // Next numerator term
            result /= i;           // Divide by next denominator term
            // Division is exact at every step because C(n,r) is always integer
        }
        return result;
    }

    /**
     * Generates all C(n,r) combinations using backtracking with startIndex.
     *
     * The startIndex parameter is the key difference from permutation generation:
     *   - We only consider items at index >= startIndex
     *   - This ensures items are selected in strictly increasing index order
     *   - [A,B] appears exactly once; [B,A] is never generated
     *
     * Why does this prevent duplicates?
     * By enforcing forward-only selection, each unique subset can only be
     * constructed in one order — the one where lower-index items appear first.
     * All other orderings of the same subset are structurally unreachable.
     *
     * Contrast with permutation generation which uses used[] and no startIndex,
     * allowing [A,B] and [B,A] both to appear.
     *
     * Time: O(C(n,r) × r), Space: O(r) stack depth + O(C(n,r) × r) results.
     */
    public static void generate(
        List<String> items,
        int r,
        int startIndex,          // Only consider items at or after this position
        List<String> current,
        List<List<String>> results
    ) {
        if (current.size() == r) {
            results.add(new ArrayList<>(current));
            return;
        }
        for (int i = startIndex; i < items.size(); i++) {
            current.add(items.get(i));
            generate(items, r, i + 1, current, results); // i+1: never revisit earlier items
            current.remove(current.size() - 1);           // Backtrack
        }
    }

    public static void main(String[] args) {
        // Count-only: formula results
        System.out.println("C(8,3) = " + countCombinations(8, 3));   // 56
        System.out.println("C(52,5) = " + countCombinations(52, 5)); // 2,598,960 (poker hands)
        System.out.println("C(20,18) = " + countCombinations(20, 18)); // 190 via symmetry as C(20,2)
        System.out.println("C(10,0) = " + countCombinations(10, 0));  // 1 (edge case)
        System.out.println("C(10,10) = " + countCombinations(10, 10)); // 1 (edge case)

        // Generate all C(4,2) combinations of [A,B,C,D]
        List<String> items = List.of("A", "B", "C", "D");
        List<List<String>> results = new ArrayList<>();
        generate(items, 2, 0, new ArrayList<>(), results);
        System.out.println("\nC(4,2) = " + results.size() + " combinations:");
        results.forEach(System.out::println);
    }
}
Output
C(8,3) = 56
C(52,5) = 2598960
C(20,18) = 190
C(10,0) = 1
C(10,10) = 1
C(4,2) = 6 combinations:
[A, B]
[A, C]
[A, D]
[B, C]
[B, D]
[C, D]
Pro Tip: Incremental Division Is the Correct Implementation Pattern for Combinations
The naive implementation — compute numerator as n×(n-1)×...×(n-r+1), compute denominator as r!, divide — overflows for moderate n because the intermediate numerator grows much faster than long's capacity before division brings it back down. The incremental approach (multiply by (n-i+1), then immediately divide by i in the same loop iteration) keeps intermediate values small because the partial result is always a valid C(n,k) for some k. This is correct and exact because C(n,r) is always a positive integer — the division never produces a remainder at any intermediate step. For n up to about 62, long with incremental division is sufficient. For larger n, switch to BigInteger. In an interview, demonstrating this incremental pattern signals awareness of the overflow issue — a clear differentiator.
Production Insight
Combination generation differs from permutation generation in exactly one structural way: the startIndex parameter. The startIndex enforces a monotonically increasing index selection — once you have selected the item at index i, you never look back at indices 0 through i-1. This is what makes [A,B] and [B,A] structurally the same combination in the generation algorithm: [B,A] is unreachable because once B (at index 1) is selected, A (at index 0) is below the startIndex and cannot be considered.
The symmetry property C(n,r) = C(n,n-r) is not just a mathematical curiosity. It halves computation time for large r values and is the kind of optimization that interviewers notice as a signal of mathematical intuition. C(20,18) should collapse to C(20,2) = 190 in your head within two seconds. If you start computing 20 × 19 × 18 × ... × 3 for C(20,18), you have missed the symmetry and the interviewer has noticed.
Key Takeaway
Combination = unordered selection. C(n,r) = P(n,r) / r! — the r! denominator removes the internal ordering duplicates that permutation counts as distinct.
The symmetry C(n,r) = C(n,n-r) is both a performance optimization and a signal of mathematical fluency. Apply it reflexively whenever r > n/2.
Incremental division in the loop is the correct implementation — not computing numerator and denominator separately. It prevents overflow, keeps intermediate values small, and produces exact integer results at every step.
Combination Formula Application
IfProblem involves forming an unordered group — team, committee, subset, hand of cards
UseUse C(n,r) — swapping any two members produces the same group, confirming order does not matter
IfProblem has r close to n — choosing most items from the pool
UseApply symmetry immediately: C(n,r) = C(n,n-r) — compute from the smaller side to minimize iterations
IfProblem requires the actual list of combinations, not just the count
UseUse backtracking with startIndex — never look back at earlier indices. Time O(C(n,r) × r)
IfProblem involves multiple independent group selections (AND conditions)
UseCompute C(n1,r1) × C(n2,r2) × ... — independent choices multiply. Each group selection is computed separately then combined

Cracking the Interview Problem — Identify First, Formula Second

The most common interview failure in counting problems is not getting the formula wrong — it is applying the wrong formula because the order-matter diagnostic never happened. The formula you choose is a downstream consequence of the problem structure you identify. Train yourself to ask one question before touching any formula: does swapping the order of two selected items produce a different valid outcome?

If yes, it is a permutation. If no, it is a combination. Write this on the whiteboard. Say it out loud. Not because the interviewer needs you to explain basic reasoning, but because articulating the check makes it impossible to skip — and skipping it is exactly how candidates end up with answers that are 6x too large or 6x too small.

Let's work through a layered problem that interviewers use because it tests decomposition, not just formula recall: 'A company has 5 backend engineers and 4 frontend engineers. A project team must include exactly 2 backend and 2 frontend engineers. How many unique teams can be formed?'

Step 1 — Does order matter? No. A team is an unordered group. Swapping Alice and Bob within the backend selection produces the same team. Combination.

Step 2 — How many independent choices are there? Two: the backend selection AND the frontend selection. They are independent because the backend pool and frontend pool are separate — choosing from one does not affect the other.

Step 3 — Independent choices multiply. C(5,2) × C(4,2) = 10 × 6 = 60.

The AND-multiply rule is the key tool for compound problems. When choices are independent, the total count is the product of individual counts. When scenarios are mutually exclusive — either scenario A happens OR scenario B happens, never both — the total count is the sum. This AND-multiply / OR-add distinction resolves about 80% of compound counting problems without requiring any new conceptual machinery.

A harder variant: 'At least one of the two backend engineers chosen must be the team lead.' This adds a constraint. The most reliable approach for 'at least one' constraints is complementary counting — total teams minus teams with no team lead. Total: C(5,2) × C(4,2) = 60. Teams with no team lead (choosing 2 from the 4 non-lead backend engineers): C(4,2) × C(4,2) = 6 × 6 = 36. Answer: 60 - 36 = 24.

Complementary counting often produces simpler arithmetic than direct enumeration of the constrained cases. Recognize it as a pattern: whenever a problem says 'at least one' or 'at least two,' consider whether counting the complement (the cases where the condition fails) is easier.

io/thecodeforge/math/TeamFormationSolver.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package io.thecodeforge.math;

public class TeamFormationSolver {

    /**
     * Solves the compound team formation problem:
     *   - 5 backend engineers, choose exactly 2
     *   - 4 frontend engineers, choose exactly 2
     *   - Order does not matter in either group (combination, not permutation)
     *   - Choices are independent (AND condition → multiply)
     *
     * Demonstrates: decompose first, classify each sub-problem, combine results.
     */
    public static long uniqueTeams(
        int backendPool, int backendNeeded,
        int frontendPool, int frontendNeeded
    ) {
        long backendWays = CombinationCalculator.countCombinations(backendPool, backendNeeded);
        long frontendWays = CombinationCalculator.countCombinations(frontendPool, frontendNeeded);
        // AND condition between independent groups: multiply individual counts
        return backendWays * frontendWays;
    }

    /**
     * Variant with constraint: at least one backend engineer must be the team lead.
     *
     * Direct approach: count teams WITH team lead (fix team lead, choose 1 more from 4)
     * Complementary approach: total teams - teams WITHOUT team lead
     *
     * Both approaches give the same answer. Complementary is often simpler
     * arithmetic for 'at least one' problems — recognize this pattern.
     */
    public static long teamsWithTeamLead(
        int backendPool, int backendNeeded,
        int frontendPool, int frontendNeeded
    ) {
        long totalTeams = uniqueTeams(backendPool, backendNeeded, frontendPool, frontendNeeded);
        // Teams where team lead is NOT selected: choose backendNeeded from (backendPool - 1)
        long teamsWithoutLead = CombinationCalculator.countCombinations(backendPool - 1, backendNeeded)
                               * CombinationCalculator.countCombinations(frontendPool, frontendNeeded);
        // Complementary counting: at least one team lead = total - none
        return totalTeams - teamsWithoutLead;
    }

    public static void main(String[] args) {
        long total = uniqueTeams(5, 2, 4, 2);
        System.out.println("Total unique teams (no constraint): " + total);
        // C(5,2) = 10, C(4,2) = 6, total = 10 x 6 = 60

        long withLead = teamsWithTeamLead(5, 2, 4, 2);
        System.out.println("Teams with at least one team lead: " + withLead);
        // Total 60 - teams without lead C(4,2)*C(4,2) = 6*6 = 36 -> 60 - 36 = 24

        // Additional example: exclusive scenario counting (OR)
        // How many teams have EITHER all backend OR all frontend engineers (4-person team)
        long allBackend = CombinationCalculator.countCombinations(5, 4);
        long allFrontend = CombinationCalculator.countCombinations(4, 4);
        // OR between mutually exclusive scenarios: add individual counts
        System.out.println("4-person teams that are entirely one discipline (OR): "
            + (allBackend + allFrontend));
        // C(5,4)=5, C(4,4)=1, total = 5 + 1 = 6
    }
}
Output
Total unique teams (no constraint): 60
Teams with at least one team lead: 24
4-person teams that are entirely one discipline (OR): 6
Interview Gold: AND Means Multiply, OR Means Add — Make This a Reflex
When a problem requires choosing from group A AND group B independently, multiply the individual counts. When a problem counts scenario A OR scenario B (where both cannot simultaneously occur), add the individual counts. Internalizing this as a reflex resolves about 80% of compound counting problems without requiring new formulas. The complement rule for 'at least one' problems — compute total minus none — is the most useful application: whenever direct counting of the constrained case is complex, count the complement and subtract. It is almost always simpler.
Production Insight
Compound counting problems decompose into independent sub-problems, and the decomposition step is where most candidates lose marks — not in the final formula application. The decomposition tells you how many independent counting operations to perform and whether to combine them with multiplication (AND, independent) or addition (OR, exclusive).
Misidentifying AND versus OR gives answers off by orders of magnitude — the kind of error that is obviously wrong to the interviewer even before checking the arithmetic. Write the decomposition explicitly on the whiteboard: 'backend choice AND frontend choice → multiply' before computing either count. This makes the logical structure visible and gives the interviewer something to engage with even if your arithmetic has a small error.
Key Takeaway
The diagnostic question — does swapping produce a different outcome? — is the entire skill being tested. The formula is just the consequence of answering it correctly.
Decompose compound problems into independent sub-problems. Classify each sub-problem as permutation or combination. Combine with AND-multiply for independent choices and OR-add for exclusive scenarios.
Complementary counting is your best tool for 'at least one' constraints — total minus none is almost always easier to compute than directly enumerating the constrained cases.
Compound Counting Problem Decomposition
IfProblem counts selections from a single pool — no multiple groups or scenarios
UseApply P(n,r) or C(n,r) directly — no decomposition needed; decide order-matters first
IfProblem requires selecting from multiple independent groups simultaneously (AND)
UseCompute the count for each group independently, then multiply all results together
IfProblem counts across mutually exclusive scenarios — either A or B, never both (OR)
UseCompute each scenario's count independently, then add all results together
IfProblem has 'at least one' or 'at least k' constraint
UseUse complementary counting: total (unconstrained) minus count of outcomes where the condition fails entirely; almost always simpler arithmetic than direct enumeration of constrained cases
● Production incidentPOST-MORTEMseverity: high

Wrong Formula Selection Costs Candidate a Senior Engineer Offer

Symptom
The candidate was asked: 'How many unique teams of 3 can be formed from 8 candidates?' The candidate answered 336. The correct answer was 56. The discrepancy was exactly 3! = 6, a textbook indicator of using permutation instead of combination. The interviewer noted the error in the debrief as 'applied formula mechanically without checking whether order mattered to the problem.'
Assumption
The candidate had memorized P(n,r) = n!/(n-r)! and recognized a pattern — choose r from n — without pausing to ask what kind of outcome the problem was actually counting. The formula pattern-matched to the problem surface before the reasoning happened.
Root cause
The candidate skipped the diagnostic step entirely. The question 'does swapping two chosen team members produce a different valid team?' was never asked. Teams are unordered groups — swapping Alice and Bob produces the same team composition. This means the problem is a combination, and the denominator must include r! to divide away the internal ordering duplicates that permutation counts as distinct. The formula P(8,3) = 336 counts ordered arrangements — gold, silver, bronze medalist assignments. The problem was asking for something fundamentally different.
Fix
Always apply the order-matter diagnostic before selecting a formula. Write it on the whiteboard, out loud, before writing any formula. For team, committee, group, and subset problems: order does not matter, use C(n,r). For role, rank, position, and sequence problems: order matters, use P(n,r). Under interview pressure, the whiteboard articulation of this check is itself a signal to the interviewer that you understand the structure of the problem rather than pattern-matching to a formula.
Key lesson
  • The formula is never the hard part — identifying whether order matters is the actual skill being tested. An interviewer who watches you apply the wrong formula correctly has learned that you cannot reason about problem structure under pressure.
  • If your answer is exactly r! times larger than the expected answer, you used permutation instead of combination. If it is exactly r! times smaller, you used combination instead of permutation. The ratio is always a clean factorial — use this as your self-check.
  • Write the diagnostic question on the whiteboard first: 'Does swapping two chosen items produce a different valid outcome?' This is not a formality. It is the actual mathematical reasoning the formula encodes, and making it explicit shows interviewers that you understand counting, not just computation.
  • Interviewers at strong companies watch your reasoning process more carefully than your arithmetic. A candidate who writes the right formula for the wrong reason will eventually get the wrong answer on a harder variant. A candidate who demonstrates correct reasoning and makes an arithmetic error is far more hirable.
Production debug guideDiagnostic steps when your permutation or combination answer does not match the expected result — ordered from most likely to least likely cause4 entries
Symptom · 01
Your answer is exactly r! times larger than the expected answer
Fix
You used P(n,r) instead of C(n,r). The problem is asking for unordered selections — groups, teams, or committees where internal order does not distinguish outcomes. Divide your answer by r!, or recompute from scratch using C(n,r) = n! / (r! × (n-r)!). To confirm before recomputing: ask whether swapping any two selected items produces a different valid outcome. If no, it is always C(n,r).
Symptom · 02
Your answer is exactly r! times smaller than the expected answer
Fix
You used C(n,r) instead of P(n,r). The problem involves ordered arrangements — assigning roles, filling ranked positions, or constructing sequences where the order of selected items produces distinct outcomes. Multiply your combination count by r!, or recompute using P(n,r) = n! / (n-r)!.
Symptom · 03
Your answer is negative, wraps to a large positive number, or is wildly wrong for moderate n
Fix
Integer overflow. Java int silently overflows at 13! (1,932,053,504 for 13! exceeds int max of 2,147,483,647). Java long silently overflows at 21!. No exception is thrown — the value wraps and produces a wrong answer with no warning. Switch to long for n up to 20, BigInteger for n above 20, or use the incremental division loop for combinations which keeps intermediate values within long range for n up to approximately 62.
Symptom · 04
Edge cases return 0, throw ArithmeticException, or produce wrong values for r = 0 or r = n
Fix
Missing 0! = 1 base case handling. When r = 0, the formula requires computing 0! in the denominator. If your factorial function returns 0 for 0, the denominator is 0 and division fails. Add an explicit base case before any loop or recursion: if (r == 0 || r == n) return 1. This handles C(n,0) = 1 and C(n,n) = 1 correctly without reaching the factorial computation.
Permutation vs Combination — Complete Reference
AspectPermutation P(n,r)Combination C(n,r)
Does order matter?Yes — [A,B] ≠ [B,A]; the sequence itself carries meaningNo — {A,B} = {B,A}; the set membership is the entire outcome
Formulan! / (n−r)! = n × (n-1) × ... × (n-r+1)n! / (r! × (n−r)!) = P(n,r) / r!
Relation between P and CP(n,r) is always ≥ C(n,r) for the same n and rC(n,r) = P(n,r) / r! — the r! removes internal ordering duplicates
Problem keyword signals'arrange', 'rank', 'order', 'assign roles', 'schedule', 'PIN', 'password', 'first/second/third place''choose', 'select', 'group', 'team', 'committee', 'subset', 'hand', 'combination lock (misleadingly named)'
Symmetry propertyNo flip shortcut — P(n,r) ≠ P(n,n-r) in general; P(n,n) = n! is the only special caseC(n,r) = C(n,n-r) — always flip to use the smaller r side for fewer computation steps
When r = nP(n,n) = n! — all items arranged in every possible orderC(n,n) = 1 — only one way to choose everything
When r = 0P(n,0) = 1 — one way to make no ordered selectionC(n,0) = 1 — one way to choose nothing
PIN and password problemsAlways permutation — digit order defines the code; 1234 and 4321 unlock different thingsNever combination for ordered codes — 'combination lock' is a misnomer that trips up candidates
Backtracking generator distinguisherNo startIndex — use used[] boolean array; every item eligible at every recursion levelUse startIndex — only consider items at indices ≥ startIndex; never revisit earlier items
Overflow risk in codeHigh — multiplying r consecutive terms starting from n grows quickly; use long for n ≤ 20Lower — incremental division keeps intermediates small; safe with long for n ≤ ~62
Repetition-allowed variantn^r — each of r positions has n independent choices; not the standard permutation formulaC(n+r-1, r) — stars and bars formula for unordered selection with replacement

Key takeaways

1
The one diagnostic question that gates every formula decision
'does swapping two chosen items create a different valid outcome?' Yes means permutation, no means combination. Write this on the whiteboard before writing any formula — it is the actual skill being tested, not the arithmetic.
2
P(n,r) mental calculation
write only the top r terms of n! and stop. P(8,3) = 8 × 7 × 6 = 336. Never compute the full factorial under exam pressure. This cancellation shortcut is what makes permutation problems solvable in seconds without a calculator.
3
C(n,r) = C(n,n-r)
apply the symmetry reflex before computing anything. C(20,18) is C(20,2) = 190 in three seconds. C(52,47) is C(52,5) = 2,598,960. Always compute from the smaller side. Using incremental division in the loop keeps intermediates within long range for n up to ~62.
4
AND means multiply, OR means add. Decompose compound problems into independent sub-problems, classify each as permutation or combination, then combine with multiplication for AND conditions and addition for OR conditions. Complementary counting
total minus complement — is your best tool for 'at least one' constraints.

Common mistakes to avoid

5 patterns
×

Using permutation when the problem is a combination — the most common interview error

Symptom
Your answer is exactly r! times too large — you get 336 instead of 56 for a team-of-3 problem, or 60 instead of 10 for a committee-of-2 problem. The ratio between your answer and the correct answer is always a clean factorial, which is the clearest possible diagnostic signal.
Fix
Ask 'does swapping two chosen items produce a different valid outcome?' before writing any formula. Write the question on the whiteboard. If no, use C(n,r). When you have already computed an answer, divide by r! and check whether the result makes intuitive sense — 56 teams from 8 people is plausible; 336 teams is implausibly large for 8 people.
×

Forgetting that 0! = 1 — produces wrong results or exceptions at edge cases

Symptom
Formula breaks for C(n,n) or C(n,0), either returning a division-by-zero exception or returning 0 when 1 is correct. Code that computes factorial in a loop from n down to 2 returns 0 for input 0 because the loop body never executes.
Fix
Add explicit base cases before any loop: if (r == 0 || r == n) return 1L. This handles C(n,0) and C(n,n) correctly without the formula ever executing, which is both correct and avoids any potential division or multiplication by 0. Never let the general formula handle these boundary inputs.
×

Integer overflow when computing factorials or large combinations with the wrong numeric type

Symptom
C(52,5) returns a negative number (wraps to -288,975,480 with int) or a plausible but wrong number with no exception thrown. The code appears to execute correctly. This is the most dangerous type of mistake because it produces confident wrong answers.
Fix
Use long for n up to 20, BigInteger for n above 20. For combination computation specifically, use the incremental division loop — multiply by (n-i+1) then divide by i within the same iteration — which keeps intermediate values within long range for n up to approximately 62 without requiring BigInteger. State your type choice explicitly in interviews: 'I'll use long here because n stays below 20 per the constraint.'
×

Confusing AND-multiply with OR-add in compound counting problems

Symptom
Answer is off by orders of magnitude. For a problem requiring 2 backend and 2 frontend engineers, adding C(5,2) + C(4,2) = 10 + 6 = 16 instead of multiplying to get 60. Or multiplying mutually exclusive scenarios instead of adding, producing a count far larger than the total possible outcomes.
Fix
Map the problem structure explicitly before computing. Independent groups selected simultaneously → AND → multiply. Mutually exclusive scenarios where only one can occur → OR → add. Write 'AND = ×, OR = +' on the whiteboard at the start of any compound problem. The structural analysis takes 30 seconds and prevents a class of errors that is otherwise invisible until you check against known answers.
×

Applying standard permutation or combination formulas to problems with repetition allowed

Symptom
Problem says 'digits can repeat' or 'repetition is allowed,' but you apply P(n,r) = n!/(n-r)! anyway, getting 5,040 instead of 10,000 for a 4-digit PIN from digits 0-9. The repetition constraint fundamentally changes the formula.
Fix
Repetition-allowed ordered selection (PIN where digits can repeat): n^r, not P(n,r). Each of the r positions has n fully independent choices. Repetition-allowed unordered selection (choosing r items from n types where duplicates are allowed): C(n+r-1, r) — the stars-and-bars formula. Check explicitly for the repetition condition before selecting any formula.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01SENIOR
LeetCode 77: Given two integers n and k, return all possible combination...
Q02SENIOR
LeetCode 46: Generate all permutations of distinct integers. How does th...
Q03JUNIOR
A lock has a 4-digit code using digits 0-9. How many codes are possible ...
Q04SENIOR
How many ways can a committee of 3 be formed from 7 people if one specif...
Q05SENIOR
Derive the count of unique arrangements of the word SUCCESS. What is the...
Q01 of 05SENIOR

LeetCode 77: Given two integers n and k, return all possible combinations of k numbers chosen from the range [1, n]. Implement this using backtracking and explain why the generator uses startIndex instead of a used[] array.

ANSWER
Use backtracking with a startIndex parameter that starts at 1 and increases with each recursive call. At each level, iterate from startIndex to n, add the current number to the current combination, recurse with startIndex = i + 1, then remove the last element to backtrack. The startIndex is the critical structural element. It enforces a monotonically increasing selection order — once we pass index i, we never look back at numbers before i. This means [1,2] appears exactly once and [2,1] is structurally unreachable, which is exactly what we want for combinations where the order of selection is irrelevant. Contrast with permutation generation (LeetCode 46) which uses a used[] boolean array and no startIndex. In permutations, [1,2] and [2,1] are distinct outputs because the order of selection defines a different outcome. Using used[] and iterating from index 0 at every level ensures both orderings are generated. Time complexity: O(C(n,k) × k) — C(n,k) combinations, each requires O(k) work to copy into results. Space: O(k) for the recursion stack depth plus O(C(n,k) × k) for stored results. For counting only, use the formula C(n,k) with incremental division: no generation needed, O(min(k, n-k)) time, O(1) space.
FAQ · 4 QUESTIONS

Frequently Asked Questions

01
What is the difference between permutation and combination in simple terms?
02
How do I know whether to use permutation or combination in a word problem?
03
Why does 0! equal 1 instead of 0?
04
What changes when repetition is allowed — why don't the standard formulas apply?
🔥

That's Aptitude. Mark it forged?

7 min read · try the examples if you haven't

Previous
Syllogism Problems
11 / 14 · Aptitude
Next
Simple and Compound Interest