Quant Memo
Core

Detecting Cycles in an Iterated Sequence

How to tell, using almost no extra memory, whether a sequence generated by repeatedly applying a function eventually loops back on itself — the tortoise-and-hare technique.

Prerequisites: Recursion and the Call Stack, How to Crack a What-Comes-Next Question

Define x0=2x_0 = 2 and xn+1=(xn2+3)mod20x_{n+1} = (x_n^2 + 3) \bmod 20, and ask: does this sequence ever repeat a value? With only 20 possible remainders, it must eventually repeat by pigeonhole — but where does the loop start, and how long is it? Storing every value you've seen and checking for repeats works, but it costs memory proportional to how far you go. There's a classic trick that finds the cycle using only two pointers and no memory of history at all.

The shape of an eventually-periodic sequence

Once a deterministic sequence xn+1=f(xn)x_{n+1} = f(x_n) repeats any value, it's trapped: from that point on it must retrace exactly the same steps forever, because ff always produces the same output from the same input. So the sequence looks like a "rho" shape — a straight run-in of length μ\mu (the tail), followed by a loop of length λ\lambda (the cycle) that it circles forever after.

Floyd's cycle detection ("tortoise and hare")

Run two pointers through the sequence at different speeds: the tortoise moves one step per iteration, tf(t)t \leftarrow f(t); the hare moves two steps, hf(f(h))h \leftarrow f(f(h)). Both start at x0x_0. Because the hare gains one extra step on the tortoise every iteration, and both are eventually confined to a loop of finite length λ\lambda, the hare is guaranteed to lap the tortoise and land on exactly the same value within one full loop — so if a cycle exists, the two pointers must meet. In plain English: imagine two runners on a circular track who never leave the track once they enter it — the faster one, going twice the speed, is bound to catch up to the slower one from behind, no matter where either started.

Worked example: tracing the sequence by hand

x0=2x_0=2: x1=(4+3)mod20=7x_1 = (4+3)\bmod 20 = 7, x2=(49+3)mod20=12x_2 = (49+3)\bmod20 = 12, x3=(144+3)mod20=7x_3=(144+3)\bmod20=7. Already x3=x1=7x_3 = x_1 = 7 — the sequence hit 7 again after just one extra step, so the tail is x0=2x_0=2 alone (μ=1\mu=1) and the cycle is 71277 \to 12 \to 7 \to \cdots, length λ=2\lambda = 2. Run the tortoise/hare check: tortoise at x0=2x_0=2, hare at x0=2x_0=2. Step 1: tortoisex1=7\to x_1=7, harex2=12\to x_2=12 (two steps: 27122\to7\to12). Not equal. Step 2: tortoisex2=12\to x_2=12, harex4=f(f(12))=f(7)=12\to x_4 = f(f(12)) = f(7) = 12 (since x3=7x_3=7, x4=12x_4=12). Tortoise is at 12, hare is at 12 — they meet, confirming a cycle exists, found in just two iterations without ever storing a list of past values.

tail (μ steps) cycle (λ steps) x0 entry point tortoise/hare meet here
Every eventually-periodic sequence has this rho shape: a straight tail leading into a loop. The tortoise and hare are mathematically guaranteed to meet somewhere on the loop.

What this means in practice

This exact two-pointer trick, at constant memory, is used to detect loops in linked-list-style structures generally, and the underlying pigeonhole reasoning — a process with finitely many states run forward indefinitely must repeat a state — is the same argument behind guaranteeing that a deterministic pseudo-random generator or a discretized mean-reverting process with finite precision eventually cycles.

A deterministic sequence over a finite set of possible values must eventually repeat, producing a tail followed by a cycle. Floyd's tortoise-and-hare algorithm finds this using two pointers moving at speeds 1 and 2, guaranteed to meet inside the cycle using only constant extra memory.

Related concepts

Practice in interviews

Further reading

  • Knuth, The Art of Computer Programming, vol. 2, sec. 3.1
ShareTwitterLinkedIn