Interactive course · ~4 hours

Computability and Complexity

You can build a computer out of a strip of paper, a pencil and a short list of rules. Not a toy version. A machine that can work out anything your laptop can work out, given enough paper and enough patience. That is the good news, and it arrives first. The bad news arrives in Step 5, when the same strip of paper is used to prove that some perfectly sensible questions have no answer any machine can ever produce.

How this works

There is a working machine here: a tape, a head that reads and writes one square at a time, a state, and a rule table you type into. Every machine in the course runs on it, including the ones that never stop. Later on, when a method is described as slow, the method is run and its work counted as it goes, so the numbers on screen were produced by running something rather than copied out of a book.

What you need before you start

Introduction to Algorithms, or at least the habit of measuring a method by counting the steps it takes rather than by timing it on one particular computer. Nothing else. No programming. No electronics. The only piece of arithmetic that turns up beyond multiplying is doubling over and over, and there is a note on it at the point where it first matters.

The steps

Step 1

A machine made of tape and rules

Put chips and screens out of your mind for a while. Here is a computer you could cut out with scissors. A long strip of paper, ruled into squares, running off to the right and the left as far as you ever need. Each square holds exactly one symbol, or nothing at all. Sitting over the strip is a head: a small reader that can look at one square, and only one.

The head repeats three actions for as long as it runs. It reads the symbol in the square beneath it. It writes a symbol into that square, rubbing out whatever was there. Then it shuffles one square to the left or to the right. That is the entire body of the machine. There is nothing else in it.

What counts as a symbol

A symbol is one mark you are allowed to write in one square. Which marks are allowed is up to whoever designs the machine, and the list is fixed before it starts. In this course the marks are the digits 0 and 1, a few letters such as X and Y, a plus sign and one more: an empty square.

An empty square needs a name, because rules have to be able to mention it. It is called the blank, and it is written as an underscore, _. On screen an empty square is drawn with a dashed edge so you can tell it apart from a square holding a real mark.

Lab 1 · Be the head yourself
Try this firstPress Write 1, then Move right ▶, then Write 0. The pink arrow above the strip is the head, and it slides one square each time you move it. The line at the bottom says what you just did. Your target is printed above the strip: get the strip to match it.
Notice how little the head can see. It has no idea what is in the square next door, or how far along the strip it has come, or what it wrote a moment ago. You were supplying all of that from your own head. Take you away and the machine cannot take a single step, because nothing in it knows what to do next. Fixing that is the rest of this step.

So the machine needs two more parts. The first is a state: one word, which you can picture on a sticky note attached to the head. That word is the machine's entire memory of what it is in the middle of doing. The second is a rule table, which is the machine's program.

A single word is the whole memory. How can that be enough

Because the tape is doing the rest of the remembering. The state only has to hold what the machine cannot write down: which part of the job it has reached. Something like "I am searching for a plus sign", or "I have seen an odd number of 1s so far". A handful of words covers that.

The number of different words allowed is fixed and finite, and that matters later. A machine cannot have a state for every number it might meet, because there are infinitely many numbers and only so many words. Anything the machine wants to count has to be counted on the tape.

A rule has five parts, written on one line. The first two are the situation, and the last three are what to do about it.

seek 0 -> 1 R seek

Read it as a sentence: when the state is seek and the square under the head holds 0, write 1 into that square, move Right, and make the new state seek. The move is R for right, L for left, or S to stay still. The machine stops when it reaches a state called halt, and it also stops if no rule in the table covers the situation it is in, which is a bug rather than a plan.

What if two rules cover the same situation

They are not allowed to. Each pair of state and symbol may appear at most once in the table. If two rules both started with seek 0 the machine would have a choice, and then nobody could say what it does. A machine with no choices is called deterministic and every machine in this course is one.

This is not a small technical detail. It is what makes the machine trustworthy: run it twice on the same tape and you get the same answer both times, every time, because at every step exactly one rule applies. The editor below will tell you if you write the same situation twice.

Lab 2 · The rulebook takes over
Try this firstPress Step five times, slowly. Each press carries out exactly one rule. The rule that matched lights up in the table, the head slides one square, and the line at the bottom reads out what happened in words. Then press Run to the end and watch the rest go by.
Notice that nothing outside the table is doing any thinking. The machine sees a state and a symbol, finds the one rule that starts with that pair, and obeys it. Then try breaking it: delete the last line of the table and press Run to the end again. The machine walks to the first blank square, finds no rule for that situation, and stops where it stands. It has not crashed, and nothing is on fire. It simply has nothing to do.
A rule table has a rule for state seek reading 0, and one for seek reading 1, and nothing else. The tape holds 0 1 1 and the machine starts in state seek on the first square. What happens?
It stops on the fourth square. Reaching halt is one way to stop, and running out of rules is the other. Neither is an error the machine can report, because the machine has nobody to report to. Later on, that second kind of stopping turns out to be handy: a machine that gets stuck has still stopped, and Step 5 only ever asks whether a machine stops, not how politely.
Step 2

It can add, and that is already surprising

Flipping 0s into 1s is not obviously computing. So make the strip of paper do arithmetic, which is what the word computer used to mean when it was still a job title.

First, numbers need a shape the machine can handle. Write three as 111 and two as 11: one mark per unit, the way a prisoner scratches days on a wall. Put a plus between them and the tape reads 111+11. The machine's job is to leave 11111 behind.

Why tally marks instead of ordinary numbers

Because it keeps the first adding machine short enough to read in one go. A machine that adds ordinary written numbers, with carrying, is perfectly possible and takes about twenty rules; a machine that adds tallies takes five. Nothing about what a machine can do depends on the choice. It only changes how many squares a number takes up and how many steps the work costs.

That difference is real, though, and Step 7 comes back to it. Written as tallies, the number one million needs a million squares. Written in the ordinary way it needs seven. When people measure how slow a method is, the size of the written-down input is what they measure against, so the choice of writing changes the measurement.

What an algorithm is

An algorithm is a method written out in enough detail that something with no judgement at all can follow it and get the right answer every time. No steps left to common sense, no "and then sort it out somehow". Long division is an algorithm. A recipe that says "season to taste" is not.

