JOVANA
Explore Library Glossary Getting Started Three Levels Fields How it works Mission
Join the mission
All guides

What Is an Algorithm? Problems, Instances, and Outputs

Before we design anything, we need to agree on what we are even talking about. We separate the problem from the algorithm from the program, pin down what an input and its size are, and meet the cost model and the worst/best/average lenses that the rest of this ladder leans on.

A recipe, written carefully enough that a machine could follow it

An algorithm is a finite, unambiguous list of steps that turns an input into the right output. Think of a cooking recipe — but a recipe that never says "add salt to taste" or "cook until done." Every step must be precise enough that a patient clerk with no judgment, or a machine with no intuition, could carry it out and always get the same result. "Finite" matters too: the steps must eventually run out, so the procedure actually stops and hands you an answer rather than looping forever.

Two demands hide inside that one sentence, and we will spend whole rungs on each. First, the steps must lead to the correct output on every allowed input — that is correctness, and proving it (not just testing it) is its own art. Second, the steps must run out — that is termination. An algorithm that gives perfect answers but never finishes is no algorithm at all. For now, hold the picture: precise steps, in, then out, guaranteed to stop.

Problem, algorithm, program: three different things

Beginners blur these three, and untangling them is the single most useful move you make in this rung. A problem is the specification — the contract that says "given THIS kind of input, produce an output satisfying THESE conditions." "Sort a list of numbers into nondecreasing order" is a problem. It says nothing about how. The distinction between problem, algorithm, and program is exactly this: what versus how versus how-on-this-machine.

An algorithm is one chosen method for solving that problem — a particular strategy of steps. Sorting has many: insertion sort, merge sort, quicksort, and more, all correct, all different in cost. A program is an algorithm written in a real language — Python, C, whatever — with all the fiddly details a computer demands. The relationship is one-to-many going down: one problem, many correct algorithms; one algorithm, many faithful programs. We design and analyze at the algorithm level on purpose, because that is where the ideas live and where the answers don't depend on which compiler you used.

Instances and outputs: the problem is the whole family

A problem is not a single question; it is an infinite family of questions sharing one shape. Each concrete filled-in question is a problem instance. For the sorting problem, the list [3, 1, 2] is one instance and [9, 9, 1, 5] is another. The problem is the pattern; an instance is one example you could actually hand to an algorithm. An algorithm earns the word "correct" only if it produces a valid output on every instance the problem allows — not just the three you happened to try.

The output is whatever the contract promises back. Sometimes it is a rearranged list; sometimes a single number; sometimes a yes/no. And here is a subtlety worth meeting now: a problem can have more than one valid output for the same instance. "Find a shortest path" may have two paths tied for shortest — both are correct outputs. So "the answer" is sometimes really "an answer." Keeping problem, instance, and output cleanly separated is what lets us later say sharp things like "this method is correct on all instances but slow on some of them."

Input size: the dial we measure cost against

Cost is meaningless without a yardstick, and the yardstick is input size, almost always written n. It is some honest count of how big the instance is: the number of elements in a list, the number of vertices and edges in a graph, the number of digits in a number. The whole game of analysis is to describe cost as a function of n — "this many steps for an instance of size n" — because that tells us how the method scales as inputs grow, which is the question that actually decides whether something is usable at real scale.

Choosing n honestly takes a little care. For sorting, n is the list length — clear enough. But for "is this number prime?", the right size is the number of digits, not the value itself, because a 20-digit number is genuinely a bigger input than a 2-digit one even though both are "just one number." Picking the wrong n is a classic trap that makes a slow algorithm look fast on paper. We'll meet this digit-versus-value subtlety again much higher up the ladder, when it quietly separates "efficient" from "only-efficient-looking" methods.

Counting steps without a stopwatch

If we timed algorithms with a literal stopwatch, our answers would change with every laptop, language, and background app. To get a machine-independent measure, we count basic steps instead — primitive operations like "compare two numbers," "add," "read or write one memory cell" — and agree that each costs one unit. This abstraction is the RAM model (random-access machine): a simple imagined computer where any memory cell is reachable in one step and each elementary operation takes constant time. It is a deliberate simplification, but a remarkably useful one.

Be honest about what the model hides. It pretends adding two huge numbers costs the same as adding two small ones, and that reaching cache and reaching far-off memory cost the same — neither is literally true on real hardware. But for ranking strategies and seeing how cost grows with n, the RAM model is exactly the right altitude: high enough to ignore the noise, low enough to still count what matters. Guide 4 in this rung pins down precisely what gets to count as one step.

Same size, different luck: worst, best, and average

Even after fixing n, two instances of the same size can cost wildly different amounts. Searching a list for a value might find it at position one (a single step) or only at the very end (n steps). So a cost is not one number per size — it is a spread over all instances of that size, and we summarize that spread through three lenses, captured by the idea of worst, best, and average case.

  1. Worst case: over all instances of size n, the most steps any of them forces. This is the headline measure of the field, because it is a guarantee — "never slower than this" — and a guarantee is what you can build on.
  2. Best case: the fewest steps any size-n instance forces. Usually the least useful lens — it flatters an algorithm with its luckiest input — but it sometimes reveals an early-exit that genuinely helps.
  3. Average case: the typical cost, averaged over instances. Powerful, but with a catch worth stating loudly: "average" only means something once you fix an assumed distribution over inputs. Change what you assume is typical, and the average can change — so an average-case claim is only as honest as the input model behind it.

These three lenses are the payoff of all the careful separating we did above. Because we kept problem, instance, and input size distinct, we can now say something genuinely precise — "on the worst size-n instance this takes at most so many steps" — instead of a vague "it's pretty fast." Guide 5 of this rung takes these three apart properly; for now, just carry the habit of asking "which case am I even talking about?" whenever someone quotes you a running time.