3.7.1 · D4Algorithm Paradigms

Exercises — Brute force — exhaustive search, when acceptable

2,301 words10 min readBack to topic

Before we start, here is the mental picture we keep reusing — the whole game is counting how big the pile of candidates is, then asking if the pile is small enough to walk through.

Figure — Brute force — exhaustive search, when acceptable

Level 1 — Recognition

Goal: read a constraint and name the search-space size + verdict. No coding.

Exercise 1.1

A problem says: "given items, consider every subset." How many subsets are there, and will brute force fit under ?

Recall Solution 1.1

WHAT we count: every subset. WHY : each of the items is independently in or out — that's choices, made times, so . With : . Since , brute force is fine (even multiplied by a small per-subset cost).

Exercise 1.2

A problem has and asks you to try every ordering (permutation) of the items. Size? Verdict?

Recall Solution 1.2

WHY : the first slot has choices, the next (one item used up), and so on down to . Multiply: . . That is above . So bare permutation brute force is borderline-to-risky — likely too slow unless verification is trivial and constants are tiny. Prefer pruning (backtracking) or DP here.

Exercise 1.3

"Find any two indices with ; ." Which enumeration, what size, verdict?

Recall Solution 1.3

WHAT: enumerate all unordered distinct pairs. WHY : pick slot 1 ( ways) and slot 2 ( ways), then divide by because and are the same pair: . : . Brute force OK.


Level 2 — Application

Goal: write the enumeration loop / mask and count its cost.

Exercise 2.1

Write brute force that returns all index pairs , , with . State the exact number of pair-checks for general .

Recall Solution 2.1
def all_pairs(a, t):
    n = len(a)
    out = []
    for i in range(n):              # first index
        for j in range(i+1, n):     # second index, strictly after i
            if a[i] + a[j] == t:
                out.append((i, j))
    return out

WHY start j at i+1: it enforces , so every unordered pair is visited exactly once — no self-pairs, no duplicates. Number of checks: the inner loop runs times. This is the we derived. For that is exactly checks.

Exercise 2.2

Using bitmasks, count how many subsets of have sum . Show the masks.

Recall Solution 2.2

WHY masks: a number mask from to encodes a subset — bit set element is chosen. This realises the count. Subsets summing to :

  • → mask , sum
  • → mask , sum
  • → mask , sum
  • → mask , sum

That's 4 subsets. (Check: no other combination of totals .)

Exercise 2.3

A length-3 password uses lowercase letters . How many candidates must brute force try in the worst case? Is that instant?

Recall Solution 2.3

WHY : each of the positions is filled independently from an alphabet of size , so . . That's way under — effectively instant. (This is exactly why 3-character passwords are worthless.)


Level 3 — Analysis

Goal: separate enumeration cost from verify cost, and reason about totals.

Exercise 3.1

A brute force enumerates all subsets, and for each subset spends time scanning the bits to compute its weight. Give the total cost and the largest that stays under .

Recall Solution 3.1

Total cost . We want .

  • :
  • :
  • : ✓ (just under)
  • :

So the largest safe is .

Exercise 3.2

Two solutions to the same problem: (A) brute force with the given bound ; (B) a "clever" algorithm run on a different problem where . Which does fewer operations?

Recall Solution 3.2
  • (A): .
  • (B): . (A) does ~ fewer operations. Moral: "exponential" is not automatically slower than "polynomial" — the input bound decides. A tiny tames the exponential.

Exercise 3.3

A subset-sum brute force updates its running best like this:

best = max(best, val)   # <-- placed BEFORE the weight check
if wt <= W:
    pass

What is wrong, and what does it return in the worst case?

Recall Solution 3.3

Bug: it scores the subset (max(best, val)) before confirming the subset is feasible (wt <= W). So it can adopt the value of an overweight, illegal subset. Worst case: the heaviest, highest-value subset — which violates — wins, returning a value that no legal packing can achieve. The answer is an over-estimate. Fix: verify the constraint first, then score:

if wt <= W:
    best = max(best, val)

Level 4 — Synthesis

Goal: combine enumeration + pruning + a feasibility decision into a full plan.

Exercise 4.1

Knapsack-style: weights=[2,3,4,5], values=[3,4,5,6], cap W=5. By full subset enumeration, find the max value with total weight . Show the reasoning.

Recall Solution 4.1

Enumerate all subsets; keep only those with weight ; take the best value. Feasible subsets and their (weight, value):

  • → (0, 0)
  • → (2, 3)
  • → (3, 4)
  • → (4, 5)
  • → (5, 6)
  • → (5, 7) ← weight exactly 5, value 7
  • → (6, —) infeasible
  • all larger subsets exceed 5.

Best feasible value = 7, from taking items and (weight , value ).

Exercise 4.2

You must solve the same knapsack but with items. Pure brute force needs operations. Estimate it, decide whether it's acceptable, and name the right paradigm.

Recall Solution 4.2

, times operations. That is over the budget — hopeless by brute force. Structure exists (overlapping subproblems / optimal substructure), so use DP: knapsack in . With a moderate this is trivial. (If but no small , meet-in-the-middle via Bitmasking splits into two halves of each.)

Exercise 4.3

Design a brute force that finds the shortest route visiting all cities once (Travelling Salesman). What do you enumerate, what's the size, and for which is it acceptable?

Recall Solution 4.3

Enumerate: all orderings (permutations) of the cities; fix the start city to kill rotational duplicates, giving tours. Verify: sum the leg-distances, each. Total order of work.

  • : ✓ fine.
  • : ✓ still OK.
  • : ✗ borderline/too slow.

TSP is NP-hard, so beyond ~ we use Bitmasking-DP (Held–Karp, , good to ~) or heuristics/Backtracking with pruning.


Level 5 — Mastery

Goal: judge trade-offs and pick the paradigm from constraints alone.

Exercise 5.1

For each constraint, state the intended complexity and whether brute force survives. Justify with the rule. (a) (b) , subsets (c) (d) .

Recall Solution 5.1

(a) → hint of ; brute force (permutations) OK. (b) , subsets → ; even is (a bit over), so plain OK, borderline — use meet-in-the-middle if verify is heavy. (c) ; borderline but usually acceptable with light constants. (d) ✗; brute force dies. Target ✓.

Exercise 5.2

A judge gives and asks for the best subset under a constraint, hinting bit tricks. Which two paradigms are both viable, and how do you choose?

Recall Solution 5.2

, so Bitmasking brute force over all subsets is viable and simplest to get right. If the constraint has a knapsack-like weight structure, DP () is also viable and can be faster for large -free structure. Choose by: (i) if enumeration count and correctness matters most → bitmask brute force; (ii) if could grow or is small → DP. When unsure, code brute force first and use it as a reference tester for the DP.

Exercise 5.3

You wrote a fast DP and want confidence it's correct on cases. Describe how brute force earns its keep here even though the "real" solution is the DP.

Recall Solution 5.3

Use brute force as a differential tester: for many random small inputs (, where is instant), run both the brute force and the DP and assert their answers match. Because complete enumeration is guaranteed correct, any mismatch localises a bug in the DP. Brute force's slowness is irrelevant at ; its correctness-for-free is the whole point.


Recall Wrap-up: the one-line procedure (EVE)

Enumerate the candidates, Verify each, Estimate size×cost vs . If the estimate loses, hunt for structure: recurrence → DP, exchange → greedy, prune-partial → Backtracking, all-subsets small → Bitmasking. Complexity language throughout is from Time Complexity & Big-O.