A rule table is an unusually honest way of writing one down, because the machine that follows it really does have no judgement. If a rule table works, there is no argument about whether the method was described properly: you can watch every step of it happen.

Lab 3 · Watch it add
Try this firstPress Run to the end. Five 1s are left on the strip, which is 3 plus 2. Then press Start over and use Step to go through it slowly: the plus sign turns into a 1 early on, and one 1 is rubbed out at the far end near the finish. Change the numbers in the two boxes and run it again.
Notice that it never counts anything. Turning the plus into a 1 joins the two groups into one, which gives an answer one too big every single time, so the machine walks to the end and rubs one out. Getting the wrong answer on purpose and then correcting it by a fixed amount is a trick that turns up over and over in real programs, and it is usually cheaper than doing the job properly.
Say the adding trick another way

Three tallies, a plus, two tallies. If you rubbed the plus out entirely you would have five tallies touching, which is the answer, but the machine cannot rub something out and close the gap: a square cannot be removed from the strip, only overwritten. So it writes a 1 in the plus sign's square instead. Now the strip reads six 1s in a row, one too many, and the fix is to delete any single one of them.

The machine deletes the last one, because getting to the end is easy: walk right until you meet a blank, then step back one. Walking left to the front would work just as well and cost the same.

Lab 4 · Fix the machine that is always one too big
Try this firstPress Check my machine before changing anything. It runs your rules on five sums you cannot see, including two with a zero in them, and names the first one that came out wrong along with what was left on the strip. Then change one symbol in the last rule and check again.
The check never reads your rules. It runs them and looks at the strip afterwards, which is the only fair way to mark a program. Any table that leaves the right number of 1s behind passes, whether it works the way the one above does or by some entirely different route. Try the sums with a zero in them by hand as well: a machine that gets 3 plus 2 right and 0 plus 0 wrong is the normal kind of broken.
The adder is given two tally numbers that together fill about n squares. Roughly how many steps does it take before it stops?
About n. The head crosses the strip once, from the first square to the first blank, and then takes one step back. The size of the table says nothing about the running time: five rules can keep a machine busy for a week, and Step 6 has one that keeps five rules busy for ever. What the running time follows is the size of the input.
Step 3

Anything a computer does, this can do

Every programming language ever made offers the same three things underneath the syntax. Somewhere to keep values. A way to choose between two paths. A way to repeat something. The strip of paper has all three, and they are worth meeting one at a time, because after this step the machine gets used rather than explained.

Values live in two places: on the tape, which is roomy and slow to reach, and in the state, which holds one word and is instant. Choosing happens on every single step, because the pair of state and symbol is what picks the rule. Repeating is a rule whose next state is the state it came from, which sends the machine round the same rule again.

Which part of a rule table is the loop

Look for a rule whose first word and last word are the same. walk 1 -> 1 R walk reads the symbol, leaves it alone, moves right, and comes back to walk. If the next square also holds a 1, the same rule fires again. That is a loop, and it keeps going for exactly as long as the condition holds, which here means until the head meets something that is not a 1.

The way out of the loop is another rule starting with the same state and a different symbol. In a familiar language you would write "while the square holds a 1, move right". Here the while and the condition and the body are all folded into two lines.

Lab 5 · The three ingredients, one at a time
Try this firstPress remember, then Run to the end. The machine rubs out the first symbol, walks to the far end, and writes that same symbol there, without ever being able to see both ends at once. Then press choose and repeat and run each of those.
Notice where the remembered symbol was kept. Not on the tape: it was rubbed out on the first step. It was in the state name. carry0 and carry1 behave identically all the way along the strip and only differ at the very last rule, which is what carrying a value in a state looks like. One word of memory, used well, and it survived a walk of any length.

Some machines are asked to work something out, and some are asked a yes-or-no question. For the second kind we need a way to give an answer, and the tidiest way is to use the state the machine stops in. Two more special state names join halt: a machine that stops in state yes is answering yes and one that stops in no is answering no.

Why the answer is a state name rather than a mark on the tape

It could be either. Writing Y or N in a square would work perfectly well, and some books do it that way. Using the stopping state is slightly tidier because the machine does not have to tidy the tape first, and because the thing we care about from Step 5 onwards is what a machine does rather than what it leaves lying around.

A machine that answers a yes-or-no question and always stops is said to decide that question. Hold on to that word. Almost everything in the second half of this course is about which questions can be decided and which cannot.

Lab 6 · Build a machine that answers a question
Try this firstPress Check my machine straight away. It runs your table on seven strips you cannot see, including an empty one, and tells you which one it got stuck on and why. The table is missing two rules. Add them and check again.
Notice that the table is a counter that can only count to two. It never writes anything down, and it never looks back. All it does is flip between two states as it passes each 1, which is enough to answer the question because the question only ever needed the last flip. Working out what the smallest thing you must remember is, and remembering only that, is most of what algorithm design is.

Now the claim this whole course rests on. Anything your laptop can work out, one of these strips of paper can work out too. Not quickly, and not comfortably, but it gets there. Nobody has proved that, because "anything a computer can do" is not a precise enough idea to prove things about, but every attempt to invent a more powerful machine since the 1930s has turned out to be exactly as powerful as this one. The claim has a name: the Church-Turing thesis.

How can people be so sure, if it has never been proved

Because of how many separate attempts landed in the same place. Alonzo Church built a system out of nothing but functions being applied to other functions. Alan Turing built the tape machine. Kurt Gödel and Jacques Herbrand built one out of equations about whole numbers. Emil Post built one out of a queue. All of these were done at roughly the same time, by people working differently, and each system turned out to be able to imitate every other one exactly.

Every computer built since fits the same pattern, which is the part that matters for the rest of this course. A limit proved about the strip of paper is a limit on every machine anybody has ever managed to build, including the ones that do not exist yet. That is why Step 5 is allowed to say "no computer" and mean it.

Someone claims to have designed a machine that can do something no tape machine can do, because it has a thousand tapes running side by side. What is the most likely truth?
One tape can imitate the lot. Interleave the thousand tapes along one strip and write a marker for each head, and the single-tape machine can carry out any step the multi-tape machine would. It is far slower, because a step that took one move now takes a long walk, but it computes the same answers. Almost every "more powerful machine" turns out like this: faster or more convenient, never able to answer a question the plain tape machine cannot.
Step 4

