Discrete-Time Models and the Logistic Map

Many organisms breed in discrete seasons, and many diseases are tracked generation by generation, so their dynamics are naturally written as a rule mapping this year’s state to next year’s. These maps are simple to iterate yet capable of astonishingly rich behavior — including deterministic chaos from a one-line equation.

Bifurcation diagram of the logistic map: as  increases the attractor period-doubles into chaos.
Figure 1. Bifurcation diagram of the logistic map: as rr increases the attractor period-doubles into chaos.

Geometric growth#

The discrete analogue of exponential growth is geometric growth, Nt+1=λNt,N_{t+1}=\lambda N_t, which generates the sequence Nt=λtN0N_t=\lambda^t N_0. Here λ\lambda is the finite (per-generation) growth rate; it links to the continuous intrinsic rate by λ=er\lambda=e^{r}. The population grows when λ>1\lambda>1, shrinks when λ<1\lambda<1, and holds steady when λ=1\lambda=1.

The Ricker model#

Real populations are density-dependent, and the Ricker model builds crowding into a discrete map, Nt+1=Nter(1Nt/K).N_{t+1}=N_t\,e^{r(1-N_t/K)}. When NtN_t is small the exponent is near rr and growth is nearly geometric; as NtN_t approaches the carrying capacity KK the exponent goes to zero and Nt+1NtN_{t+1}\approx N_t. Unlike the smooth continuous logistic, overshoot in the Ricker map can produce oscillations and, for large rr, chaos.

The logistic map#

The most famous discrete model is the logistic map, xt+1=rxt(1xt),x_{t+1}=r\,x_t(1-x_t), where xt[0,1]x_t\in[0,1] is a scaled population fraction and rr is a growth parameter (here 0r40\le r\le 4). Robert May’s 1976 analysis of this map showed that even a trivial nonlinear rule can produce cycles and chaos, reshaping how ecologists think about complexity and predictability.

Fixed points and stability#

A fixed point xx^* satisfies x=f(x)x^*=f(x^*) with f(x)=rx(1x)f(x)=rx(1-x). Solving x=rx(1x)x^*=rx^*(1-x^*) gives two fixed points: x=0x^*=0 and x=11/rx^*=1-1/r (the latter positive only when r>1r>1). Stability of a discrete map depends on the slope at the fixed point: xx^* is stable when f(x)<1.|f'(x^*)|<1. Here f(x)=r(12x)f'(x)=r(1-2x). At x=0x^*=0, f(0)=rf'(0)=r, so the origin is stable only for r<1r<1. At x=11/rx^*=1-1/r, f(x)=r(12(11/r))=2rf'(x^*)=r\bigl(1-2(1-1/r)\bigr)=2-r, so this nonzero fixed point is stable when 2r<1|2-r|<1, i.e. for 1<r<31<r<3.

Period-doubling and chaos#

As rr increases past 33 the fixed point loses stability and a stable 2-cycle appears; past 3.449\approx 3.449 a 4-cycle; then 8, 16, and so on, with the thresholds bunching up in a period-doubling cascade. Beyond r3.569r\approx 3.569 the dynamics become chaotic — bounded, aperiodic, and sensitive to initial conditions. Each loss of stability is a bifurcation, and plotting the long-run attractor against rr produces the classic bifurcation diagram.

Cobweb plots#

A cobweb plot visualizes iteration: draw y=f(x)y=f(x) and the line y=xy=x, then bounce between them (up to the curve, across to the diagonal) to trace the orbit. Converging staircases spiral into a stable fixed point; diverging ones reveal cycles or chaos.

A worked example#

Take the logistic map with r=2.5r=2.5. The nonzero fixed point is x=11/r=11/2.5=10.4=0.6x^*=1-1/r=1-1/2.5=1-0.4=0.6. Its stability slope is f(x)=2r=22.5=0.5f'(x^*)=2-r=2-2.5=-0.5, and since 0.5<1|-0.5|<1 the fixed point is stable — orbits converge to 0.60.6. Now take r=3.2r=3.2: the fixed point x=11/3.2=0.6875x^*=1-1/3.2=0.6875 has slope 23.2=1.22-3.2=-1.2, and 1.2>1|-1.2|>1, so it is unstable; the map instead settles onto a stable 2-cycle.

In code#

We iterate the map and build the bifurcation diagram by plotting late iterates over a range of rr.

R#

R
logmap <- function(x, r) r * x * (1 - x)

# converge to the fixed point 0.6 when r = 2.5
x <- 0.1
for (i in 1:100) x <- logmap(x, 2.5)
round(x, 6)   # 0.6

# bifurcation diagram
rs <- seq(2.8, 4.0, length.out = 600)
plot(NULL, xlim = range(rs), ylim = c(0, 1), xlab = "r", ylab = "x")
for (r in rs) {
  x <- 0.2
  for (i in 1:300) x <- logmap(x, r)          # transient
  for (i in 1:200) { x <- logmap(x, r); points(r, x, pch = ".") }
}

Python#

Python
import numpy as np

def logmap(x, r):
    return r * x * (1 - x)

x = 0.1
for _ in range(100):
    x = logmap(x, 2.5)
print(round(x, 6))   # 0.6  -> stable fixed point

# bifurcation diagram data
rs = np.linspace(2.8, 4.0, 600)
R, X = [], []
for r in rs:
    x = 0.2
    for _ in range(300):        # discard transient
        x = logmap(x, r)
    for _ in range(200):
        x = logmap(x, r)
        R.append(r); X.append(x)
# plt.plot(R, X, ',k')  -> period-doubling route to chaos
0.6

Julia#

Julia
logmap(x, r) = r * x * (1 - x)

x = 0.1
for _ in 1:100
    x = logmap(x, 2.5)
end
round(x, digits = 6)   # 0.6

rs = range(2.8, 4.0; length = 600)
R = Float64[]; X = Float64[]
for r in rs
    x = 0.2
    for _ in 1:300; x = logmap(x, r); end       # transient
    for _ in 1:200
        x = logmap(x, r)
        push!(R, r); push!(X, x)
    end
end
# scatter(R, X; markersize = 0.5)  -> bifurcation diagram

Why it matters#

Discrete maps are the right tool for seasonally breeding populations and generation-based disease models, and they force us to check stability with the slope condition f(x)<1|f'(x^*)|<1 rather than the sign of a derivative. The logistic map is a canonical warning that simple, fully deterministic ecological rules can generate irregular, effectively unpredictable dynamics — a lesson that carries directly into forecasting outbreaks and managed populations.