<!-- BUG: FIX PDF Render
HTML interactivo + PDF con “snapshot” automático
Qué haces: generas el HTML y, para el PDF, incluyes una captura (imagen) del estado por defecto del gráfico.
Por qué: la estética coincide con la versión interactiva y no reescribes lógica.
Trade-off: necesitas una pequeña tarea de captura (build step) pero evitas duplicar código.
HTML interactivo + PDF con enlace/QR a la versión viva
Qué haces: en PDF pones una figura fija + link/QR a la página interactiva.
Por qué: cero mantenimiento extra, UX clara para quien realmente quiera jugar con sliders.
Úsalo cuando: el PDF es más para imprimir/leer y la exploración se hace online. -->
<!-- TODO: Equilibrios para los modelos poblacionales, añadir texto en las pestañas -->
# Modelos poblacionales
Los **modelos poblacionales** constituyen una aplicación importante de las ecuaciones diferenciales ordinarias. Permiten describir y predecir la evolución temporal de poblaciones biológicas, desde bacterias hasta especies animales, pasando por recursos pesqueros o forestales. Este tipo de modelos se encuentran incluso en contextos no biológicos, como el número de artículos publicados, la difusión de innovaciones o el crecimiento de usuarios en una red social.
### Clasificación de los modelos
Los modelos que estudiaremos se pueden clasificar según su complejidad:
1. **Modelos de crecimiento libre (sin limitaciones):**
- **Malthus (exponencial)**: Crecimiento proporcional al tamaño poblacional
- **Explosión**: Crecimiento acelerado (cuadrático)
2. **Modelos con saturación (limitaciones ambientales):**
- **Verhulst (logístico)**: Capacidad de carga del medio
- **Allee Effect**: Umbral mínimo de población viable
3. **Modelos con explotación (gestión de recursos):**
- **Harvest Quota**: Extracción constante
- **Relative Harvest**: Extracción proporcional
Estos modelos se desarrollaron de manera constructiva, cada uno mejorando o adaptando al anterior para reflejar fenómenos más realistas:
1. **Malthus**: Punto de partida, modelo más simple, tasa de crecimiento lineal
2. **Explosión**: Incrementa el crecimiento (no lineal)
3. **Verhulst**: Introduce la idea de límite natural
4. **Allee Effect**: Incorpora umbral mínimo de población viable
5. **Harvest Quota**: Aplicación práctica (extracción constante)
6. **Relative Harvest**: Gestión proporcional más realista
Este orden permite **comprender progresivamente** cómo cada modelo corrige o generaliza el anterior, reflejando fenómenos cada vez más realistas.
::: {.panel-tabset}
#### Modelo de Malthus {#sec-vis-malthus}
**Modelo de crecimiento exponencial**
El modelo más simple de crecimiento poblacional fue propuesto por Thomas Malthus en 1798. Supone que la tasa de crecimiento de una población es proporcional al tamaño de la población en cada instante:
$$
\frac{dP}{dt} = kP
$$
donde:
- $P(t)$ es la población en el instante $t$
- $k$ es la constante de proporcionalidad (tasa de crecimiento)
- $k > 0$ indica crecimiento, $k < 0$ indica decrecimiento
La **solución general** es:
$$
P(t) = P_0 e^{kt}
$$
donde $P_0$ es la población inicial en $t = 0$.
La solución describe un **crecimiento exponencial** si $k > 0$ o un **decaimiento exponencial** si $k < 0$.
El modelo de Malthus representa situaciones donde una población crece sin limitaciones externas, es decir, cuando los recursos son abundantes y no hay competencia, enfermedades ni depredadores. En la vida real, esto solo ocurre durante períodos iniciales o en ambientes controlados.
::: {.content-visible when-format="html"}
::: {.callout-tip collapse="true" title="Visualización interactiva"}
```{ojs}
//| echo: false
//| label: malthus-sliders
//| fig-cap: "Gráfico de crecimiento exponencial"
viewof k_malthus = Inputs.range([0.01, 1], {
value: 0.1,
step: 0.01,
label: "k (tasa de crecimiento)"
})
viewof P0_malthus = Inputs.range([0, 50], {
value: 10,
step: 1,
label: "P₀ (población inicial)"
})
viewof t_max_malthus = Inputs.range([10, 100], {
value: 50,
step: 5,
label: "Tiempo máximo (t)"
})
```
```{ojs}
//| echo: false
//| label: malthus
malthus_data = {
const points = [];
for (let t = 0; t <= t_max_malthus; t += 0.5) {
const P = P0_malthus * Math.exp(k_malthus * t);
points.push({t: t, P: P});
}
return points;
}
tiempo_duplicacion_malthus = Math.log(2) / k_malthus;
Plot.plot({
width: 700,
height: 400,
marginLeft: 60,
grid: true,
x: {
label: "Tiempo (t) →",
domain: [0, t_max_malthus]
},
y: {
label: "↑ Población P(t)",
domain: [0, Math.max(...malthus_data.map(d => d.P))]
},
marks: [
Plot.line(malthus_data, {
x: "t",
y: "P",
stroke: "#0066cc",
strokeWidth: 2.5
}),
Plot.dot([{t: 0, P: P0_malthus}], {
x: "t",
y: "P",
fill: "#0066cc",
r: 5
}),
// Plot.ruleY([P0_malthus], {
// stroke: "#999",
// strokeDasharray: "4,4"
// }),
// Equilibrio P* = 0 (inestable)
Plot.ruleY([0], {stroke: "red", strokeWidth: 2, strokeDasharray: "5,5"}),
Plot.text([{t: t_max_malthus * 0.95, P: 0}], {
text: ["P* = 0 (inestable)"],
fill: "red",
dx: -10,
dy: -10,
fontSize: 11
}),
Plot.text([{t: t_max_malthus * 0.7, P: Math.max(...malthus_data.map(d => d.P)) * 0.9}], {
text: () => [`P(t) = ${P0_malthus}e^{${k_malthus.toFixed(2)}t}`,
`T_duplicación = ${tiempo_duplicacion_malthus.toFixed(2)}`],
dy: [0, 20],
fontSize: 12,
fill: "#0066cc"
})
]
})
```
**Valores particulares:**
- Tiempo de duplicación: $T_d = \frac{\ln 2}{k} \approx$ `{ojs} tiempo_duplicacion_malthus.toFixed(2)` unidades
- Con $k =$ `{ojs} k_malthus.toFixed(2)`, la población en $t = 10$ es $P(10) =$ `{ojs} (P0_malthus * Math.exp(k_malthus * 10)).toFixed(2)`
**Observaciones:**
- **Sensibilidad temporal:** En etapas avanzadas del crecimiento, pequeños incrementos en el tiempo producen grandes variaciones en $P(t)$.
- **Ruptura de escala:** En los valores finales de $t$, el crecimiento de la población es tan acelerado que rompe la escala de representación, perdiendo realismo y aplicabilidad
Esto pone de manifiesto que el modelo **pierde realismo y aplicabilidad** en horizontes temporales largos.
:::
:::
::: {.content-visible when-format="pdf"}
{{< include _interactive_note.qmd >}}
:::
::: {.solution .make-collapse data-title="Resolución"}
<!-- ##### Resolución -->
Separando variables:
$$
\frac{dP}{P} = k \, dt
$$
Integrando:
$$
\int \frac{dP}{P} = \int k \, dt \implies \ln|P| = kt + C
$$
Con $P(0) = P_0$:
$$
P(t) = P_0 e^{kt}
$$
:::
#### Modelo de Explosión
**Modelo de crecimiento cuadrático**
Supongamos que la tasa de reproducción es proporcional al **número de parejas** posibles. En una población de tamaño $P$, el número de parejas es proporcional a $P^2$:
$$
\frac{dP}{dt} = kP^2
$$
donde $k > 0$ es la constante de reproducción.
La **solución general** es:
$$
P(t) = \frac{P_0}{1 - kP_0 t}
$$
**Tiempo de explosión:** La población tiende a infinito cuando $t \to t^* = \frac{1}{kP_0}$.
::: {.content-visible when-format="html"}
::: {.callout-tip collapse="true" title="Visualización interactiva"}
```{ojs}
//| echo: false
//| label: Explosion-sliders
viewof k_explosion = Inputs.range([0.001, 0.1], {
value: 0.01,
step: 0.001,
label: "k (tasa de reproducción)"
})
viewof P0_explosion = Inputs.range([1, 20], {
value: 5,
step: 1,
label: "P₀ (población inicial)"
})
```
```{ojs}
//| echo: false
//| label: Explosion
t_explosion = 1 / (k_explosion * P0_explosion);
explosion_data = {
const points = [];
const t_max = t_explosion * 0.98; // Ir muy cerca pero no llegar
const num_points = 500; // Muchos más puntos para suavizar
const dt = t_max / num_points;
for (let i = 0; i <= num_points; i++) {
const t = i * dt;
const P = P0_explosion / (1 - k_explosion * P0_explosion * t);
if (P > 0 && P < 2000) { // Límite más alto
points.push({t: t, P: P});
}
}
return points;
}
Plot.plot({
width: 700,
height: 400,
marginLeft: 60,
grid: true,
x: {
label: "Tiempo (t) →",
domain: [0, t_explosion * 1.05]
},
y: {
label: "↑ Población P(t)",
domain: [0, Math.min(Math.max(...explosion_data.map(d => d.P)) * 1.1, 1000)]
},
marks: [
Plot.line(explosion_data, {
x: "t",
y: "P",
stroke: "#dc3545",
strokeWidth: 2.5,
curve: "catmull-rom" // Suavizado de curva
}),
Plot.dot([{t: 0, P: P0_explosion}], {
x: "t",
y: "P",
fill: "#dc3545",
r: 5
}),
// Línea vertical de explosión
Plot.ruleX([t_explosion], {
stroke: "#666",
strokeDasharray: "6,4",
strokeWidth: 2
}),
Plot.text([{t: t_explosion, P: Math.max(...explosion_data.map(d => d.P)) * 0.5}], {
text: [`t* = ${t_explosion.toFixed(2)}`],
dx: -30,
fontSize: 11,
fill: "#666"
}),
// Equilibrio P* = 0 (inestable)
Plot.ruleY([0], {stroke: "red", strokeWidth: 2, strokeDasharray: "5,5"}),
Plot.text([{t: t_explosion * 0.5, P: 0}], {
text: ["P* = 0 (inestable)"],
fill: "red",
dx: 0,
dy: -10,
fontSize: 11
})
]
})
```
**Valores particulares:**
Tiempo de explosión: $t^* = \frac{1}{kP_0} =$ `{ojs} t_explosion.toFixed(2)` unidades
**Observaciones:**
Divergencia a infinito en tiempo **finito**.
:::
:::
::: {.content-visible when-format="pdf"}
{{< include _interactive_note.qmd >}}
:::
::: {.solution .make-collapse data-title="Resolución"}
Separando variables:
$$
\frac{dP}{P^2} = k \, dt \implies \int P^{-2} dP = \int k \, dt
$$
$$
-\frac{1}{P} = kt + C
$$
Con $P(0) = P_0$:
$$
P(t) = \frac{P_0}{1 - kP_0 t}
$$
:::
#### Modelo de Verhulst
**Modelo logístico con capacidad de carga**
El modelo de Verhulst (1838) introduce un término de saturación que limita el crecimiento cuando la población se acerca a la **capacidad de carga** del medio:
$$
\frac{dP}{dt} = kP\left(1 - \frac{P}{K}\right)
$$
donde:
- $k$ es la tasa de crecimiento intrínseca
- $K$ es la capacidad de carga del medio
La **solución general** es:
$$
P(t) = \frac{K}{1 + \left(\frac{K - P_0}{P_0}\right)e^{-kt}}
$$
Esta curva tiene forma de sigmoide y tiende asintóticamente a $K$.
::: {.content-visible when-format="html"}
::: {.callout-tip collapse="true" title="Visualización interactiva"}
```{ojs}
//| echo: false
//| label: Verhulst-sliders
viewof k_verhulst = Inputs.range([0.01, 1], {
value: 0.1,
step: 0.01,
label: "k (tasa de crecimiento)"
})
viewof K_verhulst = Inputs.range([50, 500], {
value: 100,
step: 10,
label: "K (capacidad de carga)"
})
viewof P0_verhulst = Inputs.range([1, K_verhulst*1.5], {
value: 10,
step: 1,
label: "P₀ (población inicial)"
})
viewof t_max_verhulst = Inputs.range([10, 200], {
value: 100,
step: 10,
label: "Tiempo máximo (t)"
})
```
```{ojs}
//| echo: false
//| label: Verhulst
verhulst_data = {
const points = [];
for (let t = 0; t <= t_max_verhulst; t += 0.5) {
const B = (K_verhulst - P0_verhulst) / P0_verhulst;
const P = K_verhulst / (1 + B * Math.exp(-k_verhulst * t));
points.push({t: t, P: P});
}
return points;
}
Plot.plot({
width: 700,
height: 400,
marginLeft: 60,
grid: true,
x: {
label: "Tiempo (t) →",
domain: [0, t_max_verhulst]
},
y: {
label: "↑ Población P(t)",
domain: [0, K_verhulst * 1.8]
},
marks: [
Plot.line(verhulst_data, {
x: "t",
y: "P",
stroke: "#28a745",
strokeWidth: 2.5
}),
Plot.dot([{t: 0, P: P0_verhulst}], {
x: "t",
y: "P",
fill: "#28a745",
r: 5
}),
// Equilibrio P* = K (estable)
Plot.ruleY([K_verhulst], {stroke: "green", strokeWidth: 2, strokeDasharray: "5,5"}),
Plot.text([{t: t_max_verhulst * 0.95, P: K_verhulst}], {
text: [`P* = K = ${K_verhulst} (estable)`],
fill: "green",
dx: -10,
dy: -10,
fontSize: 11
}),
// Equilibrio P* = 0 (inestable)
Plot.ruleY([0], {stroke: "red", strokeWidth: 2, strokeDasharray: "5,5"}),
Plot.text([{t: t_max_verhulst * 0.05, P: 0}], {
text: ["P* = 0 (inestable)"],
fill: "red",
dx: 10,
dy: -10,
fontSize: 11
})
]
})
```
**Valores particulares:**
- Capacidad de carga: $K =$ `{ojs} K_verhulst`
**Observaciones:**
- Curva sigmoide característica
- $\lim_{t \to \infty} P(t) = K$
:::
:::
::: {.content-visible when-format="pdf"}
{{< include _interactive_note.qmd >}}
:::
::: {.solution .make-collapse data-title="Resolución"}
Separando variables y usando fracciones parciales:
$$
\frac{dP}{P\left(1 - \frac{P}{K}\right)} = k \, dt
$$
$$
\left(\frac{1}{P} + \frac{1}{K - P}\right) dP = k \, dt
$$
Integrando y resolviendo para $P$:
$$
P(t) = \frac{K}{1 + \left(\frac{K - P_0}{P_0}\right)e^{-kt}}
$$
:::
#### Efecto Allee
**Umbral mínimo de población viable**
El **efecto Allee** describe situaciones donde una población pequeña tiene dificultades para crecer. Se requiere una **población mínima** $A$ para que el crecimiento sea positivo:
$$
\frac{dP}{dt} = kP\left(1 - \frac{P}{K}\right)\left(\frac{P}{A} - 1\right)
$$
donde $A$ es el umbral de Allee y $K$ es la capacidad de carga $(A < K)$.
**Puntos de equilibrio:**
- $P = 0$ (extinción)
- $P = A$ (umbral crítico, inestable)
- $P = K$ (capacidad, estable)
**Dinámica:**
- Si $P_0 < A$: extinción
- Si $A < P_0 < K$: crecimiento hacia $K$
::: {.content-visible when-format="html"}
::: {.callout-tip collapse="true" title="Visualización interactiva"}
```{ojs}
//| echo: false
//| label: Allee-sliders
viewof k_allee = Inputs.range([0.001, 0.1], {
value: 0.01,
step: 0.001,
label: "k (tasa de crecimiento)"
})
viewof A_allee = Inputs.range([10, 50], {
value: 20,
step: 5,
label: "A (umbral de Allee)"
})
viewof K_allee = Inputs.range([50, 200], {
value: 100,
step: 10,
label: "K (capacidad de carga)"
})
viewof P0_allee = Inputs.range([1, K_allee*1.5], {
value: 30,
step: 1,
label: "P₀ (población inicial)"
})
```
```{ojs}
//| echo: false
//| label: Allee
allee_data = {
const points = [];
const dt = 0.1;
let P = P0_allee;
for (let t = 0; t <= 200; t += dt) {
if (P > 0.1 && P < K_allee * 2) {
points.push({t: t, P: P});
const dP = k_allee * P * (1 - P/K_allee) * (P/A_allee - 1);
P += dP * dt;
} else {
break;
}
}
return points;
}
Plot.plot({
width: 700,
height: 400,
marginLeft: 60,
grid: true,
x: {
label: "Tiempo (t) →",
domain: [0, 200]
},
y: {
label: "↑ Población P(t)",
domain: [0, K_allee * 1.3]
},
marks: [
Plot.line(allee_data, {
x: "t",
y: "P",
stroke: "#6f42c1",
strokeWidth: 2.5
}),
Plot.dot([{t: 0, P: P0_allee}], {
x: "t",
y: "P",
fill: "#6f42c1",
r: 5
}),
Plot.ruleY([A_allee], {
stroke: "#dc3545",
strokeDasharray: "6,4",
strokeWidth: 2
}),
Plot.ruleY([K_allee], {
stroke: "#28a745",
strokeDasharray: "6,4",
strokeWidth: 2
}),
Plot.text([{t: 10, P: A_allee}], {
text: [`A = ${A_allee} (umbral)`],
dy: -10,
fontSize: 11,
fill: "#dc3545"
}),
Plot.text([{t: 10, P: K_allee}], {
text: [`K = ${K_allee}`],
dy: -10,
fontSize: 11,
fill: "#28a745"
})
]
})
```
**Valores particulares:**
- Umbral de Allee $A =$ `{ojs} A_allee` (rojo)
- Capacidad de carga $K =$ `{ojs} K_allee` (verde)
**Observaciones:**
- El equilibrio en $P = A$ es **inestable** y el equilibrio en $P = K$ es **estable**.
- Si $P_0 < A$: extinción. Si $P_0 > A$: supervivencia hacia $K$
:::
:::
::: {.content-visible when-format="pdf"}
{{< include _interactive_note.qmd >}}
:::
::: {.solution .make-collapse data-title="Resolución"}
La ecuación se puede reescribir como:
$$
\frac{dP}{dt} = kP\left(1 - \frac{P}{K}\right)\left(\frac{P}{A} - 1\right) = \frac{kP}{KA}\left(K - P\right)\left(P - A\right)
$$
Separando variables:
$$
\frac{dP}{P(K - P)(P - A)} = \frac{k}{KA} \, dt
$$
Usando fracciones parciales:
$$
\frac{1}{P(K-P)(P-A)} = \frac{1}{A(K-A)P} + \frac{1}{K(K-A)(K-P)} + \frac{1}{AK(P-A)}
$$
Integrando y aplicando condiciones iniciales se obtiene una solución implícita compleja. El análisis cualitativo de los puntos de equilibrio es más relevante:
- $P^* = 0$: estable
- $P^* = A$: inestable
- $P^* = K$: estable
:::
#### Harvest Quota (Constante)
**Extracción constante de población**
Consideremos una población de Verhulst de la cual extraemos una cantidad **constante** $c$ por unidad de tiempo:
$$
\frac{dP}{dt} = kP\left(1 - \frac{P}{K}\right) - c
$$
donde $c$ es la **tasa de cosecha** constante.
**Análisis de equilibrio:**
Los equilibrios satisfacen $kP\left(1 - \frac{P}{K}\right) = c$.
Existe cosecha sostenible si $c \leq c_{max} = \frac{kK}{4}$.
::: {.content-visible when-format="html"}
::: {.callout-tip collapse="true" title="Visualización interactiva"}
```{ojs}
//| echo: false
//| label: Harvest-sliders
viewof k_harvest = Inputs.range([0.01, 0.5], {
value: 0.1,
step: 0.01,
label: "k (tasa de crecimiento)"
})
viewof K_harvest = Inputs.range([50, 200], {
value: 100,
step: 1,
label: "K (capacidad de carga)"
})
viewof c_harvest = Inputs.range([0, 3], {
value: 2.5,
step: 0.1,
label: "c (tasa de cosecha constante)"
})
viewof P0_harvest = Inputs.range([0, K_harvest*1.3], {
value: 50,
step: 2,
label: "P₀ (población inicial)"
})
```
```{ojs}
//| echo: false
//| label: Harvest
c_max_harvest = (k_harvest * K_harvest) / 4;
harvest_data = {
const points = [];
const dt = 0.1;
let P = P0_harvest;
for (let t = 0; t <= 200; t += dt) {
if (P > 0.1 && P < K_harvest * 1.5) {
points.push({t: t, P: P});
const dP = k_harvest * P * (1 - P/K_harvest) - c_harvest;
P += dP * dt;
} else {
break;
}
}
return points;
}
equilibrios_harvest = {
const discriminante = K_harvest * K_harvest - (4 * c_harvest * K_harvest) / k_harvest;
const tolerancia = 0.01 * K_harvest * K_harvest; // Tolerancia relativa al 1% de K²
if (discriminante < -tolerancia) {
return []; // No hay equilibrios reales
} else if (Math.abs(discriminante) < tolerancia) {
return [{P: K_harvest / 2, estable: false, tipo: "bifurcación"}]; // Un equilibrio (bifurcación)
} else {
const P1 = (K_harvest - Math.sqrt(discriminante)) / 2;
const P2 = (K_harvest + Math.sqrt(discriminante)) / 2;
return [
{P: P1, estable: false, tipo: "inestable"},
{P: P2, estable: true, tipo: "estable"}
];
}
}
Plot.plot({
width: 700,
height: 400,
marginLeft: 60,
grid: true,
x: {
label: "Tiempo (t) →",
domain: [0, 200]
},
y: {
label: "↑ Población P(t)",
domain: [0, K_harvest * 1.2]
},
marks: [
Plot.line(harvest_data, {
x: "t",
y: "P",
stroke: "#fd7e14",
strokeWidth: 2.5
}),
Plot.dot([{t: 0, P: P0_harvest}], {
x: "t",
y: "P",
fill: "#fd7e14",
r: 5
}),
// Línea de capacidad K (referencia)
// Plot.ruleY([K_harvest], {
// stroke: "#999",
// strokeWidth: 1.5,
// strokeDasharray: "3,3",
// opacity: 0.5
// }),
Plot.text([{t: 200 * 0.98, P: K_harvest}], {
text: [`K = ${K_harvest}`],
fill: "gray",
dx: -10,
dy: -10,
fontSize: 10
}),
// Equilibrios dinámicos
...equilibrios_harvest.map((eq, i) =>
Plot.ruleY([eq.P], {
stroke: eq.estable ? "green" : (eq.tipo === "bifurcación" ? "orange" : "red"),
strokeWidth: 1.5,
strokeDasharray: "5,5"
})
),
...equilibrios_harvest.map((eq, i) =>
Plot.text([{t: 200 * (0.3 + i * 0.4), P: eq.P}], {
text: [`P* = ${eq.P.toFixed(1)} (${eq.tipo})`],
fill: eq.estable ? "green" : (eq.tipo === "bifurcación" ? "orange" : "red"),
dx: 10,
dy: -10,
fontSize: 11
})
),
Plot.text([{t: 100, P: K_harvest * 1.1}], {
text: () => c_harvest > c_max_harvest ?
[`c = ${c_harvest.toFixed(2)} > c_max = ${c_max_harvest.toFixed(2)} → EXTINCIÓN`] :
[`c = ${c_harvest.toFixed(2)} ≤ c_max = ${c_max_harvest.toFixed(2)} → SOSTENIBLE`],
fontSize: 11,
fill: c_harvest > c_max_harvest ? "#dc3545" : "#28a745"
})
]
})
```
```{ojs}
//| echo: false
equilibrios_texto = equilibrios_harvest.length === 0 ?
"Sin equilibrios (extinción inevitable)" :
equilibrios_harvest.length === 1 ?
"P* = " + equilibrios_harvest[0].P.toFixed(2) + " (bifurcación en K/2)" :
"P₁* = " + equilibrios_harvest[0].P.toFixed(2) + " (inestable), P₂* = " + equilibrios_harvest[1].P.toFixed(2) + " (estable)"
```
**Observaciones:**
- Cosecha máxima sostenible: $c_{max} = \frac{kK}{4} =$ `{ojs} c_max_harvest.toFixed(2)`
- Tasa actual: $c =$ `{ojs} c_harvest.toFixed(2)`
- Equilibrios actuales: `{ojs} equilibrios_texto`
- **Casos:**
- Si $c < c_{max}$: dos equilibrios (uno estable P₂, uno inestable P₁)
- Si $c = c_{max}$: un equilibrio en P* = K/2 (bifurcación)
- Si $c > c_{max}$: sin equilibrios → extinción inevitable
:::
:::
::: {.content-visible when-format="pdf"}
{{< include _interactive_note.qmd >}}
:::
::: {.solution .make-collapse data-title="Resolución"}
La ecuación diferencial es:
$$
\frac{dP}{dt} = kP\left(1 - \frac{P}{K}\right) - c
$$
Expandiendo:
$$
\frac{dP}{dt} = -\frac{k}{K}P^2 + kP - c
$$
Los equilibrios satisfacen: $kP\left(1 - \frac{P}{K}\right) = c$, que es una ecuación cuadrática:
$$
P^2 - KP + \frac{cK}{k} = 0
$$
Soluciones:
$$
P_{1,2} = \frac{K \pm \sqrt{K^2 - \frac{4cK}{k}}}{2}
$$
Para que existan equilibrios reales: $K^2 \geq \frac{4cK}{k}$, es decir, $c \leq c_{max} = \frac{kK}{4}$.
- Si $c < c_{max}$: dos equilibrios, $P_2$ estable y $P_1$ inestable
- Si $c = c_{max}$: un equilibrio $P^* = \frac{K}{2}$ (bifurcación)
- Si $c > c_{max}$: no hay equilibrios → extinción
:::
#### Relative Harvest Quota
**Extracción proporcional a la población**
La cosecha es **proporcional** al tamaño de la población:
$$
\frac{dP}{dt} = kP\left(1 - \frac{P}{K}\right) - hP
$$
donde $h$ es la **tasa de cosecha relativa**.
Reescribiendo: $\frac{dP}{dt} = (k - h)P\left(1 - \frac{P}{K}\right)$
**Análisis:**
- Si $h < k$: supervivencia hacia $K$
- Si $h \geq k$: extinción
::: {.content-visible when-format="html"}
::: {.callout-tip collapse="true" title="Visualización interactiva"}
```{ojs}
//| echo: false
//| label: Harvest_rel-sliders
viewof k_rel = Inputs.range([0.01, 0.5], {
value: 0.1,
step: 0.01,
label: "k (tasa de crecimiento)"
})
viewof K_rel = Inputs.range([50, 200], {
value: 100,
step: 10,
label: "K (capacidad de carga)"
})
viewof h_rel = Inputs.range([0, 0.2], {
value: 0.05,
step: 0.01,
label: "h (tasa de cosecha relativa)"
})
viewof P0_rel = Inputs.range([10, 120], {
value: 50,
step: 5,
label: "P₀ (población inicial)"
})
```
```{ojs}
//| echo: false
//| label: Harvest_rel
k_effective = k_rel - h_rel;
P_equilibrio_rel = K_rel * (1 - h_rel / k_rel);
rel_harvest_data = {
const points = [];
const dt = 0.1;
let P = P0_rel;
for (let t = 0; t <= 200; t += dt) {
if (P > 0.1 && P < K_rel * 1.5) {
points.push({t: t, P: P});
const dP = k_rel * P * (1 - P/K_rel) - h_rel * P;
P += dP * dt;
} else {
break;
}
}
return points;
}
Plot.plot({
width: 700,
height: 400,
marginLeft: 60,
grid: true,
x: {
label: "Tiempo (t) →",
domain: [0, 200]
},
y: {
label: "↑ Población P(t)",
domain: [0, K_rel * 1.2]
},
marks: [
Plot.line(rel_harvest_data, {
x: "t",
y: "P",
stroke: "#17a2b8",
strokeWidth: 2.5
}),
Plot.dot([{t: 0, P: P0_rel}], {
x: "t",
y: "P",
fill: "#17a2b8",
r: 5
}),
// Equilibrio P* = K(1 - h/k) (solo si h < k)
...(h_rel < k_rel ? [
Plot.ruleY([P_equilibrio_rel], {stroke: "green", strokeWidth: 2, strokeDasharray: "5,5"}),
Plot.text([{t: 200 * 0.95, P: P_equilibrio_rel}], {
text: [`P* = K(1-h/k) = ${P_equilibrio_rel.toFixed(1)} (estable)`],
fill: "green",
dx: -10,
dy: -10,
fontSize: 11
})
] : []),
// Línea de referencia K
Plot.ruleY([K_rel], {
stroke: "#999",
strokeWidth: 1.5,
strokeDasharray: "3,3",
opacity: 0.5
}),
Plot.text([{t: 200 * 0.98, P: K_rel}], {
text: [`K = ${K_rel}`],
fill: "gray",
dx: -10,
dy: -10,
fontSize: 10
}),
// Equilibrio P* = 0 (color depende de si h >= k)
Plot.ruleY([0], {
stroke: h_rel >= k_rel ? "green" : "red",
strokeWidth: 2,
strokeDasharray: "5,5"
}),
Plot.text([{t: 200 * 0.05, P: 0}], {
text: [`P* = 0 (${h_rel >= k_rel ? 'estable' : 'inestable'})`],
fill: h_rel >= k_rel ? "green" : "red",
dx: 10,
dy: -10,
fontSize: 11
}),
Plot.text([{t: 100, P: K_rel * 1.1}], {
text: () => h_rel >= k_rel ?
[`h = ${h_rel.toFixed(3)} ≥ k = ${k_rel.toFixed(3)} → EXTINCIÓN`] :
[`k_efectiva = k - h = ${k_effective.toFixed(3)} → SOSTENIBLE`],
fontSize: 11,
fill: h_rel >= k_rel ? "#dc3545" : "#28a745"
})
]
})
```
**Observaciones:**
- Tasa efectiva: $k_{eff} = k - h =$ `{ojs} k_effective.toFixed(3)`
- Si $h < k$: equilibrio estable en $P^* = K(1 - h/k) =$ `{ojs} P_equilibrio_rel.toFixed(1)`
- Si $h \geq k$: extinción
- La cosecha reduce la capacidad efectiva del medio
:::
:::
::: {.content-visible when-format="pdf"}
{{< include _interactive_note.qmd >}}
:::
::: {.solution .make-collapse data-title="Resolución"}
La ecuación diferencial es:
$$
\frac{dP}{dt} = kP\left(1 - \frac{P}{K}\right) - hP = P\left[k\left(1 - \frac{P}{K}\right) - h\right]
$$
Factorizando:
$$
\frac{dP}{dt} = P\left[k - h - \frac{kP}{K}\right] = P\left[(k-h) - \frac{k}{K}P\right]
$$
Reescribiendo en forma logística:
$$
\frac{dP}{dt} = (k-h)P\left[1 - \frac{P}{K(1-h/k)}\right]
$$
**Equilibrios:** $\frac{dP}{dt} = 0 \implies P = 0$ o $k\left(1 - \frac{P}{K}\right) = h$
Del segundo: $1 - \frac{P}{K} = \frac{h}{k} \implies P^* = K\left(1 - \frac{h}{k}\right)$
**Caso 1:** Si $h < k$ (cosecha sostenible)
El equilibrio no trivial es $P^* = K\left(1 - \frac{h}{k}\right) > 0$ (estable).
La solución es de tipo logístico con capacidad reducida $K_{eff} = K(1-h/k)$ y tasa efectiva $k_{eff} = k-h$:
$$
P(t) = \frac{K(1-h/k)}{1 + \left(\frac{K(1-h/k) - P_0}{P_0}\right)e^{-(k-h) t}}
$$
con $\lim_{t \to \infty} P(t) = K\left(1 - \frac{h}{k}\right)$.
**Caso 2:** Si $h = k$
$$
P^* = 0 \implies \frac{dP}{dt} = -\frac{kP^2}{K} \implies P(t) \to 0 \text{ (extinción)}
$$
**Caso 3:** Si $h > k$ (sobrepesca)
No hay equilibrio positivo, solo $P^* = 0$ que es estable $\implies P(t) \to 0$ (extinción).
:::
#### Comparación de Modelos
**Análisis comparativo:**
**Comportamiento asintótico:**
- **Malthus:** Crecimiento exponencial sin límite → $P(t) \to \infty$
- **Explosión:** Crecimiento superexponencial → explosión en tiempo finito
- **Verhulst:** Saturación logística → $P(t) \to K$
- **Allee:** Comportamiento bistable → extinción o supervivencia según $P_0$ vs $A$
- **Harvest Quota:** Equilibrio reducido o extinción según $c$ vs $c_{max}$
- **Relative Harvest:** Saturación en $K$ con tasa efectiva $(k-h)$
**Tasas de crecimiento:**
En $t$ pequeño:
- Explosión crece más rápido (cuadrático)
- Malthus y Verhulst similares inicialmente
- Allee crece más lento si $P_0$ cercano a $A$
- Harvest models dependen de intensidad de extracción
En $t$ grande:
- Malthus diverge exponencialmente
- Verhulst, Allee y Relative Harvest saturan (en $K$ o valores menores)
- Harvest Quota alcanza equilibrio estable o extinción
- Explosión ya ha explotado
**Efectos de la explotación:**
- **Harvest Quota**: Reduce capacidad efectiva, riesgo de colapso si $c > c_{max}$
- **Relative Harvest**: Reduce tasa de crecimiento pero mantiene forma logística
**Aplicabilidad:**
::: {.small}
| Modelo | Escenario | Ejemplo | Ecuación diferencial |
| -------------------- | ---------------------------------------------------------- | ------------------------------------------------ | --------------------------------------------------------------------------------- |
| **Malthus** | Crecimiento exponencial sin límites | Crecimiento bacteriano inicial (etapa temprana) | $\frac{dP}{dt} = kP$ |
| **Explosión** | Crecimiento cuadrático (más rápido que Malthus) | Reacciones en cadena, difusión sin freno | $\frac{dP}{dt} = kP^2$ |
| **Verhulst** | Crecimiento limitado por capacidad del entorno (logístico) | Poblaciones animales con recursos limitados | $\frac{dP}{dt} = kP \left(1 - \frac{P}{K} \right)$ |
| **Allee** | Crecimiento logístico + umbral de población mínima | Especies sociales con dificultad de reproducción | $\frac{dP}{dt} = kP \left(1 - \frac{P}{K} \right)\left( \frac{P}{A} - 1 \right)$ |
| **Harvest Quota** | Crecimiento logístico con extracción constante | Pesca/caza con cuotas fijas | $\frac{dP}{dt} = kP \left(1 - \frac{P}{K} \right) - c$ |
| **Relative Harvest** | Crecimiento logístico con extracción proporcional | Gestión pesquera proporcional al stock | $\frac{dP}{dt} = kP \left(1 - \frac{P}{K} \right) - hP$ |
:::
::: {.content-visible when-format="html"}
::: {.callout-tip collapse="true" title="Comparación Interactiva"}
```{ojs}
//| echo: false
//| label: poblacional-sliders
viewof k_comp = Inputs.range([0.01, 0.5], {
value: 0.09,
step: 0.01,
label: "k (tasa de crecimiento común)"
})
viewof K_comp = Inputs.range([50, 200], {
value: 70,
step: 10,
label: "K (capacidad de carga)"
})
viewof P0_comp = Inputs.range([5, K_comp*1.6], {
value: 10,
step: 5,
label: "P₀ (población inicial común)"
})
viewof A_comp = Inputs.range([10, 50], {
value: 20,
step: 5,
label: "A (umbral Allee)"
})
viewof t_max_comp = Inputs.range([20, 200], {
value: 100,
step: 10,
label: "Tiempo máximo (t)"
})
viewof modelos_seleccionados = Inputs.checkbox(
["Malthus", "Explosión", "Verhulst", "Allee", "Harvest Quota", "Relative Harvest"],
{value: ["Malthus", "Verhulst", "Allee"], label: "Modelos a mostrar:"}
)
viewof c_comp = Inputs.range([0, 2], {
value: 1,
step: 0.1,
label: "c (cosecha constante para Harvest Quota)"
})
viewof h_comp = Inputs.range([0, 0.15], {
value: 0.05,
step: 0.01,
label: "h (cosecha relativa para Relative Harvest)"
})
```
```{ojs}
//| echo: false
//| label: poblacional
comparison_data = {
const all_data = [];
const dt = 0.1;
const t_explosion_comp = 1 / (k_comp * 0.01 * P0_comp); // k más pequeño para explosión
// Malthus
if (modelos_seleccionados.includes("Malthus")) {
for (let t = 0; t <= t_max_comp; t += 0.5) {
const P = P0_comp * Math.exp(k_comp * t);
if (P < 500) {
all_data.push({t: t, P: P, modelo: "Malthus"});
}
}
}
// Explosión
if (modelos_seleccionados.includes("Explosión")) {
const k_explosion_small = k_comp * 0.01; // Escala reducida
const t_exp = 1 / (k_explosion_small * P0_comp);
for (let t = 0; t <= Math.min(t_exp * 0.95, t_max_comp); t += 0.5) {
const P = P0_comp / (1 - k_explosion_small * P0_comp * t);
if (P > 0 && P < 500) {
all_data.push({t: t, P: P, modelo: "Explosión"});
}
}
}
// Verhulst
if (modelos_seleccionados.includes("Verhulst")) {
for (let t = 0; t <= t_max_comp; t += 0.5) {
const B = (K_comp - P0_comp) / P0_comp;
const P = K_comp / (1 + B * Math.exp(-k_comp * t));
all_data.push({t: t, P: P, modelo: "Verhulst"});
}
}
// Allee
if (modelos_seleccionados.includes("Allee")) {
let P = P0_comp;
for (let t = 0; t <= t_max_comp; t += dt) {
if (P > 0.1 && P < K_comp * 2) {
all_data.push({t: t, P: P, modelo: "Allee"});
const dP = k_comp * P * (1 - P/K_comp) * (P/A_comp - 1);
P += dP * dt;
} else {
break;
}
}
}
// Harvest Quota
if (modelos_seleccionados.includes("Harvest Quota")) {
let P = P0_comp;
for (let t = 0; t <= t_max_comp; t += dt) {
if (P > 0.1 && P < K_comp * 2) {
all_data.push({t: t, P: P, modelo: "Harvest Quota"});
const dP = k_comp * P * (1 - P/K_comp) - c_comp;
P += dP * dt;
} else {
break;
}
}
}
// Relative Harvest
if (modelos_seleccionados.includes("Relative Harvest")) {
let P = P0_comp;
for (let t = 0; t <= t_max_comp; t += dt) {
if (P > 0.1 && P < K_comp * 2) {
all_data.push({t: t, P: P, modelo: "Relative Harvest"});
const dP = k_comp * P * (1 - P/K_comp) - h_comp * P;
P += dP * dt;
} else {
break;
}
}
}
return all_data;
}
Plot.plot({
width: 700,
height: 450,
marginLeft: 60,
marginRight: 120,
grid: true,
x: {
label: "Tiempo (t) →",
domain: [0, t_max_comp]
},
y: {
label: "↑ Población P(t)",
domain: [0, Math.min(K_comp * 1.5, Math.max(...comparison_data.map(d => d.P)) * 1.1)]
},
color: {
legend: true,
domain: ["Malthus", "Explosión", "Verhulst", "Allee", "Harvest Quota", "Relative Harvest"],
range: ["#0066cc", "#dc3545", "#28a745", "#6f42c1", "#fd7e14", "#17a2b8"]
},
marks: [
Plot.line(comparison_data, {
x: "t",
y: "P",
stroke: "modelo",
strokeWidth: 2.5,
curve: "catmull-rom"
}),
Plot.dot(comparison_data.filter(d => d.t === 0), {
x: "t",
y: "P",
fill: "modelo",
r: 5
}),
Plot.ruleY([K_comp], {
stroke: "#999",
strokeDasharray: "6,4",
strokeWidth: 1.5
}),
Plot.ruleY([A_comp], {
stroke: "#ff6b6b",
strokeDasharray: "4,2",
strokeWidth: 1.5
}),
Plot.text([{t: t_max_comp * 0.95, P: K_comp}], {
text: [`K = ${K_comp}`],
dy: -10,
dx: -10,
fontSize: 10,
fill: "#666"
}),
Plot.text([{t: t_max_comp * 0.95, P: A_comp}], {
text: [`A = ${A_comp}`],
dy: 15,
dx: -10,
fontSize: 10,
fill: "#ff6b6b"
})
]
})
```
:::
:::
:::
::: {.content-visible when-format="pdf"}
{{< include _interactive_note.qmd >}}
:::