A program is data

Look at a rule table again, and this time do not read it as instructions. Read it as a row of symbols. Words, digits, arrows, spaces. There is nothing about it that stops you writing it out along a tape, in the same squares you would use for a number.

Turning something into a row of symbols by a fixed set of conventions is called encoding it, and the row you end up with is called a string. Encoding a rule table is not clever. Pick a separator between the five parts of a rule, pick another between one rule and the next, and write everything out in order.

What a string is, and why the word is used

A string is a row of symbols one after another, in a definite order, with a definite length. hello is a string of five symbols. So is 111+11, and so is the whole contents of a tape between the first and last mark. The name is old and comes from the picture of beads strung on a thread.

The word gets used constantly in computing because so many different things turn out to be one: text, a program's source, a picture file, a message crossing a network. All of them are rows of symbols that some agreed convention says how to read.

Lab 7 · Squash a machine into a row of symbols
Try this firstChange the word seek to go everywhere it appears in the table. The encoded string underneath gets shorter as you type, and the count of squares next to it drops. Then press the adder to load a bigger machine and watch the string grow.
Notice that the encoding is dull on purpose. Commas between the parts of a rule, semicolons between rules, and that is the whole convention. Dull matters, because the next lab needs a machine to be able to read this string back and act on it, and a machine has no imagination. Any convention would do so long as it is fixed in advance and never ambiguous.

Now put a description of a machine on a tape, put an input next to it, and build a second machine whose job is to read the description and do whatever it says. That second machine is called a universal machine, and it is the single most consequential idea in this course.

You have been using one of these all day

The reason a laptop is not sold as a chess machine, or a word machine, or a spreadsheet machine, is that it is a universal machine. The programs on it are not part of it. They are strings sitting in storage, and the machine reads one and behaves like whatever that string describes. Change the string and the same hardware becomes a different machine.

Before this idea, a machine that did a job was built to do that job. Turing's paper of 1936 described the universal machine years before anyone had built an electronic computer at all, and every general-purpose computer since has been one.

Lab 8 · The machine that runs other machines
Try this firstPress Run to the end. The long strip at the top is one single tape: a machine description, then a #, then that machine's input. Underneath, the panel shows the machine being described as it runs, with its own strip, its own state, and its own step count. Then press the parity machine to put a completely different program on the same tape.
Nothing about the machine being run is built into this page. Edit the description on the top strip by hand, change an R to an L, and press Run again: the behaviour underneath changes, because the behaviour is coming off the tape. That is the whole trick, and it is why the next step is possible at all. Once programs are data, a program can be handed to a program, including itself.
Is that one machine, or is the page cheating

A real universal tape machine does the reading and obeying with nothing but its own rule table, and it is a slog: a few hundred rules and a great deal of shuffling back and forth between the description and the working area to look up each step. Every serious textbook builds one and nobody enjoys it. In the lab above, that shuffling happens at full speed instead of one square at a time, so the idea is watchable.

The part that would be cheating is not happening. The rules being obeyed are read from the top strip, symbol by symbol, and if you write nonsense there the panel below reports that it could not make sense of the description, exactly as the real machine would grind to a halt on it.

You encode machine A as a string and write it on a tape. You then feed that tape to machine A itself. Which is true?
An ordinary run. A machine has no idea what the symbols on its tape are supposed to mean. The adder from Step 2, handed its own description, would hunt for a plus sign and make a mess. Nothing dramatic happens. That flatness is what makes the next step work: feeding a machine its own description is allowed, is easy, and is not a trick.
Step 5

The question with no answer

Some machines stop. Some run for ever. Both are easy to write, and once you have a few dozen rules it is no longer obvious from looking which kind you have. Every programmer has shipped a program that hangs, and every programmer has wished the computer would say so in advance.

So here is the question, stated carefully, because the care is where the whole argument lives. Given a description of a machine, and an input for it, will that machine eventually stop? This is called the halting problem.

Why not just run it and see

Run it, and one of two things happens. It stops. You have your answer. Or it is still going. You have learned nothing, because "still going after a million steps" and "going for ever" look identical from the outside and always will. There is no moment at which waiting turns into knowing.

You might try setting a limit: run it for a million steps, and if it has not stopped call it a non-stopper. That method always gives an answer, and it is wrong about every machine that stops at step one million and one. The lab below has a machine that runs for over a thousand steps and then stops. It is not doing anything odd.

Lab 9 · Stops, or never stops
Try this firstRead the first machine, press stops or never stops to commit, then press Run it. The machine runs with a budget of 4000 steps and reports what actually happened. Your running score is kept at the bottom. Press Next machine ▶ for the next one.
Notice what the third and fifth ones do to your confidence. One takes over a thousand steps and then stops perfectly normally. Another stops or does not depending only on whether the input holds an even number of 1s, which is a fact about the input rather than about the machine. The question is never just "does this machine stop". It is always "does this machine stop on this input".

Running it is not a method, then. What we want is a machine that reads a description and an input and answers correctly without running anything, or at least without running it forever. The argument below shows that no such machine can exist. It is not a hard argument, but it has to be walked through in order, so it is laid out as cards you turn over one at a time.

What "suppose it exists" is doing in an argument

It is a way of proving something is impossible without having to check every possibility. You pretend the thing exists, follow the consequences honestly, and arrive somewhere that cannot be true. Since the reasoning was honest, the fault has to be the pretence, so the thing does not exist. This is called proof by contradiction.

Small example. Suppose there is a largest whole number, and call it L. Then L plus 1 is a whole number, and it is bigger than L. So L was not the largest after all and the supposition has eaten itself. Nobody had to look at every number. The argument on the cards has exactly this shape, with a machine in place of a number.

Lab 10 · The contradiction, one card at a time
Try this firstRead card 1 and press Go on ▶. Some cards ask you to choose before they turn over, and a wrong choice explains itself and lets you try again. At card 4 the argument splits in two and you have to walk both halves; the row of dots at the top shows which cards you have turned and which are still face down.
Notice how little was assumed. Not that H is slow, or complicated, or built any particular way. Only that it exists, always stops, and is never wrong. Everything after that was ordinary construction: run one machine, look at its answer, do the opposite. The contradiction has nowhere to hide, so the only thing that can be false is the very first supposition.
The whole thing in one breath

