Modelling density dependent (logistic) population growth

Last updated: 7 November 2018

Let's derive some more population growth functions!

In a previous post we derived a function for population growth based on the vital rates of reproduction and mortality. We assumed that the growth rate was constant with respect to the number of individuals in the population or . This led to the unrealistic prediction that populations will grow indefinitely. Of course, populations will eventually run into resource problems (e.g. no food) or other density dependent issues (e.g. over-crowding and disease).

One way to set un upper limit on population growth is to scale the growth rate by a function that is 1 when is small and 0 when is big. Here is one such function:

When is small the growth rate is approximately but as the growth rate scales to zero. Thus can be thought of as the carrying capacity of the population. It is important to note that unlike the equation for unconstrained exponential growth, this is a function of convenience rather than having mechanistic interpretation based on how reproduction or mortality depend on population density - it is the simplest function that satisfies our requirements for density dependent growth.

Numerical solution

Before we solve this equation with maths (analytically), let's solve it using computers (numerically) for , and assuming the population started with just 2 individuals.

library(tidyverse)
library(deSolve)
parameters <- c(r = 0.3, K = 100)
state <- c(N = 2)
logistic <- function(t, state, parameters) {{
  with(as.list(c(state, parameters)), {{
    dN <-  (1 - N / K) * (r * N )
    list(dN)
  }})
}}
times <- seq(0, 50, by = 0.2)
sol = as.data.frame(
  ode(y = state, times = times, func = logistic, parms = parameters)
)
 
ggplot(sol) + 
  geom_line(aes(time, N), col = 'red')+
   theme_minimal() +
  theme(
    plot.background = element_rect(fill = rgb(.2,.21,.27)),
    text = element_text(colour = 'grey'), 
    axis.text = element_text(colour = 'grey'), 
    panel.grid = element_line(colour = 'grey')
  )

Logistic equation solved numerically

Wow! That was easy.

Analytical solution

Now that we've seen the pretty curve, let's use our excitement to try and describe the curve with a single equation without derivatives (solve the differential equation for as a function of and get rid of the ).

First, separate the variables.

Now we want to split up the fraction into something more manageable so we assume there exists some and such that:

When :

Now substituting and N = 0:

Thus we have:

Now integrate both sides:

The part is a little tricky, but substituting and will help.

From here we can finish the original integration.

As N is strictly positive we can remove the absolute operators. Remembering your logarithm rules we get:

We really just want one so that we can conveniently write the function but for now let's solve the constant at and

Back to solving for .

Substitute in the constant we solved for earlier.

And simplify by multiplying through by

Let plot it!

r = 0.3
K = 100
N0 = 2
time <- seq(0, 50, by = 0.2)
sol = data.frame(
  time,
  N = K*N0/(N0 + (K - N0)*exp(-r*time))
)
 
ggplot(sol) + 
  geom_line(aes(time, N), col = 'red')+
   theme_minimal() +
  theme(
    plot.background = element_rect(fill = rgb(.2,.21,.27)),
    text = element_text(colour = 'grey'), 
    axis.text = element_text(colour = 'grey'), 
    panel.grid = element_line(colour = 'grey')
  )

Logistic equation solved analytically

All that calculus for the same line deSolve gave us...

Source code.