#set document(title: "1.2 The Logistic Equation", author: "XYZ Homework") #set page(width: 8.5in, height: auto, margin: 1in) #import "@preview/cetz:0.5.2" #set text(font: ("STIX Two Text", "Libertinus Serif", "New Computer Modern"), size: 10.5pt, lang: "en") #show math.equation: set text(font: ("STIX Two Math", "New Computer Modern Math")) #set par(justify: true, leading: 0.62em, spacing: 0.9em) #set enum(spacing: 1.1em) // room between list items so tall inline fractions don't collide #set list(spacing: 1.1em) #set table(stroke: 0.5pt + rgb("#c7ccd3")) #let BLUE = rgb("#183B6F") // brand navy — section bars + example/solution labels (white on navy 11.09:1) #let ORANGE = rgb("#A94509") // brand primary-700 — AA-safe deep orange for TEXT (5.93:1 on white; raw brand #F37021 is 2.94:1 and must never carry text) #let RED = rgb("#DC2626") // brand error-600 #let GREEN = rgb("#059669") // brand success-600 (decoration only; small green text uses green-text #007942) #show heading.where(level: 1): it => block(width: 100%, above: 0pt, below: 16pt, fill: gradient.linear(BLUE, rgb("#2C5AA0")), inset: (x: 14pt, y: 12pt), radius: 3pt, text(fill: white, weight: "bold", size: 19pt, it.body)) #show heading.where(level: 2): it => block(width: 100%, above: 18pt, below: 10pt, fill: BLUE, inset: (x: 10pt, y: 6pt), radius: 2pt, text(fill: white, weight: "bold", size: 12pt, it.body)) #show heading.where(level: 3): it => text(fill: ORANGE, weight: "bold", size: 12.5pt, it.body) #show heading.where(level: 4): it => text(fill: BLUE, weight: "bold", size: 10.5pt, it.body) #let examplebox(label, title, body) = block(width: 100%, breakable: true, fill: rgb("#EFF1F5"), stroke: 0.5pt + rgb("#CFDDF0"), radius: 4pt, inset: 10pt, above: 12pt, below: 12pt)[ #block(below: 6pt)[#box(fill: BLUE, inset: (x: 6pt, y: 2pt), radius: 2pt, text(fill: white, weight: "bold", size: 8.5pt, label)) #h(0.4em) #strong[#title]] #body] // rail = decorative left rule (raw brand token); labelcolor = AA-safe label text shade #let notebox(label, rail, labelcolor, tint, body) = block(width: 100%, breakable: true, fill: tint, stroke: (left: 3pt + rail), inset: (left: 10pt, rest: 8pt), radius: (right: 4pt), above: 11pt, below: 11pt)[ #text(fill: labelcolor, weight: "bold", size: 7.5pt, tracking: 0.5pt)[#upper(label)] #linebreak() #body] #let solutionbox(body) = block(above: 4pt, below: 8pt)[ #text(fill: BLUE, weight: "bold", size: 8.5pt)[Solution] #linebreak() #body] #let figph(msg) = block(width: 100%, height: 60pt, fill: rgb("#f6f7f9"), stroke: (paint: rgb("#c7ccd3"), dash: "dashed"), radius: 4pt, inset: 10pt)[ #align(center + horizon, text(fill: rgb("#889"), style: "italic", size: 9pt, msg))] // Standardize inlined figure sizes: measure the natural CeTZ canvas, then scale to a // consistent envelope (aspect-aware; see build_typst.py FIG_* constants). Unlike the // print preamble, dimensions are FLOORED: in an editor a user can trim a figure to a // degenerate 1-D shape (a bare line), and w/h or tw/w would then divide by zero. #let _STD_W = 3.5 #let _WIDE_W = 5.6 #let _MAX_H = 3.4 #let _ASPECT_WIDE = 2.2 #let _UPSCALE_MAX = 1.15 #let stdfig(body) = context { let m = measure(body) let w = calc.max(m.width / 1in, 0.01) let h = calc.max(m.height / 1in, 0.01) let tw = if w / h > _ASPECT_WIDE { _WIDE_W } else { _STD_W } let s = calc.min(tw / w, _MAX_H / h, _UPSCALE_MAX) align(center, box(scale(x: s * 100%, y: s * 100%, reflow: true, body))) } #show figure: set block(breakable: false) #set figure(gap: 8pt) #show figure.caption: set text(size: 8.5pt, fill: rgb("#555")) == 1.2#h(0.6em)The Logistic Equation #notebox("Note", rgb("#8a94a6"), rgb("#556666"), rgb("#f7f8fa"))[ #emph[Objectives] - Read the logistic equation as "exponential growth with a brake" and identify its two equilibria. - Connect the S-shape of logistic solutions to the sign and size of #math.equation(block: false, alt: "d P / d t")[$d P / d t$] at each population level. - Classify the equilibria #math.equation(block: false, alt: "P equals 0")[$P = 0$] and #math.equation(block: false, alt: "P equals 8")[$P = 8$] as unstable and stable by watching where nearby solutions go. ] Exponential growth cannot last. A population that doubles every hour eventually runs out of food, space, or patience, and a model that ignores this fact answers questions about the far future with nonsense. The classical fix is to make the per-capita growth rate fall as the population rises. With carrying capacity #math.equation(block: false, alt: "8")[$8$] (in whatever units the habitat supports), the #emph[logistic equation] is #math.equation(block: true, alt: "the fraction d P over d t equals b P open parenthesis 1 minus the fraction P over 8 close parenthesis .")[$frac(d P, d t) = b P ( 1 − frac(P, 8) ) .$] Read it before solving it. When #math.equation(block: false, alt: "P")[$P$] is small the factor #math.equation(block: false, alt: "1 minus P / 8")[$1 − P / 8$] is close to #math.equation(block: false, alt: "1")[$1$] and the equation is nearly #math.equation(block: false, alt: "P prime equals b P")[$P^(′) = b P$] — exponential growth. When #math.equation(block: false, alt: "P")[$P$] is near #math.equation(block: false, alt: "8")[$8$] the factor is near #math.equation(block: false, alt: "0")[$0$] and growth stalls. Between them, the growth rate #math.equation(block: false, alt: "b P open parenthesis 1 minus P / 8 close parenthesis")[$b P ( 1 − P / 8 )$] is largest at #math.equation(block: false, alt: "P equals 4")[$P = 4$], halfway up. And there are two population levels where nothing changes at all: #math.equation(block: false, alt: "P equals 0")[$P = 0$] and #math.equation(block: false, alt: "P equals 8")[$P = 8$], the #emph[equilibria], where the right-hand side vanishes and a population that starts there stays there forever. The equation can be solved in closed form, and the solutions are #math.equation(block: true, alt: "P open parenthesis t close parenthesis equals the fraction 8 over 1 plus a e to the power minus b t ,")[$P ( t ) = frac(8, 1 + a e^(− b t)) ,$] where the constant #math.equation(block: false, alt: "a")[$a$] encodes the starting value: #math.equation(block: false, alt: "P open parenthesis 0 close parenthesis equals 8 / open parenthesis 1 plus a close parenthesis")[$P ( 0 ) = 8 / ( 1 + a )$], so large #math.equation(block: false, alt: "a")[$a$] means a small starting population and #math.equation(block: false, alt: "a")[$a$] near zero means starting almost at capacity. {"camera":{"fov":50,"position":\[5,4,11\],"projection":"perspective","target":\[5,4,0\]},"grid":{"axisColors":{"x":"\#ef4444","y":"\#22c55e","z":"\#3b82f6"},"divisions":10,"gridColor":"\#e5e7eb","showAxes":true,"showLabels":true,"size":10,"visible":true},"id":"f9259538-2c26-4d8e-995a-d47ff5c33e56","metadata":{"alt\_text":"A red S-shaped logistic curve rising from its starting value toward a dashed horizontal line at height 8, with a second dashed line along height 0. The slider a moves the starting population P(0) between about 0.4 and 7.3, and the slider b sharpens or relaxes how fast the curve climbs toward the dashed capacity line, which it approaches but never crosses.","created\_at":"2026-08-01T00:00:00.000Z","description":"Closed-form logistic solutions P(t) = 8/(1 + a\*exp(-b\*t)) on t in \[0, 10\] with carrying capacity fixed at 8, plus the two equilibrium lines P = 8 and P = 0 as dashed references.","tags":\["diffeq","first-order","logistic","equilibrium","slider"\],"updated\_at":"2026-08-01T00:00:00.000Z"},"objects":\[{"id":"2f174a04-e5c9-4a39-b91d-cd4c66dbc804","kind":"parameter","label":{"text":"a sets the start: P(0) = 8/(1+a)","visible":true},"max":20,"min":0.1,"name":"a","step":0.1,"value":7,"visible":true},{"id":"1e613121-f604-4c74-94ce-4c9a954742ee","kind":"parameter","label":{"text":"b = growth rate","visible":true},"max":2,"min":0.2,"name":"b","step":0.05,"value":0.8,"visible":true},{"id":"f5026137-aa05-410c-80a9-bd8e5a2f6f6b","kind":"parametric-curve","label":{"text":"P(t) = 8/(1 + a e^(-bt))","visible":true},"samples":800,"style":{"color":"\#ef4444","dash":\[\],"lineWidth":4},"tDomain":\[0,10\],"visible":true,"xExpr":"t","yExpr":"8/(1 + a\*exp(-b\*t))","zExpr":"0"},{"id":"b89a36e0-eee3-4fff-9a6d-60b82bfc0d4a","kind":"parametric-curve","label":{"text":"carrying capacity P = 8","visible":true},"samples":100,"style":{"color":"\#9ca3af","dash":\[5,5\],"lineWidth":2},"tDomain":\[0,10\],"visible":true,"xExpr":"t","yExpr":"8","zExpr":"0"},{"id":"56023614-8819-4824-ba93-5ef79569d867","kind":"parametric-curve","label":{"text":"extinction equilibrium P = 0","visible":true},"samples":100,"style":{"color":"\#9ca3af","dash":\[5,5\],"lineWidth":2},"tDomain":\[0,10\],"visible":true,"xExpr":"t","yExpr":"0","zExpr":"0"}\],"title":"Logistic solutions P(t) = 8/(1 + a e^(-bt))","version":1} Logistic solutions #math.equation(block: false, alt: "P open parenthesis t close parenthesis equals 8 / open parenthesis 1 plus a e to the power minus b t close parenthesis")[$P ( t ) = 8 / ( 1 + a e^(− b t) )$] with carrying capacity #math.equation(block: false, alt: "8")[$8$] (upper dashed line) and the extinction equilibrium #math.equation(block: false, alt: "P equals 0")[$P = 0$] (lower dashed line). The slider #math.equation(block: false, alt: "a")[$a$] sets the starting population and #math.equation(block: false, alt: "b")[$b$] sets the growth rate. === Explore + #strong[Observe.] Set #math.equation(block: false, alt: "a equals 7")[$a = 7$], #math.equation(block: false, alt: "b equals 0.8")[$b = 0.8$], so #math.equation(block: false, alt: "P open parenthesis 0 close parenthesis equals 1")[$P ( 0 ) = 1$]. The curve is S-shaped: slow, then fast, then slow. At roughly what height is the curve steepest? Compare with the halfway level #math.equation(block: false, alt: "P equals 4")[$P = 4$]. + #strong[Predict.] If you increase #math.equation(block: false, alt: "a")[$a$] to #math.equation(block: false, alt: "20")[$20$], the starting population drops to about #math.equation(block: false, alt: "0.4")[$0.4$]. Will the curve still reach toward the dashed line at #math.equation(block: false, alt: "8")[$8$], or level off lower? Decide, then drag the slider. + #strong[Verify.] With any settings, zoom your attention to the right edge of the window. Does the red curve ever touch or cross the dashed line at #math.equation(block: false, alt: "8")[$8$]? Explain from the equation why it cannot: what would #math.equation(block: false, alt: "d P / d t")[$d P / d t$] be at the moment of touching? + #strong[Observe.] Drag #math.equation(block: false, alt: "a")[$a$] down to #math.equation(block: false, alt: "0.1")[$0.1$], so the population starts near #math.equation(block: false, alt: "7.3")[$7.3$], close to capacity. The S-shape disappears. Which part of the S did this starting value skip? + #strong[Predict, then verify.] The equilibrium #math.equation(block: false, alt: "P equals 0")[$P = 0$] is also a solution. Judging from the curves with large #math.equation(block: false, alt: "a")[$a$], does a tiny population move toward #math.equation(block: false, alt: "0")[$0$] or away from it? What does that say about the stability of the extinction equilibrium? === Connect The two dashed lines behave differently, and the difference is the most important qualitative fact about the model. Solutions that start just above #math.equation(block: false, alt: "0")[$0$] flee from it: #math.equation(block: false, alt: "P equals 0")[$P = 0$] is #emph[unstable], an equilibrium in the sense of a pencil balanced on its tip. Solutions from every positive starting value climb or descend toward #math.equation(block: false, alt: "8")[$8$] and flatten against it: #math.equation(block: false, alt: "P equals 8")[$P = 8$] is #emph[stable], an equilibrium like a marble at the bottom of a bowl. You verified both facts with sliders, but they were readable from the equation alone — the sign of #math.equation(block: false, alt: "b P open parenthesis 1 minus P / 8 close parenthesis")[$b P ( 1 − P / 8 )$] is positive for #math.equation(block: false, alt: "0 less than P less than 8")[$0 < P < 8$] and negative for #math.equation(block: false, alt: "P greater than 8")[$P > 8$], so arrows on a vertical #math.equation(block: false, alt: "P")[$P$]-axis point away from #math.equation(block: false, alt: "0")[$0$] and toward #math.equation(block: false, alt: "8")[$8$]. That sign analysis, not the closed formula, is what generalizes to equations nobody can solve. #examplebox("Example 1")[Time to the midpoint][ A pond stocked with #math.equation(block: false, alt: "P open parenthesis 0 close parenthesis equals 1")[$P ( 0 ) = 1$] (hundred fish) obeys the logistic equation with #math.equation(block: false, alt: "b equals 0.8")[$b = 0.8$] and capacity #math.equation(block: false, alt: "8")[$8$]. When does the population reach the halfway level #math.equation(block: false, alt: "P equals 4")[$P = 4$]? From #math.equation(block: false, alt: "P open parenthesis 0 close parenthesis equals 8 / open parenthesis 1 plus a close parenthesis equals 1")[$P ( 0 ) = 8 / ( 1 + a ) = 1$] we get #math.equation(block: false, alt: "a equals 7")[$a = 7$]. Setting #math.equation(block: false, alt: "P open parenthesis t close parenthesis equals 4")[$P ( t ) = 4$] in the solution formula gives #math.equation(block: false, alt: "8 / open parenthesis 1 plus 7 e to the power minus 0.8 t close parenthesis equals 4")[$8 / ( 1 + 7 e^(− 0.8 t) ) = 4$], so #math.equation(block: false, alt: "1 plus 7 e to the power minus 0.8 t equals 2")[$1 + 7 e^(− 0.8 t) = 2$], hence #math.equation(block: false, alt: "e to the power minus 0.8 t equals 1 / 7")[$e^(− 0.8 t) = 1 / 7$] and #math.equation(block: false, alt: "t equals open parenthesis ln 7 close parenthesis / 0.8 approximately equals 2.43")[$t = ( ln 7 ) / 0.8 ≈ 2.43$]. The halfway moment is also the steepest moment — after it, growth decelerates. Check on the figure: with #math.equation(block: false, alt: "a equals 7")[$a = 7$], #math.equation(block: false, alt: "b equals 0.8")[$b = 0.8$], the curve crosses height #math.equation(block: false, alt: "4")[$4$] near #math.equation(block: false, alt: "t equals 2.4")[$t = 2.4$], visibly at its steepest. ] #notebox("Note", rgb("#8a94a6"), rgb("#556666"), rgb("#f7f8fa"))[ #emph[Coming soon: the slope field] The canonical picture of this chapter is still to come. A #emph[slope field] draws, at every point #math.equation(block: false, alt: "open parenthesis t , P close parenthesis")[$( t , P )$] of the plane, a short segment whose slope is #math.equation(block: false, alt: "b P open parenthesis 1 minus P / 8 close parenthesis")[$b P ( 1 − P / 8 )$] — the equation's instruction at that point — so that every solution curve threads through the field tangent to the segments, and the stable and unstable equilibria appear as horizontal rivers the flow converges to or splits from. That figure needs the slope-field object now being built for the XYZ 3D engine. When it lands, you will see this section's whole story — S-curves, equilibria, stability — repainted as one field of arrows, no formulas required. ]