Suppose a perfect stopping-detector exists. Build a machine that asks the detector what it will do, and then does the opposite. Ask that machine about itself. Whatever it does, it does the opposite of what it does, so the detector was never perfect. That is the entire argument and every extra sentence in the cards is there to keep it honest rather than to add anything.

The same shape of trick shows up elsewhere."This sentence is false" has no truth value for the same reason. What Turing added was the observation that a machine can be handed its own description, which turns a word game into a fact about every computer that will ever be built.

A friend says the halting argument only works because C was built to be awkward, and that an honest programmer would never write such a machine. What is wrong with that?
One counterexample is enough. H was not claimed to be usually right, or right about programs people actually write. It was claimed to be right about every machine and every input. C is a machine, and it is not even a complicated one, so it counts. This is also why the result does not stop anyone writing useful loop-checkers: those never claimed to handle every machine, and Step 6 has one you can try to defeat.
Step 6

What undecidable actually means, and what it does not

The word for what Step 5 found is undecidable. A yes-or-no question is undecidable when no single machine answers it correctly for every input while always stopping. The halting problem is the first question ever shown to be undecidable, and it is the one most others are traced back to.

The word gets misread constantly, and usually in the direction of despair, so here is what it does not mean. It does not mean nobody can tell whether any particular program stops. It does not mean the question is merely difficult. It does not mean a faster computer would help, or that a cleverer person might crack it next year. It means there is no method that always works.

Undecidable is a claim about all inputs at once

Pick any single machine and any single input. Either it stops or it does not, and that fact is already settled, whether or not anyone knows it. For most machines you meet, working out which is easy: the adder from Step 2 obviously stops and a rule that sends the head right forever on blank tape obviously does not.

Undecidability is about the whole collection. There is no one machine that gets every case right. There are always cases left over, and no amount of adding special-case tests to your checker will finish the job, because the argument in Step 5 can be run against your improved checker just as well as against the original.

Then why does my compiler warn me about infinite loops sometimes

A compiler is a program that turns the code a person writes into the instructions a machine runs, and on the way it inspects the code and points out things that look wrong. Those warnings are honest because of how they are phrased. A compiler says "this loop cannot exit", not "this program halts". It only speaks when it is certain, and stays quiet otherwise.

That is allowed, and it is useful. It is not a solution to the halting problem. A checker with three possible answers, yes, no, and I cannot tell, is easy to build and never wrong. A checker with two possible answers that is never wrong is what cannot exist. The lab below is exactly such a three-answer checker. You are invited to defeat it.

Lab 11 · Eight claims, true or false
Try this firstPress true or false on the first claim. The reason appears underneath it straight away, right or wrong, and your score updates at the bottom. Work down the list; press Clear my answers to go round again.
The two that catch most people are the third and the sixth. A faster computer changes how long you wait, never whether an answer exists. And putting a hard limit on the number of steps really does make the question answerable, because then running the machine is a method that always finishes. The next step is about what that answer costs.
Lab 12 · Write a machine the checker cannot classify
Try this firstPress Ask the checker without changing anything. It answers stops, and says how it knows. Your job is to change the rules or the starting strip so the checker must return cannot tell. Press Check my machine to have that marked.
The checker is never wrong, and that is the point. It says stops only after watching a machine stop. It says never stops only when it has caught the machine in exactly the same situation twice, same state, same square, same tape, which guarantees it will go round again forever. Everything else gets cannot tell, and no amount of extra cleverness would ever empty that third box.
Is halting the only undecidable thing about programs

Far from it. A result called Rice's theorem says that essentially every interesting question about what a program computes is undecidable. Does this program ever print the number 7. Do these two programs always agree. Does this program ever touch that file. All undecidable, and each one is proved by showing that a machine answering it could be turned into a machine answering the halting problem, which Step 10 shows you how to do.

Questions about how a program is written stay perfectly answerable: how many lines it has, whether a particular word appears, whether every bracket is closed. The line runs between the text of a program and its behaviour. Anything about the text is fair game; almost anything about the behaviour is not.

You write a loop-checker that reports never stops only when it sees a machine return to a situation it has been in before, and reports cannot tell the rest of the time. A colleague says this violates the Step 5 result. Who is right?
You are. What cannot exist is a checker that always gives one of two answers and is never wrong. Yours has three answers and hides in the third one whenever it is unsure. Real tools do exactly this, which is why compilers and analysers exist at all. The honest description of such a tool is "never wrong, often silent".
Step 7

Answerable, and hopeless anyway

Undecidable is one way to be stuck, and it is the dramatic one. The other way costs far more sleep in practice, because it looks like success right up until the moment you use it on a real amount of data.

Take this question. You have a list of numbers and a target. Is there some group of the numbers that adds up to exactly the target? There is an obvious method: try every possible group and see. That method always finishes, so the question is decidable. It is also unusable, and the lab shows how quickly.

Why the number of groups is 2 multiplied by itself

Go along the list and make one decision per number: in the group, or out of it. Two choices for the first number. For each of those, two choices for the second, so four so far. Then eight, then sixteen. With n numbers you get 2 multiplied by itself n times, which is written 2n and said as "two to the n".

Small numbers hide how fast this moves. Ten numbers give 1024 groups, which is nothing. Twenty give just over a million. Forty give over a million million. Each single extra number doubles the entire pile, which is why the counter in the lab below goes from comfortable to hopeless in about ten presses.

Lab 13 · Add one more number, watch the wall arrive
Try this firstPress Add one more number ▶ about eight times, pausing to read the counts. Both methods are really run each time and their work really counted: the top lane scans the list once, the bottom lane tries every group. The right-hand column shows how many times the count grew when the list got one longer.
Notice the ratio column rather than the counts. The scan's ratio sits near 1, because one more number is one more step. The brute-force ratio sits at 2, every time, without drifting. That steady 2 is the whole problem: it means the cost is not creeping up with the list, it is doubling with it, and doubling always wins in the end.
Counting steps rather than timing seconds

