1 Το πρόβλημα: μια λίμνη με νούφαρα

Φανταστείτε ότι φτάνουμε σε μια λίμνη και μετράμε τα νούφαρα.

  • Ημέρα 0: \(N_0 = 100\) νούφαρα
  • Ημέρα 1: \(N_1 = 110\) νούφαρα

Επιστρέφοντας την επόμενη μέρα παρατηρούμε αύξηση 10%. Ο στόχος μας είναι:

Πώς, ξεκινώντας από αυτή την απλή παρατήρηση, καταλήγουμε στον τύπο της εκθετικής αύξησης μέσω μιας διαφορικής εξίσωσης;

Το ταξίδι έχει τρεις σταθμούς:

  1. Παρατήρηση → ρυθμός μεταβολής (διακριτή σκέψη)
  2. Διακριτό → συνεχές (παίρνουμε το όριο και γράφουμε τη διαφορική εξίσωση \(\frac{dN}{dt} = rN\))
  3. Επίλυση της διαφορικής (χωρισμός μεταβλητών → \(N(t) = N_0 e^{rt}\))

2 Από την παρατήρηση στον ρυθμό μεταβολής

2.1 Η μεταβολή του πληθυσμού

Σε χρόνο \(\Delta t = 1\) ημέρα, η μεταβολή του πληθυσμού είναι:

\[ \Delta N \;=\; N_1 - N_0 \;=\; 110 - 100 \;=\; 10 \]

2.2 Ο κατά κεφαλήν (per capita) ρυθμός \(r\)

Δεν μας ενδιαφέρει η απόλυτη μεταβολή (10 νούφαρα), γιατί αυτή εξαρτάται από το πόσα νούφαρα είχαμε στην αρχή. Μας ενδιαφέρει πόσο μεταβλήθηκε ο πληθυσμός ανά άτομο, ανά μονάδα χρόνου:

\[ r \;=\; \frac{\Delta N}{N \cdot \Delta t} \;=\; \frac{10}{100 \cdot 1} \;=\; 0{,}1 \;=\; 10\%/\text{ημέρα} \]

Το \(r\) ονομάζεται εγγενής (intrinsic) ρυθμός αύξησης του πληθυσμού. Είναι η βασική παράμετρος όλης της κλασικής πληθυσμιακής δυναμικής.

# Υπολογισμός του r από τα δεδομένα
N0 <- 100
N1 <- 110
dt <- 1   # μέρες

r <- (N1 - N0) / (N0 * dt)
r
## [1] 0.1

3 Από το διακριτό στο συνεχές

3.1 Διακριτή εξίσωση

Αν υποθέσουμε ότι αυτός ο ρυθμός παραμένει σταθερός, τότε σε κάθε χρονικό βήμα \(\Delta t\):

\[ \Delta N \;=\; r \, N \, \Delta t \quad\Longleftrightarrow\quad \frac{\Delta N}{\Delta t} \;=\; r N \]

3.1.1 παράδειγμα τιμών για r=0.1 (10%)

N ΔΝ/Δt
100 10
200 20
1000 100
5000 500

Αυτή είναι μια διακριτή προσέγγιση: μετράμε τον πληθυσμό σε διακριτά βήματα (κάθε μέρα).

3.2 Το όριο \(\Delta t \to 0\)

Όμως τα νούφαρα δεν περιμένουν την αυγή για να αυξηθούν — αυξάνονται συνεχώς. Παίρνοντας το όριο όταν το χρονικό βήμα γίνεται απειροελάχιστο:

\[ \lim_{\Delta t \to 0} \frac{\Delta N}{\Delta t} \;=\; \frac{dN}{dt} \]

Καταλήγουμε στη διαφορική εξίσωση της εκθετικής αύξησης:

\[ \boxed{\;\frac{dN}{dt} \;=\; r\,N\;} \]

Διαισθητική ερμηνεία: Ο ρυθμός μεταβολής του πληθυσμού σε κάθε χρονική στιγμή είναι ανάλογος του τρέχοντος μεγέθους του πληθυσμού. Όσα περισσότερα νούφαρα, τόσο πιο γρήγορα εμφανίζονται καινούρια.


4 Επίλυση της διαφορικής εξίσωσης

Θα λύσουμε τη \(\frac{dN}{dt} = rN\) με τη μέθοδο του χωρισμού μεταβλητών.

4.1 Βήμα 1 — Χωρίζουμε τις μεταβλητές

\[ \frac{dN}{N} \;=\; r\,dt \]

(Όλα τα \(N\) στο αριστερό μέλος, όλα τα \(t\) στο δεξί.)

4.2 Βήμα 2 — Ολοκληρώνουμε και τα δύο μέλη

\[ \int \frac{dN}{N} \;=\; \int r\,dt \quad\Longrightarrow\quad \ln(N) \;=\; r\,t + C \]

