Notes 05 · Sessions 8–9 · Tuesday & Thursday, September 29 & October 1, 2026
Where Patterns End
Some jobs no pattern and no machine will ever do — and this week you learn to prove it, politely, with one honest trick.
The wall, as promised
For four weeks, every job we touched turned out to be doable: design the states, or type the pattern, done. Last week ended on a promise — tonight we find the edge. You'll meet a job that looks like a warm-up and is actually impossible, and you'll learn to prove impossibility yourself. Not "I couldn't find a machine." Nobody ever will.
The job: some a's, then exactly that many b's. The yes pile is ab, aabb, aaabbb, and on up — the counting language that has haunted these notes since Week 1. Or, dressed for work: matching brackets — every open bracket closed, every close bracket earned. Same job in programmer's clothes, and every compiler on Earth has to do it.
Try to build the machine. Honestly try — this failure is the whole lesson. The design question from Notes 02: while the a's stream past, what's the least you must remember? The count. Not a digest of the count, the count itself, because at the end every single a must be answered by a b. So you lay out states: "no a's yet," "one a," "two a's," "three a's"…
Feel the failure? Every attempt ends the same way: counting with no ceiling, using a memory with a fixed size. However clever the design, some two different counts end up sharing a state — and Notes 02 told us what that means: the difference between them is forgotten for good. Tonight we sharpen that feeling into an actual proof.
"I tried hard and failed" convinces nobody. Tonight's trick convinces everybody — including the machine's biggest fan.
Plan: Tuesday, the loop trick and the game you play with it. Thursday, the other side of the coin — counting memories and shrinking machines — plus Project 1, due this week.
The loop trick
One observation powers the entire evening. A machine has a fixed handful of states — say ten. Walk a long string through it: every letter is a step, so fifteen letters make a walk of sixteen visits (count the one before the first letter). Sixteen visits, ten states — sixteen guests, ten chairs: somebody sits in the same chair twice.
The piece of the walk between the two visits — leave a state, wander, come back — is a loop. Every long enough walk has one. Always.
Now the sneaky part. Going around the loop reads some chunk of letters and lands you right back where you started — so nothing stops another lap. Or ten. Or walking straight past without entering. The machine can't object: it has no lap counter, no memory beyond "which state am I in." Which gives us:
If a machine says yes to a long string — longer than its number of states — then its walk contains a loop, and the machine must also say yes to every version of that string with the loop's chunk repeated: twice, ten times, fifty — or deleted entirely. Same doorstep in and out; the rest of the walk can't tell the difference.
Flip it into a weapon: to prove no machine can do a job, show that pumping the loop always produces a string the job must refuse — wherever the loop sits. Then every claimed machine says yes to something in the no pile. Every single one is broken.
Let's swing it at the counting job. A skeptic claims a machine — we ask one question: how many states? Say ten. Feed it ten a's, then ten b's. In the pile, so the machine says yes. But the walk's first eleven visits happen while reading the first ten letters — all a's — so the loop sits entirely inside the a's; its chunk is pure a's, at least one. Pump it twice: the a's grow, the b's don't. Out of the pile — and the machine still says yes. Caught. A hundred-state machine? Same trap, a hundred a's. Bigger machine, longer bait, same ending.
A machine is a creature of habit: whatever it does in a loop, it will happily do forever. Jobs that can't tolerate that are jobs no machine can hold.
A bit of trivia: textbooks call this the pumping lemma. Around here it stays the loop trick — walk long enough, revisit a chair, rerun the round trip.
Now get your hands on it. Below: one correct machine, and two seductive attempts at the counting job that count on a clock face and quietly wrap around. Feed each a long yes-string, watch the forced loop light up, then pump — and watch what breaks, and what doesn't.
Lab · find the loop, then pump it
amber letters = the loop's chunkWorth trying: pump the correct machine and notice nothing ever breaks — that's what "pattern-shaped" feels like. Pump a clock attempt ×0 and watch a's vanish while every b stays. And feed an attempt a short string: no loop, no trick — which is why small test cases never catch this kind of bug.
Think first: why does deleting the loop (pumping zero times) count as fair play?
The loop is a round trip back to the same doorstep — skip the detour and the rest of the walk proceeds exactly as before, ending in the same yes-state. Deleting is often the most vicious pump: it strips out a's while every b stays put.
Playing it politely: the skeptic game
Writing a loop-trick argument can feel slippery — where's the loop? how long is the string? Here's the fix: the whole thing is a two-player game with fixed turns; play your turn well and you cannot lose. Every impossibility proof in this course is you winning this game in writing.
1. The skeptic goes first and names a number: "my machine has this many states." They never show you the machine — the trick only needs the number.
2. You pick one string from the job's yes pile, built using their number, longer than it.
3. The loop is forced somewhere in the early letters of your string — you don't choose where, so your string must break for every possible loop location.
4. Pump. If every possible loop, repeated (or deleted), lands the string outside the pile while the machine still says yes — you win. And since the number was any number, you've beaten every machine that could ever exist. That's the whole proof.
Step 3 is where everyone slips, so hear it twice: you must beat every loop, not just a convenient one. The loop lands wherever the walk puts it; your only power is the string — so choose one where every legal loop location is poison.
A full round: strings that read the same backwards
Let's play a complete game against palindromes — strings that read the same backwards, an old friend from Notes 01's tester. The skeptic opens: "my palindrome machine has forty states."
Your move: forty-one a's, one b, forty-one a's. Backwards it's the same — in the pile. And here's the craftsmanship: the first forty-two visits happen while reading the first forty-one letters, and forty-two visits into forty states forces the revisit early — the loop's chunk lives entirely inside the front run of a's. Wherever exactly it falls, it is some a's.
Now pump twice. The front run of a's grows; the b doesn't move; the back run is still exactly forty-one. Front and back no longer match — not a palindrome, out of the pile — and the machine still says yes, because loops can't be counted. The skeptic mutters "fine, eighty states." You answer "eighty-one a's, a b, eighty-one a's." There is no number they can name. No machine sorts palindromes. Ever. (Notice: flavor 2 from Notes 01 — assume it works, walk into the wall — with the loop trick supplying the wall.)
Try the game with just "eighty a's" as your string — it's a palindrome too. Pumping gives more a's… still a palindrome. Loop survives, skeptic wins. The lone b is a landmark: it pins the middle, so growing the front must break the symmetry. Choosing the string is the craft — and the part graders read closely.
Think first: play a round against "matching brackets" yourself before peeking.
Skeptic says some number of states. You answer: that many open brackets, then that many close brackets. The loop is forced inside the run of open brackets. Pump twice: more opens than closes — illegal. Pump zero: fewer opens than closes — also illegal. Every loop loses, every size loses. This is the same round we played against the counting job in §02, wearing brackets — which is why your compiler doesn't check brackets with a search pattern.
What a machine must remember
Thursday's idea comes at the wall from the other side. The loop trick catches machines misbehaving; this one asks, before any machine exists: how many different things does the job force a machine to remember? Answer that and you know everything — whether a machine exists, and how big the smallest one is.
Here's the test. Take two possible beginnings of input — say the machine has read a so far, versus aa — and ask: does some ending finish one of them in the yes pile but not the other? If yes, the two must never share a state: sharing is forgetting, and that ending would then get a wrong verdict. If no ending ever tells them apart, they can happily share. So picture sorting all possible beginnings into memory drawers: same drawer only when no ending distinguishes.
Why must a, aa, aaa all get different drawers in the counting job? Take a and aaa: the ending "one b" finishes the first into the pile (ab) and leaves the second out (aaab). Different drawers. The same move separates every pair of a-runs — the ending with exactly as many b's as one of them says yes to it and no to the other. One drawer per count. Endlessly many.
Finitely many drawers? A machine exists — and the drawers themselves are its states: one state per drawer, and "which drawer is my input-so-far in" is the machine's whole memory. No machine can use fewer, because distinguished beginnings can't share.
Endlessly many drawers? No machine, ever. Any machine has some fixed number of states, two drawers would have to share one, and the ending that distinguishes them defeats it.
Sit with how strong that is: not a trick that catches machines, but a census that settles the question before anyone builds anything — and it hands you a number, not just a verdict. "Even number of a's" forces exactly two drawers, so Notes 02's two-state machine wasn't just correct, it was the smallest possible — provable now. (Textbooks call this drawer census the Myhill–Nerode theorem; you've just done the entire idea with furniture.)
Think first: how many drawers does "ends with b" force? Name them.
Two. Drawer one: beginnings whose last letter is b — the empty ending already finishes them in the pile. Drawer two: everything else, the empty string included. Any two beginnings in the same drawer stay together under every ending, because only the freshest letter ever matters. So Notes 02 §05's two-state machine is the smallest one — provably.
The slimmest machine
The drawer census says a smallest machine exists. Here's the practical question: your machine works, but is it carrying dead weight — and how would you shed it? The answer is a merging game, and it runs on one word: twins.
Two states are twins if every possible future treats them identically: start from either, feed in any ending, and the verdicts always agree. Twins are two labels on the same memory drawer — merging them changes nothing the outside world can see. So:
1. Sweep the floor. Delete any state no input can ever reach — decoration, not machinery.
2. Find twins. Test pairs: does some ending finish one in the yes pile and not the other? If yes, not twins — they genuinely remember different things. If no ending ever splits them, twins. (A yes-state and a no-state are never twins: the empty ending already tells them apart.)
3. Merge and repeat. Collapse each twin pair into one state; arrows that pointed at either now point at the merged one. Keep going until no twins remain.
What's left has one state per memory drawer — the smallest machine for the job, full stop. And it's unique: everyone who shrinks a correct machine lands on this same one.
Watch it on a real case: a five-state machine for "contains bb somewhere" — Notes 02 did it with three, and this bloated cousin carries two freeloaders. The tinted pairs are the twins: A and D both mean "no bb yet, last letter wasn't b"; C and E both mean "bb found, yes forever." Merge both pairs and the honest three-state machine reappears.
Do it yourself below. Click two states and test them: twins get merged, and non-twins earn you the exact ending that catches the difference. Watch the plain-words meanings as you go — the twins had the same meaning all along. Bloat is two names for one memory; minimizing is noticing.
Lab · merge the twins
click two states, then testWhy Project 1 graders love this: two students hand in different-looking machines for the same language — different names, different shapes, six states versus nine. Shrink both. If the slim machines match (up to renaming), the students agree; if not, some ending separates them — a failing test case, gift-wrapped. Uniqueness turns "do these machines agree?" into a mechanical check.
Think first: why can't merging twins ever change a verdict?
Follow any input through both machines. Whenever the walk sits in a merged state, the two originals agreed on every possible remainder — that's what the twin test certified. So the final verdict matches too. Merging only removes a distinction the job itself never made.
Your field kit
Tonight's two ideas snap together into a field procedure you'll use for the rest of the course — and the rest of your career, whenever someone says "just write a regex for it."
- Ask the memory question. What must be remembered about the input so far? Try to list the drawers.
- Drawers form a short list? The job is pattern-shaped. Build the machine — one state per drawer — and you know the smallest size in advance.
- Drawers keep coming forever? The job is outside the family. To convince someone else, win a round of the skeptic game in writing: their number, your string, every loop poisoned.
| job | the drawers it forces | verdict |
|---|---|---|
| even number of a's | even so far · odd so far | machine, 2 states |
| ends with b | last letter b · anything else | machine, 2 states |
| length a multiple of three | 0, 1, or 2 past the last multiple | machine, 3 states |
| same count of a's then b's | one per count — no end | no machine, ever |
| reads the same backwards | one per first-half — no end | no machine, ever |
The census in table form. Count the drawers and the whole question — possible or not, and how big — answers itself.
One closing thought. The wall is real, but it's a wall for this machine, not for computing — "matching brackets" is impossible tonight and routine for every compiler, because compilers bring more memory to the fight. Next week we meet the first tool that does: grammars, a new kind of describing power that builds strings from the inside out, and the counting language falls to it on day one. The ladder from Notes 01 has more rungs, and we just found the reason to climb.
Quick check
Six questions. Decide on your answer before you click — being wrong here is free, and useful.
Before next week
Project 1 is due this week — you've built every piece of it in class, so this is assembly and polish, not a sprint. Submit early, sleep well, and bring questions Tuesday for the last-minute clinic.
Coming up
Sessions 10 and 11 (Oct 6, 8) — Notes 06. Grammars: recipes that grow strings instead of reading them, and the first tool that walks straight through tonight's wall — the counting language falls in the opening minutes. The Project 2 handout comes out that week.
If anything tonight didn't land, email me — yair.chaya@liu.edu — or grab an office-hours slot. I reply within 24 hours.