Seconds are a property of the machine you happened to use, the other programs running that day, and how warm the room is. Steps are a property of the method. Run the same method twice on the same input and the seconds differ while the step count is identical, which makes steps the honest thing to compare.

To turn steps into a feel for time you pick a speed and multiply. A modern processor manages something in the region of a billion simple steps a second, and that figure is what the next lab uses. It is a rough figure, deliberately generous, and being generous does not save the exponential method.

Lab 14 · Guess where the wall is
Try this firstDrag the guess slider to the list length you think takes more than a day, then press Show me the answer. The exact length is worked out from the speed you picked, and both your guess and the true answer are shown as times. Then change the speed to a trillion steps a second and press it again.
Notice how little the speed slider moves the wall. Making the computer a thousand times faster buys about ten more numbers on the list, because ten doublings is roughly a thousandfold. A million times faster buys twenty. Even multiplying the lab's fastest machine by a million million buys only forty more numbers, and then each extra number doubles the wait again. A fixed hardware improvement moves the wall; it never removes the exponential growth.
A method takes 2n steps and currently handles lists of 45 numbers overnight. Your company buys a machine 1000 times faster. Roughly what can it handle overnight now?
About 55. A thousandfold speed-up is close to ten doublings, and each doubling of available time buys exactly one more number. This is the signature of an exponential method and the reason people care so much which side of the line a problem falls on. A method taking n2 steps, given the same new machine, would go from 45 to about 1400.
Step 8

P: the problems that finish in reasonable time

We need a line between methods you can use and methods you cannot, and the line has to be drawn without mentioning any particular computer, because computers keep changing and the line should not.

The line everyone settled on is this. Count the steps as a formula in n, the size of the input. If the formula is n multiplied by itself some fixed number of times, the method is called polynomial and counts as usable. So n, n2, n3 and n5 are all in. If n appears in the exponent instead, as in 2n, it is out.

What n2 and n3 are counting

n2 means n times n. It is the number of ways of picking an ordered pair out of n things, which is why any method that compares everything with everything else lands on it. n3 is n times n times n, the number of ordered triples, and it shows up when a method has three nested walks through the data.

The difference from 2n is which part grows. In n3 the base grows and the exponent is stuck at 3. In 2n the base is stuck at 2 and the exponent grows. That swap is the entire distinction, and the second lab in this step shows how brutal it is.

What people mean by a class of problems

A class is a bag with problems in it, and a rule for what gets in. The bag called P holds every yes-or-no question for which somebody has found a polynomial method. Is this number in this sorted list. Is there a route from this town to that one. Does this list contain a repeat. All in P.

Membership is about the problem, not about your particular attempt at it. A problem is in P the moment any polynomial method exists, even if the method you personally wrote is exponential, and even if nobody has yet noticed the fast method. That last part matters: problems move into P when someone finds a better method, and several famous ones have.

Lab 15 · The doubling test
Try this firstPress all pairs, then press Double n ▶ three or four times. Each press really runs the method on a list twice as long and counts every comparison. Watch the ratio column: it settles near 4, and the widget names the shape underneath. Then try the other three methods.
The ratio is the diagnostic, not the count. Doubling n multiplies a linear method's work by 2, a quadratic method's by 4, a cubic method's by 8. Those numbers are fixed and you can read the shape straight off them. The exponential method has no fixed ratio at all: its ratio itself keeps squaring, which is why its lane runs out of room long before the others get interesting.
An n to the hundred method is not reasonable either, so why draw the line there

Because the line has two properties nothing better has managed. It does not change if you swap the kind of machine: a method that is polynomial on a tape machine is polynomial on a laptop, with a different exponent. And polynomials survive being combined, so a polynomial method that calls another polynomial method a polynomial number of times is still polynomial. A line drawn at, say,"under a million steps" has neither property.

The practical defence is that the line turns out to bite. Problems with polynomial methods almost always end up with small exponents, usually 3 or less, once people have worked on them for a while. Problems without one usually have nothing at all. The middle ground the objection worries about is oddly empty.

Lab 16 · Where the exponential overtakes
Try this firstLeave the exponent at 2, guess with the slider, then press Find the crossover. The page searches upward from n equals 2 for the first length where 2n beats n raised to your exponent, and shows both values there. Then drag the exponent up to 10 and to 20 and find them again.
Notice that a crossover always exists. Push the exponent as high as you like and the exponential still wins in the end; all a bigger exponent does is move the meeting point along a bit. That is the mathematical reason the line is drawn where it is. Every polynomial, however awful, is eventually beaten by every exponential, however gentle.
Two methods solve the same problem. Method A takes 1000 × n2 steps. Method B takes 2n steps. Which belongs in P, and which would you use on a list of 20?
A is in P, and B wins at 20. A does 400000 steps there and B does about a million, so on small inputs the exponential method is the sensible choice. P is a statement about what happens as inputs grow, not a promise about any particular size. Real engineers switch methods at a measured crossover point all the time, and they are not contradicting anything here.
Step 9

NP: when checking is the easy half

Here is a shape of problem that turns up everywhere once you notice it. Finding an answer looks hopeless. Checking an answer somebody hands you is quick and dull. Timetables, seating plans, delivery routes, packing a van: all the same shape.

The example for this step is colouring. You are given a set of dots with lines drawn between some pairs of them, and three colours. Colour every dot so that no line ever joins two dots of the same colour. Checking a finished colouring means looking at each line once. Producing one from a blank picture is another matter.

Dots and lines: what a graph is

A graph in this sense has nothing to do with charts. It is a set of dots, called nodes, and a set of connections between pairs of them, called edges. That is all. The picture on screen is only one way of drawing it; sliding a dot around changes nothing, because a graph is the pattern of connections and not the positions.

Graphs are used for anything made of things and relations between them: towns and roads, people and friendships, tasks and which must come first. Colouring with the rule that joined dots differ is how timetabling works. Dots are exams, an edge means some student sits both, and a colour is a time slot.