όπου \(C\) είναι σταθερά ολοκλήρωσης.

4.3 Βήμα 3 — Λύνουμε ως προς \(N\)

\[ N(t) \;=\; e^{rt + C} \;=\; e^{C} \cdot e^{rt} \]

4.4 Βήμα 4 — Βρίσκουμε τη σταθερά από την αρχική συνθήκη

Στο \(t = 0\) έχουμε \(N(0) = N_0\), οπότε:

\[ N_0 \;=\; e^{C} \cdot e^{0} \;=\; e^{C} \quad\Longrightarrow\quad e^{C} = N_0 \]

4.5 Ο τελικός τύπος

\[ \boxed{\;N(t) \;=\; N_0\, e^{r t}\;} \]

Αυτός είναι ο τύπος της εκθετικής αύξησης.


5 Υλοποίηση στην R

5.1 Αναλυτική λύση

Ας σχεδιάσουμε την \(N(t) = 100 \cdot e^{0{,}1\,t}\) για 60 ημέρες:

N0 <- 100
r  <- 0.1
t  <- seq(0, 60, by = 0.1)

N_analytical <- N0 * exp(r * t)

df_analytical <- data.frame(t = t, N = N_analytical)

ggplot(df_analytical, aes(x = t, y = N)) +
  geom_line(linewidth = 1, colour = "steelblue") +
  labs(
    title = "Εκθετική αύξηση πληθυσμού νούφαρων",
    subtitle = expression(paste(N(t) == N[0] * e^{r*t}, ",   ",
                                N[0] == 100, ",   ", r == 0.1)),
    x = "Χρόνος (ημέρες)",
    y = "Πληθυσμός N(t)"
  )

Σε 60 ημέρες ο πληθυσμός φτάνει τα 4.0343^{4} νούφαρα — μη ρεαλιστικό για μια πεπερασμένη λίμνη! Αυτό είναι το πρόβλημα της εκθετικής αύξησης που θα αντιμετωπίσουμε αργότερα με το λογιστικό μοντέλο.

5.2 Αριθμητική λύση με deSolve

Πολύ συχνά στη βιολογία οι διαφορικές εξισώσεις δεν λύνονται αναλυτικά. Τότε χρησιμοποιούμε αριθμητικούς ολοκληρωτές. Για το συγκεκριμένο μοντέλο, που το ξέρουμε ήδη αναλυτικά, αυτό μας δίνει μια ευκαιρία να ελέγξουμε αν η αριθμητική μέθοδος συμφωνεί.

# 1. Ορίζουμε τη διαφορική εξίσωση ως συνάρτηση
exponential_model <- function(t, state, parameters) {
  with(as.list(c(state, parameters)), {
    dN <- r * N      # dN/dt = rN
    list(dN)
  })
}

# 2. Παράμετροι, αρχική συνθήκη, χρόνος
parameters <- c(r = 0.1)
state      <- c(N = 100)
times      <- seq(0, 60, by = 0.1)

# 3. Επίλυση
out <- ode(y = state, times = times, func = exponential_model, parms = parameters)
out_df <- as.data.frame(out)

head(out_df)

5.3 Σύγκριση αναλυτικής και αριθμητικής λύσης

df_compare <- data.frame(
  t          = times,
  Αναλυτική  = N0 * exp(r * times),
  Αριθμητική = out_df$N
)

ggplot(df_compare, aes(x = t)) +
  geom_line(aes(y = Αναλυτική),  linewidth = 1.2, colour = "steelblue") +
  geom_point(aes(y = Αριθμητική),
             data = df_compare[seq(1, nrow(df_compare), by = 50), ],
             colour = "tomato", size = 2) +
  labs(
    title = "Αναλυτική (γραμμή) έναντι αριθμητικής (κουκίδες) λύσης",
    x = "Χρόνος (ημέρες)",
    y = "Πληθυσμός N(t)"
  )

# Μέγιστο σφάλμα
max(abs(df_compare$Αναλυτική - df_compare$Αριθμητική))
## [1] 0.008490642

Το σφάλμα είναι αμελητέο — οι δύο λύσεις συμπίπτουν.


6 Μια λεπτή αλλά πολύ σημαντική παρατήρηση

Παρατηρήστε κάτι: ξεκινήσαμε λέγοντας “αύξηση 10% την ημέρα”. Αυτή είναι μια διακριτή δήλωση. Όμως μετά γράψαμε \(r = 0{,}1\) μέσα σε συνεχή διαφορική εξίσωση. Είναι τα δύο όντως ίσα;

6.1 Ο διακριτός τύπος

Αν κάθε μέρα ο πληθυσμός πολλαπλασιάζεται με το \(1{,}10\):

\[ N(t) \;=\; N_0 \cdot (1+r)^{t} \;=\; 100 \cdot 1{,}1^{t} \]

