Logistic Growth Model: The Limits of Population Growth
Model
$$ \dot{N} = {{ r } \over { K }} N ( K - N) $$
Variables
- $N(t)$: Represents the population size of a group at time $t$.
Parameters
- $r \in \mathbb{R}$ : Intrinsic Rate of Increase, growth occurs if it is greater than $0$, and decline occurs if it is less than $0$. It can also be defined by the difference $r:=b-d$ between the Birth Rate $b$ and Death Rate $d$.
- $K$: Carrying Capacity, describes the size of the environment that can support the population.
Description
This differential equation is also known as the Logistic Equation or Verhulst’s Equation.
The logistic growth model was designed to improve on the Malthusian growth model, which only predicts unrealistic perpetual increase in population. The more significant the population, the more it penalizes population growth, akin to adding a sort of Malthusian Trap to the equation. If the population size $N$ exceeds the carrying capacity $K$, then $(K-N) < 0$, resulting in a negative change $n '$ in population size, meaning the population will decrease. This idea is not merely speculative; let’s directly examine the derivation to understand how such a penalty works.
Derivation
Malthusian Growth Model: $$ \dot{N} = r N $$
In such a simple model, the average growth rate per individual can be obtained by dividing both sides by $N$. Since the growth rate in the exponential growth model is constant regardless of how large the population grows, it remains a constant like this:
$$ {{ \dot{N} } \over { N }} = r $$
Now, let’s modify the RHS. We want the growth rate to decrease as the population increases, but there’s no clear consensus on how exactly it should decrease, so let’s assume it decreases linearly for simplicity. That means the individual growth rate will be depicted by a straight line, not a constant function.
As shown in the figure, modifying the RHS so that the equation of the line with the y-intercept of $K$ and the $N’/N$ intercept of $r$ results in
$$ {{ \dot{N} } \over { N }} = {{ r } \over { K }} ( K - N) $$
Multiplying both sides by $N$ gives us
$$ \dot{N} = {{ r } \over { K }} N ( K - N) $$
■
Fixed Points
The logistic growth model has two fixed points, $N=0, N=K$, with $N=K$ being Lyapunov stable1.
Visual Understanding
Let’s visually understand the logistic growth model through a grid-based simulation.
Actions
(In every cell, each turn)
- Calculation: Count how many elements there are in the up, down, left, right of each cell. Let the number calculated in the $i$ row $j$ column be $n_{ij}$.
- Diffusion: Each cell has a probability of $1-(1-\beta)^{n_{ij}}$ to contain an element.
Using only the simple action of diffusion in a grid space, one can see natural limits to growth due to the grid space being finite.
Here is the comparison with the theoretical growth trend.
As seen, the simulation does not match the theory exactly; it tends to grow linearly rather than curving, with initial growth too steep. However, it’s essential to notice the inflection point where it starts to fill the environmental capacity. If we compare this to real life, it can be like bacteria growing in a petri dish. Even if they want to keep growing, they can’t grow indefinitely beyond the confines of the dish.
The reason for the discrepancy between theory and simulation is that the simulation isn’t sophisticated enough, and the model is too simple. Perhaps it was just bad luck in the simulation, but the impact of the grid cannot be ignored. However, in this case, it’s not merely about luck.
In fact, when comparing theoretical trends, this visualization appears closer to the Gompertz growth model. Considering tumor growth in grid space, the ratio of volume to surface area appears smaller, and although cell numbers increase, the cells actually capable of increasing the size of the tumor gradually diminish.
Code
Here is the Julia code to create the GIFs.
cd(@__DIR__) # 파일 저장 경로
@time using Plots
@time using Random
@time using DifferentialEquations
row_size = 20
column_size = 20
β = 0.1 # 번식률
γ = 0.1 # 사망률
#---
K = row_size*column_size
function logistic_growth!(du,u,p,t)
N = u[1]
r = p
du[1] = dN = r/K*N*(K-N)
end
u0 = [1.0]
p = 0.8
tspan = (0.,18)
prob = ODEProblem(logistic_growth!,u0,tspan,p)
sol = solve(prob; saveat=(0.0:0.1:18))
compare = plot(sol,vars=(0,1),
linestyle = :dash, color = :black,
size = (400,300), label = "Theoretical", legend=:topleft)
#---
max_iteration = 180
Random.seed!(0);
time_evolution = Int64[]
stage_lattice = zeros(Int64, row_size, column_size)
stage_lattice[rand(1:row_size), rand(1:column_size)] = 1
let colormap_SI = cgrad([colorant"#EEEEEE", colorant"#111111"])
anim1 = @animate for t = (0:max_iteration)/10
heatmap(reverse(stage_lattice,dims=1), color=colormap_SI,
xaxis=false,yaxis=false,axis=nothing, size = [400,400], legend = false)
I_lattice = (stage_lattice .== 1)
count_lattice =
vcat(I_lattice[2:end, :], zeros(Int64, 1, column_size)) +
vcat(zeros(Int64, 1, column_size), I_lattice[1:end-1, :]) +
hcat(I_lattice[:, 2:end], zeros(Int64, row_size, 1)) +
hcat(zeros(Int64, column_size, 1), I_lattice[:, 1:end-1])
probability_lattice = 1 .- (1-β).^count_lattice
hit_lattice = probability_lattice .> rand(row_size, column_size)
stage_lattice[hit_lattice] .= 1
if sum(stage_lattice) ≥ row_size*column_size
colormap_SI = cgrad([colorant"#111111", colorant"#EEEEEE"])
end
push!(time_evolution, sum(stage_lattice))
end; gif(anim1, "logistic_growth_lattice.gif", fps = 12)
end
anim2 = @animate for t = 1:max_iteration
compare = plot(sol,vars=(0,1),
linestyle = :dash, color = :black,
label = "Theoretical", legend=:bottomright)
plot!(compare, 0.1:0.1:(t/10), time_evolution[1:t],
color = colorant"#111111", linewidth = 2, label = "Simulation",
size = [400, 300])
end; gif(anim2, "logistic_growth_time_evolution.gif", fps = 12)
From the diagrams used in the derivation, we can see that to the left of the fixed point, the population increases and moves to the right, while to the right, it decreases, moving to the left. ↩︎