====== std.numeric — numerisches Grundwerkzeug ======
Nullstellen, Integration, Interpolation und Splines, gewöhnliche Differentialgleichungen samt steifer Löser und Randwertprobleme.
→ [[lyx_-_programmiersprache:units:mathematik|Mathematik-Units]] · [[lyx_-_programmiersprache:units:sum|std.sum]] · [[lyx_-_programmiersprache:units:autodiff|std.autodiff]] · [[lyx_-_programmiersprache:sprache:funktionen|Funktionen]]
import std.numeric;
Schicht 1, hängt an [[lyx_-_programmiersprache:units:sum|std.sum]], ''std.math'' und ''std.alloc''. 34 Funktionen. Alle Beispiele mit ''lyxc 1.0.21A'' übersetzt und ausgeführt.
----
===== Die Funktion wird übergeben =====
''std.numeric'' arbeitet mit **Funktionszeigern**. Eine zu untersuchende Funktion wird als gewöhnliche ''fn'' geschrieben und beim Aufruf über ihren Namen weitergereicht:
fn f(x: f64): f64 { return x * x - 2.0; }
var wurzel: f64 := NumBrent(f, 1.0, 2.0, 0.000000001, 200);
^ Signatur ^ Wofür ^
| ''fn(f64): f64'' | Nullstellen, Integration, Ableitung |
| ''fn(f64, f64): f64'' | ODE — ''f(t, y)'' |
| ''fn(f64, f64, f64): f64'' | Randwertproblem — ''f(t, y, y')'' |
| ''fn(f64, int64, int64): void'' | ODE-System, Zustand über Adressen |
----
===== Nullstellen =====
unit main;
import std.io;
import std.string;
import std.alloc;
import std.math;
import std.numeric;
fn ZF(t: pchar, v: f64): void { Print(t); PrintF64(v); }
fn f(x: f64): f64 { return x * x - 2.0; }
fn df(x: f64): f64 { return 2.0 * x; }
fn main(): int64 {
ZF("Bisect : ", NumBisect(f, 1.0, 2.0, 0.000000001, 200));
ZF("Brent : ", NumBrent(f, 1.0, 2.0, 0.000000001, 200));
ZF("Newton : ", NumNewton(f, df, 1.0, 0.000000001, 100));
ZF("NewtonNum : ", NumNewtonNum(f, 1.0, 0.000000001, 100));
ZF("Secant : ", NumSecant(f, 1.0, 2.0, 0.000000001, 100));
ZF("sqrt(2) : ", SqrtF64(2.0));
return 0;
}
Bisect : 1.414213
Brent : 1.414213
Newton : 1.414213
NewtonNum : 1.414213
Secant : 1.414213
sqrt(2) : 1.414213
Alle fünf finden dieselbe Nullstelle — sie unterscheiden sich in Voraussetzungen und Geschwindigkeit:
^ Verfahren ^ Braucht ^ Verhalten ^
| ''NumBisect'' | Intervall mit Vorzeichenwechsel | **findet immer**, aber langsam |
| ''NumBrent'' | dasselbe | schnell **und** garantiert — die Vorgabe |
| ''NumNewton'' | Startwert **und Ableitung** | am schnellsten, kann davonlaufen |
| ''NumNewtonNum'' | nur Startwert | Ableitung numerisch, entsprechend ungenauer |
| ''NumSecant'' | zwei Startwerte | ohne Ableitung, meist schneller als Bisektion |
> **''NumBrent'' ist fast immer die richtige Wahl.** Es verbindet die Sicherheit der Bisektion mit der Geschwindigkeit der inversen quadratischen Interpolation: es kann nicht aus dem Intervall laufen, braucht aber typisch ein Zehntel der Schritte. Newton ist nur dann besser, wenn die Ableitung **exakt** vorliegt — und dann führt der Weg über [[lyx_-_programmiersprache:units:autodiff|std.autodiff]], das sie ohne Handrechnung liefert.
>
> Newton **ohne** Vorzeichenwechsel-Intervall kann bei einem schlechten Startwert beliebig weit weglaufen oder zwischen zwei Punkten pendeln. Die ''maxIter''-Grenze ist deshalb kein Formalismus.
''NumDeriv(f, x)'' und ''NumDeriv2(f, x)'' liefern erste und zweite Ableitung numerisch; ''NumDerivStep(x)'' gibt die dazu passende Schrittweite.
----
===== Integration =====
fn g(x: f64): f64 { return x * x; } // Integral 0..1 = 1/3
Trapez x^2 0..1 : 0.333333
Simpson x^2 0..1 : 0.333333
Romberg x^2 0..1 : 0.333333
Gauss5 x^2 0..1 : 0.333333
^ Verfahren ^ Eigenschaft ^
| ''NumTrapezoid(f, a, b, n)'' | einfachste Form, fester Raster |
| ''NumSimpson(f, a, b, n)'' | deutlich genauer bei gleichem Aufwand |
| ''NumAdaptiveSimpson(f, a, b, tol, …)'' | verfeinert nur dort, wo es nötig ist |
| ''NumRomberg(f, a, b, maxLevel, tol)'' | Extrapolation, sehr genau bei glatten Funktionen |
| ''NumGauss5(f, a, b)'' | fünf Stützstellen, exakt bis Grad 9 |
| ''NumGaussComposite(f, a, b, n)'' | dasselbe stückweise |
**''NumGauss5'' rechnet ''x²'' mit fünf Auswertungen exakt**, während Trapez dafür tausend braucht. Der Preis: Gauß setzt eine glatte Funktion voraus. Bei einem Knick oder einer Polstelle ist ''NumAdaptiveSimpson'' die verlässlichere Wahl, weil es die Verfeinerung dorthin lenkt, wo sich die Funktion tatsächlich ändert.
----
===== Interpolation =====
^ Funktion ^ Zweck ^
| ''NumLerp(x0, y0, x1, y1, x)'' | zwischen zwei Punkten |
| ''NumInterpLinear(xs, ys, n, x)'' | Streckenzug durch ''n'' Punkte |
| ''NumLagrange(xs, ys, n, x)'' | Polynom durch alle Punkte |
| ''NumSplineBuild'' + ''NumSplineEval'' | kubischer Spline |
> **Lagrange ist bei vielen Punkten die falsche Wahl.** Ein Polynom durch 20 Stützstellen schwingt zwischen ihnen stark aus — das Runge-Phänomen. Für glatte Kurven durch viele Punkte ist der **Spline** richtig: ''NumSplineBuild'' rechnet einmal die zweiten Ableitungen aus (Feld ''y2''), danach ist jedes ''NumSplineEval'' billig.
----
===== Differentialgleichungen =====
fn ode(t: f64, y: f64): f64 { return y; } // y' = y, y(0)=1 -> y(1) = e
Rk4 y'=y, y(1) : 2.718281
Euler y'=y, y(1) : 2.704813
e zum Vergleich : 2.718281
**Bei gleicher Schrittzahl (100) trifft Runge-Kutta alle gezeigten Stellen, Euler weicht schon in der dritten ab.** Das ist der Unterschied zwischen einem Verfahren erster und vierter Ordnung: halbiert man die Schrittweite, wird der Fehler bei Euler halbiert, bei RK4 auf ein Sechzehntel gedrückt.
^ Gruppe ^ Funktionen ^
| Einzelschritt | ''NumEulerStep'' · ''NumMidpointStep'' · ''NumRk4Step'' |
| Über ein Intervall | ''NumEulerSolve'' · ''NumRk4Solve'' · ''NumRk4Adaptive'' |
| Systeme | ''NumRk4SolveSys'' |
| Mehrschritt | ''NumAdamsBashforth4'' · ''NumAdamsMoulton4'' |
| **Steif** | ''NumBdf1'' · ''NumBdf2'' · ''NumTrapezoidal'' |
| Randwertprobleme | ''NumShoot'' · ''NumShootTrace'' · ''NumBvpLinear'' |
> **Steife Gleichungen erkennt man daran, dass RK4 winzige Schritte braucht.** Wenn eine Lösung schnell abklingende und langsam veränderliche Anteile mischt, erzwingt der schnelle Anteil die Schrittweite — auch lange nachdem er praktisch verschwunden ist. Explizite Verfahren werden dann unbrauchbar langsam oder instabil.
>
> ''NumBdf1'', ''NumBdf2'' und ''NumTrapezoidal'' sind **implizit** und haben das Problem nicht. Sie kosten je Schritt mehr, brauchen aber Größenordnungen weniger Schritte. Typische Fälle: chemische Reaktionskinetik, elektrische Netzwerke mit sehr verschiedenen Zeitkonstanten, Regelkreise.
''NumShoot'' löst ein Randwertproblem, indem es die Anfangssteigung so lange anpasst, bis der zweite Rand getroffen wird; ''NumBvpLinear'' geht bei linearen Problemen direkt über ein Gleichungssystem.
----
===== Zusammenspiel =====
* Für **exakte Ableitungen** statt numerischer: [[lyx_-_programmiersprache:units:autodiff|std.autodiff]] liefert sie über Dualzahlen — ''NumNewton'' wird damit sowohl schnell als auch genau.
* Die Summationen laufen über [[lyx_-_programmiersprache:units:sum|std.sum]], sind also gegen Auslöschung abgesichert.
----
Letzte Aktualisierung: 2026-08-16 · alle Beispiele mit ''lyxc 1.0.21A'' übersetzt und ausgeführt; Nullstellen, Integrale, Ableitungen und ODE-Lösungen gegen geschlossene Ausdrücke geprüft.