6.2 Σύγκριση των δύο μοντέλων

days <- 0:30
N_discrete   <- 100 * (1 + 0.1)^days
N_continuous <- 100 * exp(0.1 * days)

df_dc <- data.frame(
  day        = rep(days, 2),
  N          = c(N_discrete, N_continuous),
  model      = factor(rep(c("Διακριτό  N0·(1+r)^t",
                            "Συνεχές  N0·exp(rt)"), each = length(days)))
)

ggplot(df_dc, aes(x = day, y = N, colour = model)) +
  geom_line(linewidth = 1) +
  geom_point(size = 1.5) +
  labs(
    title = "Διακριτή έναντι συνεχούς εκθετικής αύξησης",
    subtitle = "Με την ίδια τιμή r = 0,1 τα δύο μοντέλα **δεν** ταυτίζονται",
    x = "Ημέρες", y = "Πληθυσμός", colour = ""
  ) +
  theme(legend.position = "bottom")

data.frame(
  Ημέρα     = c(1, 5, 10, 20, 30),
  Διακριτό  = round(100 * 1.1^c(1, 5, 10, 20, 30), 1),
  Συνεχές   = round(100 * exp(0.1 * c(1, 5, 10, 20, 30)), 1)
)

6.3 Γιατί διαφέρουν;

Στο διακριτό μοντέλο, “10% αύξηση/ημέρα” σημαίνει: στο τέλος της μέρας ο πληθυσμός είναι 110% του αρχικού.

Στο συνεχές μοντέλο με \(r = 0{,}1\), ο στιγμιαίος ρυθμός είναι 10%, αλλά επειδή τα νεογέννητα νούφαρα παράγουν αμέσως κι αυτά νέα νούφαρα (συνεχής ανατοκισμός!), στο τέλος της μέρας έχουμε:

\[ N(1) \;=\; 100 \cdot e^{0{,}1} \;\approx\; 110{,}52 \]

δηλαδή λίγο παραπάνω από 10% αύξηση.

6.4 Πώς συνδέονται;

Αν θέλουμε το συνεχές μοντέλο να δίνει ακριβώς 10% αύξηση ανά μέρα, χρειαζόμαστε:

\[ e^{r_{\text{cont}}} = 1{,}1 \quad\Longrightarrow\quad r_{\text{cont}} \;=\; \ln(1{,}1) \;\approx\; 0{,}0953 \]

log(1.1)   # στο R, log() είναι ο φυσικός λογάριθμος (ln)
## [1] 0.09531018

Στην οικολογική πράξη, τις περισσότερες φορές δουλεύουμε στο συνεχές μοντέλο και θεωρούμε \(r\) τον στιγμιαίο ρυθμό. Η διαφορά είναι μικρή για μικρά \(r\), αλλά καλό είναι να ξέρετε ότι υπάρχει.


7 Λογαριθμικός άξονας: το διαγνωστικό εργαλείο του βιολόγου

Παίρνοντας λογάριθμο στον τύπο \(N(t) = N_0 e^{rt}\):

\[ \ln N(t) \;=\; \ln N_0 \;+\; r\,t \]

Δηλαδή ο \(\ln N\) είναι γραμμική συνάρτηση του \(t\) με κλίση ίση με \(r\). Αυτό είναι εξαιρετικά χρήσιμο πρακτικά:

Αν τα δεδομένα σας σε ημι-λογαριθμικό διάγραμμα (\(\ln N\) ως προς \(t\)) σχηματίζουν ευθεία, τότε ο πληθυσμός αυξάνεται εκθετικά, και η κλίση της ευθείας σας δίνει το \(r\).

ggplot(df_analytical, aes(x = t, y = N)) +
  geom_line(linewidth = 1, colour = "steelblue") +
  scale_y_log10() +
  labs(
    title = "Εκθετική αύξηση σε ημι-λογαριθμικό άξονα",
    subtitle = "Η ευθεία γραμμή υποδηλώνει εκθετική αύξηση",
    x = "Χρόνος (ημέρες)",
    y = "log10(N(t))"
  )

7.1 Εκτίμηση του \(r\) από δεδομένα με γραμμική παλινδρόμηση

# Προσομοιωμένα δεδομένα με λίγο θόρυβο
set.seed(42)
sim_days <- 0:30
sim_N    <- 100 * exp(0.1 * sim_days) * exp(rnorm(length(sim_days), 0, 0.05))

# Γραμμική παλινδρόμηση log(N) ~ t
fit <- lm(log(sim_N) ~ sim_days)
summary(fit)$coefficients
##               Estimate  Std. Error   t value     Pr(>|t|)
## (Intercept) 4.63477789 0.021337509 217.21270 3.966003e-48
## sim_days    0.09829637 0.001221782  80.45327 1.212714e-35
cat("Εκτιμώμενο r =", round(coef(fit)[2], 4),
    "  (πραγματικό r = 0.1)\n")