Lab 17 · Checking a finished answer
Try this firstPress Check it. The edges are examined one at a time, each lighting up as it is looked at, and the counter shows how many looks it took. Then press Nudge one dot to recolour a single dot, and press Check it again to watch the check fail and name the offending line.
Notice how cheap checking is. One look per edge, no backtracking, no cleverness, and the number of looks is fixed by the picture rather than by luck. An answer that can be checked like this is called a certificate: it is not an argument, it is just the answer itself, offered so that anybody can verify it quickly.
Lab 18 · Now find one yourself
Try this firstClick a dot to cycle its colour and try to get the conflict counter down to zero. Give it a genuine attempt before pressing Let the machine try every colouring, which grinds through the possibilities in order and keeps a running count of how many it has tried.
Compare the two counters. Checking took one look per edge. Finding took the machine thousands of attempts, and the picture has only a dozen dots. Add ten more dots and the number of possible colourings is multiplied by 3 ten times over, while the cost of checking one of them barely moves. That gap between finding and checking is the subject of the rest of this course.

The bag of problems where a proposed answer can always be checked in polynomial time is called NP. Colouring is in it. So is every problem in P, because if you can find an answer quickly you can certainly check one quickly by finding it yourself and comparing. Whether NP holds anything that P does not is the open question this course is heading towards.

NP does not stand for "not polynomial"

It stands for nondeterministic polynomial time, which is a mouthful from an older way of describing the same bag. Imagine a machine allowed to make guesses, which magically always guesses correctly when a correct guess exists. Such a machine solves colouring in polynomial time by guessing the colours and then checking. Nobody can build one, but it defines the same collection of problems as the checking definition, and the checking definition is the one to keep in your head.

The misreading matters because "not polynomial" would mean NP and P have nothing in common, and the truth is the opposite: every problem in P is also in NP. P sits inside NP. The open question is whether it fills it.

Which of these would show that a problem is in NP?
The checking one. NP is defined entirely by how quickly a candidate answer can be verified, and says nothing about how hard the answer was to come by. The third option describes what people currently know rather than a property of the problem, and a method that only works up to a thousand is not a method at all in this game. Note also that the first option, if it worked for every size, would put the problem in P, which would put it in NP as well.
Step 10

Turning one problem into another

Here is the move that organises the whole field. Suppose you have a problem A you cannot solve, and a problem B someone else can solve. If you can quickly rewrite any question about A as a question about B, in such a way that B's answer gives you A's answer, then B's solver solves A too. That rewriting is called a reduction.

It is worth being slow about the direction, because it is the thing everyone gets backwards. Writing A as B means B is at least as hard as A, never the other way round. A solver for B does A's work as well, so B is carrying more.

An everyday version of the same move

You cannot measure the height of a tall building, but a friend has an accurate way of measuring shadows and angles. You turn your problem into theirs: measure the shadow, measure the angle of the sun, hand those over, get a height back. You have reduced building-measuring to shadow-measuring, and shadow-measuring is now at least as useful as building-measuring.

Notice the shape. The translation has to be something you can actually do, the answer has to come back meaning what you wanted, and none of it says a word about how your friend does the measuring. That indifference is the strength of the method: you can use a solver you know nothing about.

The problem everything gets reduced to in practice is a puzzle made of switches. You have a row of switches, each either on or off, and a list of requirements. Each requirement names a few switches and says "at least one of these is the way I want it". Satisfy every requirement at once, or show that no setting does. This is called satisfiability, or SAT.

Reading a switch puzzle written down

Each switch gets a name. A requirement is written as a short list of switch names inside brackets, joined by "or". A plain name means that switch must be on to satisfy the requirement; a name with not in front means it must be off. So (a or not b or c) is satisfied if a is on, or b is off, or c is on. Each such requirement is called a clause.

You must satisfy every clause at the same time, using one setting of the switches. A single clause is trivial. Hundreds of overlapping clauses, where turning a switch on to rescue one clause breaks three others, is not. Every setting of 20 switches means a million possibilities, and the counting from Step 7 applies exactly.

Lab 19 · Turn a colouring into a switch puzzle
Try this firstPress Translate to a switch puzzle. One switch appears for every dot-and-colour pair, and the clauses build up in the panel below, with the count of each kind. Then press Solve the switch puzzle and watch the answer come back and land on the dots as colours.
Notice that the solver never sees a picture. It is handed switch names and clauses and nothing else, and it hands back a setting of switches. The translation back into colours happens outside it. That is what makes reductions worth the trouble: one good switch-puzzle solver, and every problem you can translate becomes solvable, including problems invented after the solver was written. Then press Add the awkward edge, which joins the hub to the two remaining dots of the ring so that it touches all five, and watch the solver prove that no setting works at all.
Why the translation itself has to be quick

If translating took exponential time, the reduction would prove nothing. You could always "reduce" anything to anything by solving the first problem during the translation and then producing a trivial question with the right answer, which is cheating dressed as a proof.

So the rule is that the translation must be polynomial. In the lab above it is better than that: one clause per dot, three per dot to stop two colours being on at once, and three per edge. That is a fixed amount of work per piece of the picture, and you can watch the clause counter to confirm it grows in step with the number of dots and edges rather than exploding.

Lab 20 · Which way does the hardness flow
Try this firstRead the arrow, then judge the four claims below it using the follows and does not follow buttons. Each one tells you whether it was right and why. Then press Point the arrow the other way ⇄ and do all four again: two of them swap answers.
One arrow supports exactly two of the four claims. An arrow from A to B says a fast solver for B gives a fast solver for A, and equivalently that B is at least as hard as A. It says nothing whatever about the other direction. When both arrows exist, as they happen to for colouring and switch puzzles, the two problems are equally hard and all four claims hold at once, which is why it helps to practise on a single arrow first.
You show that the halting problem can be quickly rewritten as question Q. What follows?
Q is undecidable. Follow the machinery: if some machine decided Q, you would translate any halting question into a Q question, ask it, and take the answer. That would decide halting, which Step 5 rules out. So no machine decides Q. This is how essentially every undecidability result after 1936 was obtained, and it is the same shape of argument used for hardness inside NP, with "no method at all" replaced by "no fast method".
Step 11

The hardest problems in NP, and what a proof would mean

In 1971 Stephen Cook proved something that sounds too strong to be true, and Leonid Levin proved it independently in the Soviet Union at about the same time. Every single problem in NP can be quickly rewritten as a switch puzzle. Not the ones we have thought of. All of them, including the ones nobody has invented yet.