## Εκτιμώμενο r = 0.0983   (πραγματικό r = 0.1)
cat("Εκτιμώμενο N0 =", round(exp(coef(fit)[1]), 2),
    "  (πραγματικό N0 = 100)\n")
## Εκτιμώμενο N0 = 103.01   (πραγματικό N0 = 100)

8 Χρόνος διπλασιασμού

Μια χρήσιμη ποσότητα: σε πόσο χρόνο διπλασιάζεται ο πληθυσμός;

\[ 2 N_0 = N_0 e^{r T_2} \;\Longleftrightarrow\; T_2 = \frac{\ln 2}{r} \]

T2 <- log(2) / r
cat("Χρόνος διπλασιασμού των νούφαρων:", round(T2, 2), "ημέρες\n")
## Χρόνος διπλασιασμού των νούφαρων: 6.93 ημέρες

Άρα τα νούφαρα διπλασιάζονται περίπου κάθε 7 μέρες.


9 Ασκήσεις

  1. Παίξτε με το \(r\). Σχεδιάστε στο ίδιο διάγραμμα την εκθετική αύξηση για \(r = 0{,}05\), \(r = 0{,}1\) και \(r = 0{,}2\). Πώς αλλάζει ο χρόνος διπλασιασμού;

  2. Αρνητικό \(r\). Τι συμβαίνει αν \(r < 0\); Σχεδιάστε την περίπτωση \(r = -0{,}05\) και ερμηνεύστε βιολογικά.

  3. Εκτίμηση από πραγματικά δεδομένα. Σας δίνονται οι παρακάτω μετρήσεις:

    day <- c(0, 3, 7, 14, 21, 28)
    N   <- c(50, 73, 109, 218, 437, 870)

    Εκτιμήστε το \(r\) με γραμμική παλινδρόμηση στο \(\ln N\) και προβλέψτε τον πληθυσμό στις 60 ημέρες.

  4. Ο μαθηματικός κίνδυνος. Με \(r = 0{,}1\) και \(N_0 = 100\) νούφαρα, σε πόσες μέρες τα νούφαρα θα ξεπεράσουν τον αριθμό των ατόμων στη Γη (\(\approx 8 \times 10^{9}\)); Τι μας λέει αυτό για τους περιορισμούς του εκθετικού μοντέλου;


10 Επόμενο βήμα: ο νόμος επιστρέφει στην πραγματικότητα

Το εκθετικό μοντέλο δεν μπορεί να ισχύει για πάντα. Σε μια πραγματική λίμνη υπάρχουν περιορισμοί: χώρος, φως, θρεπτικά συστατικά. Σε κάποιο σημείο ο πληθυσμός θα κορεστεί.

Στο επόμενο μάθημα θα τροποποιήσουμε τη διαφορική εξίσωση ώστε να συμπεριλάβει τη φέρουσα ικανότητα \(K\) του περιβάλλοντος:

\[ \frac{dN}{dt} \;=\; r N \left(1 - \frac{N}{K}\right) \]

Αυτή είναι η λογιστική εξίσωση — και είναι το θέμα του επόμενου εργαστηρίου.


11 Στοιχεία συνεδρίας R

sessionInfo()
## R version 4.6.1 (2026-06-24)
## Platform: x86_64-pc-linux-gnu
## Running under: Ubuntu 24.04.4 LTS
## 
## Matrix products: default
## BLAS:   /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.12.0 
## LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.12.0  LAPACK version 3.12.0
## 
## locale:
##  [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
##  [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
##  [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
##  [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
##  [9] LC_ADDRESS=C               LC_TELEPHONE=C            
## [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       
## 
## time zone: Europe/Athens
## tzcode source: system (glibc)
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
## [1] deSolve_1.42  ggplot2_4.0.3
## 
## loaded via a namespace (and not attached):
##  [1] vctrs_0.7.3        cli_3.6.6          knitr_1.51         rlang_1.2.0       
##  [5] xfun_0.57          otel_0.2.0         S7_0.2.2           jsonlite_2.0.0    
##  [9] labeling_0.4.3     glue_1.8.1         htmltools_0.5.9    sass_0.4.10       
## [13] scales_1.4.0       rmarkdown_2.31     grid_4.6.1         evaluate_1.0.5    
## [17] jquerylib_0.1.4    fastmap_1.2.0      yaml_2.3.12        lifecycle_1.0.5   
## [21] compiler_4.6.1     RColorBrewer_1.1-3 farver_2.1.2       digest_0.6.39     
## [25] R6_2.6.1           bslib_0.10.0       tools_4.6.1        withr_3.0.2       
## [29] gtable_0.3.6       cachem_1.1.0