The sketch is closer to this course than you might expect. A problem is in NP when a checking machine can verify a candidate answer within a polynomial number of steps. That machine is a tape machine, and its whole run over that many steps can be written out as a grid of squares: which symbol is in which square at which moment, where the head is, what the state is. Then you make one switch for each of those facts, and clauses saying the grid describes a legal run that ends in yes. A setting of the switches is a run that accepts, which is a valid answer to the original problem.

How can anyone reduce a problem they have never seen

By reducing the definition rather than the problem. Being in NP already tells you everything the proof needs: there is a checking machine, and it stops within some polynomial number of steps. You never have to know what the problem is about. You only need its checker and the construction turns any checker whatsoever into clauses.

This is the same style of argument as Step 4, where the universal machine ran a program it had never seen by reading its description. Once a machine is a description, general statements about all machines become possible, and Cook's proof is the most powerful thing anybody has done with that.

A problem that is in NP, and that every other NP problem reduces to, is called NP-complete. Cook and Levin showed SAT is one. Richard Karp then showed 21 more within two years by reducing SAT to them, each new one becoming a stepping stone for the next and the list now runs to thousands.

In NP, NP-hard, NP-complete: three different things

In NP means answers can be checked quickly. That is a ceiling on difficulty, and easy problems qualify: adding two numbers is in NP. NP-hard means everything in NP reduces to it, so it is at least as hard as all of them. That is a floor, and it does not require the problem to be in NP at all; the halting problem is NP-hard and is not in NP.

NP-complete means both at once: in NP, and NP-hard. Those are the problems sitting exactly at the top of NP. They are the interesting ones, because a fast method for any single one of them would be a fast method for every problem in NP.

Lab 21 · Pull one domino over
Try this firstClick Vertex cover, then press Suppose this one gets a fast method. The page follows the reduction arrows backwards from it and lights every problem that would become fast as a result, and says how many and by what route. Then try it on Two-colouring and on Switch puzzle (SAT).
Notice that every NP-complete card lights up whichever one you pick. The arrows form a loop, so hardness flows all the way round: solve any one of them quickly and you have solved all of them, plus every other problem in NP. Two-colouring is the odd card out. It is already fast, nothing reduces to it, and pressing the button on it lights nothing new, which is exactly what being easy looks like on this map.

So the question, worth a million dollars from the Clay Mathematics Institute since 2000 and unanswered since 1971, is whether P and NP are the same bag. Nobody has found a fast method for any NP-complete problem, and nobody has proved that none exists. Almost everyone who works on it expects P and NP to be different, which would mean the gap you felt in Lab 18 is real and permanent.

What would actually break if the two turned out to be equal

Most of the locks. Public-key cryptography, which is what makes online payment possible, rests on certain problems being easy to check and hard to solve, which is exactly the NP shape. A fast method for SAT would very likely undo a large part of it. On the other hand, protein folding, chip layout, timetabling and thousands of scheduling problems would fall out at once, which would be a considerable compensation.

Two cautions. A proof might be non-constructive, showing a fast method exists without saying what it is. And a method taking n100 steps would settle the question while changing nothing whatever in practice. Even the dramatic answer might arrive quietly.

Lab 22 · The workshop
Try this firstPress a machine that never stops, then Ask the checker. Nothing here is marked. Load any machine from the course, take it apart, break it, and see what the checker makes of the wreckage.
Two things worth trying. Write a machine that stops on some inputs and not on others, then use the tape box to flip it between the two without touching a single rule. And take a working machine and change one R to an L: about a third of the time you get an immediate loop, and the rest of the time it stops early with the job half done. The checker in Step 6 proves almost every one of those loops, which is a small demonstration of why loop bugs are so common and how much a repeated situation gives away.
A friend announces they have found a method that solves graph colouring in n4 steps, and it has been checked and works. What follows?
P would equal NP. Colouring is NP-complete, so every problem in NP can be translated into it in polynomial time, and the translation plus the n4 method is a polynomial method for that problem. The halting problem is untouched: it is not in NP, and no amount of speed reaches it. Fast and possible are separate questions, which is the one idea this whole course was built around.
Step 12

Decide, recognise and enumerate are different promises

A decider stops on every input and answers yes or no correctly. A recogniser has a weaker contract: it must eventually say yes when the answer is yes, but on a no-instance it may say no or keep running. Watching forever is not a way to confirm no.

The strings accepted by a recogniser form a recognisable language. If both a language and its complement have recognisers, run the two machines a step at a time. One must eventually accept, so together they make a decider. Halting is recognisable: simulate the named program and accept if it stops. Its complement cannot be recognisable, or this two-machine method would decide halting.

Lab 23 · Read the stopping promise
Try this firstChoose recogniser, then test its no case. Compare its contract with a decider and with a program tried on only a few examples.
A time limit can make a useful test, but it does not turn unknown into a correct no.
Lab 24 · Dovetail two searches
Try this firstAdvance the round count. The scheduler gives every started search another step instead of waiting forever for the first one.
Dovetailing prevents one infinite branch from starving every other branch. It does not make an impossible decision possible.
Language

A set of finite strings. A decision problem asks whether an input string belongs to it.

Complement

All valid input strings that are not in the language.

Enumerator

A machine that eventually prints every string in a language.

Why enumeration and recognition match

Given an enumerator, wait until the target string appears. Given a recogniser, dovetail it over every possible string and print a string when its run accepts. This is an exact equivalence.

A recogniser has run for a million steps without accepting. What may you conclude?
Only not yet. A recogniser may run forever on a no-instance.
Step 13

Program text is inspectable; general program behaviour is not

Some properties are about syntax: “does this rule table contain an R?” can be answered by reading its finite description. Other properties ask what the program computes: “does it accept any string?” or “does it compute the same function as that program?” Those quantify over behaviour.

Rice’s theorem says every nontrivial semantic property of the partial function computed by a general program is undecidable. Nontrivial means some programs have it and some do not. Useful analysers remain possible by restricting the language, requiring annotations, proving only some cases, or returning unknown.

Lab 25 · Sort syntax from behaviour
Try this firstOpen each property. Decide whether a tool can inspect text directly or must settle arbitrary executions.
A semantic property can be decidable for a restricted language. Rice’s theorem concerns general computation.
Lab 26 · Follow an undecidability reduction
Try this firstPick a proposed perfect analyser. See how a halting instance is wrapped into a program whose behaviour exposes the answer.
Transform a known impossible problem into the proposed one. A perfect solver at the new end would solve the old problem.
Unknown can be the correct result

A sound analyser says safe only with a proof. For many behaviours, no analyser can be both sound and complete over all programs. Tests add evidence from selected runs; they do not cover every input.

Which property is plainly decidable from a finite source file?
Count the lines. That is syntax. The other choices ask about behaviour across executions.
Step 14

Complexity counts the input that is actually written down

Running time is measured against encoded input length. One million needs one million marks in unary but only 20 bits in binary. A loop that runs once per numeric value is linear in a unary input and exponential in the length of a binary input.

An algorithm can be pseudo-polynomial: polynomial in a numeric value such as capacity, but exponential in the bits used to write that value. State the encoding, parameters and arithmetic cost model beside a complexity claim.

Lab 27 · Write the same number two ways
Try this firstRaise the value. Compare unary length, binary length and a value-sized loop.
The value has not changed. Its representation changes which input length the running-time claim must use.
Lab 28 · Expose pseudo-polynomial work
Try this firstIncrease knapsack capacity W. A dynamic program with nW cells can be practical and still be exponential in the bit length of W.
Pseudo-polynomial tells you when a method is useful: when the numeric parameter itself is modest.
Arithmetic operations also have a cost

A unit-cost model treats addition or multiplication as one step. For very large integers, their bit lengths matter and arithmetic itself takes more work. State which model is being used before comparing algorithms.

A loop takes W steps, and W is written in binary using k bits. What can happen?
Exponential in written length. A k-bit unsigned value can be as large as 2k-1.
Step 15

Time, memory and which side has a short proof are separate questions

Time counts steps; space counts simultaneously used memory. Depth-first search can recompute work to save memory. A breadth-first frontier may use much more memory while finding a shallow answer sooner. Real limits include both resources.

NP gives short, quickly checked certificates for yes-instances. coNP contains problems whose no-instances have short certificates, equivalently complements of NP languages. Failing to find an answer is not itself a no-certificate.

Lab 29 · Trade a frontier for a stack
Try this firstIncrease branching and depth. Compare a breadth-first frontier with a depth-first active path.
These are scale models that expose the resource trade-off before method selection.
Lab 30 · Ask which side carries the certificate
Try this firstCompare a colouring, a clash and an unsatisfiable formula. Identify what a checker receives.
Whether NP equals coNP remains unknown.
Where PSPACE enters

PSPACE allows polynomial memory even when time is much larger. Games and quantified formulas alternate choices. The Complexity Classes course develops this map.

A search uses little memory. Does that prove it is fast?
No. A small bound on one resource does not force a small bound on another.
Step 16

Hardness tells you which promise to change

NP-hard does not mean give up. Engineering can restrict the input, accept an approximation with a proved ratio, isolate a small parameter, spend exponential time on modest instances, or use a heuristic and measure it without calling the answer optimal.

A parameterised method may take f(k)nc time: expensive in a small parameter k but polynomial in total size. Randomised methods need a failure probability. Approximation needs a bound. Heuristics need diverse benchmarks and feasibility checks.

Lab 31 · Choose a useful contract
Try this firstOpen each project constraint. Match it to exact, parameterised, approximate or measured heuristic work.
Required guarantee, scale and deadline choose the method.
Lab 32 · Attach evidence to a claim
Try this firstCompare four result statements. Separate a candidate, approximation guarantee, closed gap and benchmark.
Check status, tolerances, bound and candidate in the original model.
Special cases can be the right specification

Interval graphs, planar graphs, bounded treewidth and metric distances can change what is solvable or approximable. Restricting inputs is legitimate when the real system guarantees that structure and validates it at the boundary.

A heuristic found a valid tour of length 500. What does that alone prove?
Achievable. A bound is needed before calling it optimal or approximate.
Step 17

Modern solvers search better, but checkers still settle the claim

SAT, SMT, constraint-programming and mixed-integer solvers combine propagation, conflict learning, cuts, symmetry handling, restarts and parallel search. They solve many industrial instances without changing the worst-case class.

Learned models can rank branches, propose cuts, generate candidates or predict settings. Treat them as search guides. A deterministic checker should validate the assignment, proof log, schedule, code or bound. Include inference cost and a fallback.

Lab 33 · Put a learned guide inside a solver
Try this firstToggle helpful and misleading advice. See what changes in search order and what remains the checker’s job.
Poor advice should cost time, not correctness.
Lab 34 · Build a reproducible solver report
Try this firstAdd each missing record. Include instance hash, limits, versions, seed, candidate, bound and checked result.
Reproducibility lets another engineer repeat exactly what was and was not proved.
Continue into the advanced map

Continue with Complexity Classes, Algorithm Design and Quantum Algorithms. Quantum computation does not decide halting.

A learned model proposes a satisfying assignment. What establishes correctness?
Run the checker. Advice affects search time; clause evaluation checks the answer.

What you can do now

  • Write a rule table that computes something, and step it to find out why it does not.
  • Explain why a program can be data, and why that makes general-purpose computers possible.
  • Walk the halting argument from memory, and spot the two places people usually mangle it.
  • Say precisely what undecidable means, and correct the four usual misreadings.
  • Tell a polynomial method from an exponential one by doubling the input and watching the ratio.
  • State what P, NP and NP-complete are, and get the direction of a reduction right.
  • Distinguish deciders, recognisers and enumerators, and explain dovetailing.
  • Apply Rice’s theorem without confusing syntax with behaviour.
  • Audit encodings, time, space, certificates and practical solver claims.

Where this goes

  • Algorithm Design. Exact search, approximation, parameters, heuristics and certificates when the deadline is real.
  • Complexity Classes. Space, randomness, counting, interactive proofs and the wider class map.
  • Language Engineering. How computability limits shape the promises a compiler and analyser can make.