Methoden der Versuchsplanung: Optimal Design und Sobol-Sequenzen

Methoden der Versuchsplanung: Optimal Design und Sobol-Sequenzen


Latest

Methoden der Versuchsplanung: Optimal Design und Sobol-Sequenzen

Author: Christoph Würsch, ICE, Eastern Switzerland University of Applied Sciences, OST

Dieser Blog behandelt fundamentale Methoden der Versuchsplanung (Design of Experiments, DoE) und stellt zwei primäre Ansätze gegenüber: die modellbasierte optimale Versuchsplanung und modellunabhängige, raumfüllende Sampling-Strategien.

  • Der erste Teil des Dokuments führt in die optimale Versuchsplanung ein, die darauf abzielt, einen Versuchsplan X\mathbf{X} für ein spezifisches statistisches Modell (z.B. Polynomregression) zu optimieren. Der Fokus liegt auf der Informationsmatrix M=XTX\mathbf{M} = \mathbf{X}^T \mathbf{X} und den davon abgeleiteten Optimalitätskriterien (D-, A-, I- und G-Optimalität), die jeweils unterschiedliche Aspekte der Modellgüte, wie die Parametervarianz oder die Vorhersagevarianz, optimieren.
  • Der zweite Teil vergleicht drei zentrale raumfüllende Sampling-Methoden für die Erstellung von Surrogatmodellen, wenn kein a-priori-Modell angenommen wird. Es werden das einfache Monte-Carlo-Sampling (MC), das Latin Hypercube Sampling (LHS) und Quasi-Monte-Carlo-Methoden (QMC) am Beispiel der Sobol-Sequenz detailliert. Der Vergleich hebt die Unterschiede in Uniformität, Konvergenzrate (O(N1/2)O(N^{-1/2}) für MC vs. O(N1(logN)s)O(N^{-1}(\log N)^s) für QMC) und Erweiterbarkeit hervor, wobei die Sobol-Sequenz die beste mehrdimensionale Raumabdeckung und Flexibilität bietet.
  • Der dritte Teil, “Sobol-Sequenzen ‘from scratch’”, bietet eine detaillierte technische Implementierung der Sobol-Sequenz. Es wird der Bratley-und-Fox-Algorithmus erklärt, der iterativ Sobol-Punkte generiert. Dieser Ansatz basiert auf primitiven Polynomen über dem Körper F2\mathbb{F}_2 , der Vorkalkulation von Richtungszahlen (Direction Numbers) und der effizienten Anwendung von bitweisen XOR-Operationen, um eine hochgradig uniforme Punktmenge zu erzeugen.

Inhaltsverzeichnis

  1. Optimale Versuchsplanung (Optimal Design)
  2. Sampling-Methoden für die Versuchsplanung (DoE)
  3. Sobol-Sequenzen ”from scratch”
  4. Algorithmus (Bratley & Fox)


1 Optimale Versuchsplanung (Optimal Design)

Im Gegensatz zu raumfüllenden Versuchsplänen (wie Latin Hypercube oder Sobol-Sequenzen), die modellunabhängig den gesamten Raum abtasten, ist die optimale Versuchsplanung (Optimal Design) modellbasiert. Das Ziel ist es, einen Versuchsplan X\mathbf{X} zu konstruieren, der ”optimal” für die Anpassung (Fitting) eines spezifischen statistischen Modells ist, typischerweise eines Polynommodells (z.B. linear, quadratisch oder mit Interaktionen).

1.1 Die Informationsmatrix

Der Kern der optimalen Versuchsplanung ist die Informationsmatrix M\mathbf{M}. Für ein Standard-Regressionsmodell y=Xβ+ϵy = \mathbf{X} \boldsymbol{\beta} + \boldsymbol{\epsilon}, wobei X\mathbf{X} die Versuchsplanmatrix (Designmatrix) ist, ist die Informationsmatrix definiert als:

M=XTX\mathbf{M} = \mathbf{X}^T \mathbf{X}

Die Inverse dieser Matrix, (XTX)1(\mathbf{X}^T \mathbf{X})^{-1}, ist proportional zur Varianz-Kovarianz-Matrix der geschätzten Modellparameter β^\hat{\boldsymbol{\beta}}.

Ein ”optimaler” Plan ist einer, der eine skalare Funktion dieser Matrix (oder ihrer Inversen) maximiert oder minimiert. Die verschiedenen Kriterien (D, A, I, G) definieren, welche Funktion optimiert wird.

1.2 Optimalitätskriterien

Es gibt mehrere gängige Kriterien, um die ”Güte” eines Versuchsplans basierend auf der Informationsmatrix M\mathbf{M} zu bewerten.

D-Optimalität

Ein D-optimaler Plan maximiert die Determinante der Informationsmatrix:

max(det(XTX))\max \left( \text{det}(\mathbf{X}^T \mathbf{X}) \right)

Dies ist äquivalent zur Minimierung der Determinante der Kovarianzmatrix (XTX)1(\mathbf{X}^T \mathbf{X})^{-1}. Geometrisch bedeutet dies, dass das Volumen des Konfidenzellipsoids für die Modellparameter β\boldsymbol{\beta} minimiert wird. D-Optimalität ist das am häufigsten verwendete Kriterium.

A-Optimalität

Ein A-optimaler Plan minimiert die Spur (Summe der Diagonalelemente) der inversen Informationsmatrix:

min(trace(XTX)1)\min \left( \text{trace}(\mathbf{X}^T \mathbf{X})^{-1} \right)

Da die Diagonalelemente der Kovarianzmatrix Var(β^i)\text{Var}(\hat{\beta}_i) entsprechen, minimiert die A-Optimalität die durchschnittliche Varianz der Parameterschätzungen β^\hat{\boldsymbol{\beta}}.

I-Optimalität (oder V-Optimalität)

Ein I-optimaler Plan minimiert die durchschnittliche Vorhersagevarianz über den gesamten Design-Space D\mathcal{D}:

min(DVar(y^(x))dx)\min \left( \int_{\mathcal{D}} \text{Var}(\hat{y}(\mathbf{x})) \, d\mathbf{x} \right)

Dieses Kriterium ist ideal, wenn das Hauptziel die präzise Vorhersage (Interpolation) innerhalb des Versuchsraums ist, und nicht notwendigerweise die präziseste Schätzung der einzelnen Parameter βi\beta_i.

G-Optimalität

Ein G-optimaler Plan folgt einem Minimax-Ansatz. Er minimiert die maximale Vorhersagevarianz im Design-Space D\mathcal{D}:

min(maxxDVar(y^(x)))\min \left( \max_{\mathbf{x} \in \mathcal{D}} \text{Var}(\hat{y}(\mathbf{x})) \right)

Dieser Ansatz ist sehr robust, da er darauf abzielt, den ”Worst-Case” (den Punkt im Raum mit der höchsten Vorhersageunsicherheit) zu kontrollieren und zu minimieren.

1.3 Modellbasiert vs. Raumfüllend

  • Optimale Pläne (D, A, G, I): Werden verwendet, wenn ein spezifisches Regressionsmodell (z.B. yβ0+β1x1+β2x2+β3x1x2y \sim \beta_0 + \beta_1 x_1 + \beta_2 x_2 + \beta_3 x_1 x_2) bekannt ist und effizient gefittet werden soll. Sie sind ideal für die Parameter-Screening und die Optimierung von Antwortflächen (Response Surface Methodology).

  • Raumfüllende Pläne (LHS, Sobol): Werden verwendet, wenn kein Modell a priori angenommen wird. Sie sind ideal für die allgemeine Exploration, die Erstellung von Surrogatmodellen (z.B. Gauss-Prozesse, Neuronale Netze) oder für Sensitivitätsanalysen.



2. Sampling-Methoden für die Versuchsplanung (DoE)

Die Erstellung eines Surrogatmodells (oder Metamodells) hat zum Ziel, eine rechenintensive ”Black-Box”-Funktion f(X)f(\mathbf{X}) durch ein rechengünstiges Näherungsmodell f^(X)\hat{f}(\mathbf{X}) zu ersetzen. Diese Funktion ff ist oft das Ergebnis einer komplexen Simulation (z.B. FEM, CFD). Sei unser Eingaberaum (Design-Space) DRs\mathcal{D} \subset \R^s, wobei ss die Anzahl der Dimensionen (Design-Parameter) ist. Für die Analyse normieren wir diesen Raum typischerweise auf den ss-dimensionalen Einheits-Hyperwürfel D=[0,1)s\mathcal{D} = [0, 1)^s.

Ein Versuchsplan (Design of Experiments, DoE) ist eine Menge von NN Stützpunkten X={X1,,XN}\mathbf{X} = \{\mathbf{X}_1, \dots, \mathbf{X}_N\}, an denen die teure Funktion ff ausgewertet wird. Das Ziel ist, diese NN Punkte so zu wählen, dass sie den Raum D\mathcal{D} ”optimal” abdecken (engl. space-filling), um ein möglichst genaues Surrogatmodell f^\hat{f} zu trainieren.

”Optimal” bedeutet hierbei vor allem:

  • Uniformität: Die Punkte sollen den Raum gleichmässig füllen, ohne Cluster (Ballungen) oder grosse Lücken (Lücken).
  • Orthogonalität: Die Punkte sollten möglichst unkorreliert sein, um eine Verwechslung der Einflüsse verschiedener Parameter zu vermeiden.

Im Folgenden werden drei fundamentale Sampling-Strategien mathematisch und konzeptionell detailliert.

2.1 Pseudo-Zufalls-Sampling (Monte Carlo, MC)

Das Monte-Carlo-Sampling (MC) ist die fundamentalste stochastische Methode. Ein Versuchsplan Xmat\mathbf{X}mat mit NN Punkten in ss Dimensionen wird generiert, indem N×sN \times s unabhängige Zufallszahlen ui,ju_{i,j} aus der stetigen Gleichverteilung U(0,1)\mathcal{U}(0, 1) gezogen werden.

Jede Koordinate jedes Punktes Xi=(xi,1,,xi,s)\mathbf{X}_i = (x_{i,1}, \dots, x_{i,s}) ist unabhängig von allen anderen:

xi,jU(0,1)i{1,,N},j{1,,s}x_{i,j} \sim \mathcal{U}(0, 1) \quad \forall i \in \{1,\dots,N\}, j \in \{1,\dots,s\}

Stellen Sie sich vor, Sie werfen eine Handvoll Sand (die NN Punkte) auf eine quadratische Platte (den 2D-Design-Space). Die Körner landen völlig zufällig. Es ist unvermeidlich, dass einige Bereiche dicht bedeckt sind (Cluster) und andere Bereiche fast leer bleiben (Lücken). Das zentrale Gütemass für die Gleichverteilung ist die Diskrepanz DND_N^*. Sie misst die maximale Abweichung zwischen dem ”Volumen” eines beliebigen Teilquaders (gemessen an der Anzahl der Punkte, die in ihn fallen) und seinem tatsächlichen geometrischen Volumen.

Für MC-Methoden ist der erwartete Fehler bei der numerischen Integration (ein verwandtes Problem) durch die Koksma-Hlawka-Ungleichung beschränkt. In der Praxis konvergiert der probabilistische Fehler der MC-Integration mit einer Rate von:

FehlerMC=O(1N)\text{Fehler}_{\text{MC}} = O\left(\frac{1}{\sqrt{N}}\right)

Diese Konvergenzrate ist (bemerkenswerterweise) unabhängig von der Dimension ss, aber sie ist sehr langsam. Um den Fehler zu halbieren, muss die Anzahl der Punkte NN vervierfacht werden.

  • Vorteile: Extrem einfach zu implementieren, probabilistisch unvoreingenommen (unbiased), trivial erweiterbar (neue Punkte können hinzugefügt werden, ohne die Statistik zu verletzen).
  • Nachteile: Sehr ineffizient. Erzeugt unvermeidlich Cluster und Lücken. Für eine gute Raumabdeckung ist ein sehr grosses NN erforderlich.

2.2 Latin Hypercube Sampling (LHS)

LHS ist eine stratifizierte (geschichtete) Zufallsstichprobe, die das Clustering-Problem von MC in den 1D-Projektionen löst. Die Generierung eines LHS-Plans mit NN Punkten in ss Dimensionen folgt drei Schritten:

  1. Stratifizierung: Jede der ss Dimensionen wird in NN gleichwahrscheinliche Intervalle (Strata) unterteilt: Ik=[k1N,kN)I_k = \left[\frac{k-1}{N}, \frac{k}{N}\right) für k=1,,Nk = 1, \dots, N.

  2. Permutation: Für jede Dimension jj wird eine zufällige Permutation (Umsortierung) πj\pi_j der Zahlen {1,,N}\{1, \dots, N\} erstellt.

  3. Generierung: Der ii-te Sample-Punkt Xi\mathbf{X}_i wird nun konstruiert. Die jj-te Koordinate des ii-ten Punktes (xi,jx_{i,j}) wird generiert, indem ein zufälliger Punkt aus dem πj(i)\pi_j(i)-ten Intervall der jj-ten Dimension gezogen wird.

    xi,j=πj(i)1+Ui,jNwobei Ui,jU(0,1)x_{i,j} = \frac{\pi_j(i) - 1 + U_{i,j}}{N} \quad \text{wobei } U_{i,j} \sim \mathcal{U}(0, 1)

Stellen Sie sich ein N×NN \times N Sudoku-Gitter (für 2D) oder NN Schachu-Türme auf einem N×NN \times N Brett vor. Das Ziel ist es, die Türme so zu platzieren, dass sich keine zwei Türme bedrohen. Das Ergebnis ist, dass in jeder Zeile und jeder Spalte genau ein Turm steht.

LHS stellt sicher, dass, wenn man den ss-dimensionalen Raum auf eine beliebige einzelne Achse (Dimension) projiziert, genau ein Punkt in jedes der NN Intervalle fällt. LHS garantiert eine perfekte, gleichmässige Abdeckung in allen 1D-Projektionen. Dies ist ein enormer Vorteil gegenüber MC.

  • Vorteile: Deutlich bessere Raumfüllung als MC. Erzwingt die Abtastung der Extrembereiche (der ”Ränder” des Design-Space). Reduziert die Varianz bei der Schätzung von Modell-Outputs.
  • Nachteile: Garantiert keine gute mehrdimensionale Uniformität. Die Punkte können sich immer noch auf Hyper-Diagonalen ansammeln, was zu unerwünschten Korrelationen im Versuchsplan führt. Die Erweiterbarkeit ist nicht gegeben; das Hinzufügen von MM neuen Punkten zu einem NN-Punkte-Plan ergibt keinen validen (N+M)(N+M)-Punkte-LHS-Plan.

2.3 Quasi-Monte-Carlo (QMC) / Sobol-Sequenz

QMC-Methoden verwenden deterministische Sequenzen mit geringer Diskrepanz (Low-Discrepancy Sequences, LDS), um die Nachteile von MC zu überwinden. Die Sobol-Sequenz ist eine der bekanntesten digitalen (t,m,s)(t, m, s)-Sequenzen. Die Sobol-Sequenz ist keine Zufallsstichprobe, sondern eine hochgradig strukturierte, unendliche Sequenz von Punkten. Ihre Konstruktion basiert auf der Arithmetik im endlichen Körper F2={0,1}\mathbb{F}_2 = \{0, 1\} (wobei Addition \equiv bitweises XOR \oplus ist).

  1. Richtungszahlen: Für jede Dimension jj (bis ss) wird ein Satz von Richtungszahlen Vj,k(0,1)V_{j,k} \in (0, 1) aus primitiven Polynomen über F2\mathbb{F}_2 generiert.

  2. Digitale Konstruktion: Um den nn-ten Punkt Xn=(xn,1,,xn,s)\mathbf{X}_n = (x_{n,1}, \dots, x_{n,s}) zu finden, wird der Index nn binär dargestellt: n=(bkb2b1)2n = (b_k \dots b_2 b_1)_2.

  3. XOR-Operation: Die jj-te Koordinate xn,jx_{n,j} wird durch eine bitweise XOR-Summe der Richtungszahlen gebildet, die den ‘1’-Bits in nn entsprechen:

    xn,j=(b1Vj,1)(b2Vj,2)(bkVj,k)x_{n,j} = (b_1 \cdot V_{j,1}) \oplus (b_2 \cdot V_{j,2}) \oplus \dots \oplus (b_k \cdot V_{j,k})

    (Der im Python-Code gezeigte Algorithmus ist eine schnellere, iterative Variante hiervon, die xnx_n aus xn1x_{n-1} berechnet.)

Stellen Sie sich das systematische Kacheln eines Badezimmerbodens vor. Sie werfen die Kacheln nicht zufällig (MC). Sie legen sie auch nicht nur in sauberen Reihen (was zu Korrelationen führt). Sie verwenden ein komplexes, deterministisches, aber nicht-periodisch wirkendes Muster (ähnlich einer Penrose-Parkettierung), das darauf ausgelegt ist, niemals Lücken zu hinterlassen. Jeder neue Punkt (Kachel) wird gezielt in die grösste verbleibende Lücke gelegt.

QMC-Sequenzen sind explizit darauf ausgelegt, die Diskrepanz DND_N^* zu minimieren. Der Fehler bei der numerischen Integration konvergiert für QMC theoretisch mit:

FehlerQMC=O((logN)sN)\text{Fehler}_{\text{QMC}} = O\left(\frac{(\log N)^s}{N}\right)

Diese Konvergenz ist deutlich schneller als O(N1/2)O(N^{-1/2}) von MC, solange die Dimension ss nicht zu gross ist (der Faktor (logN)s(\log N)^s ist der ”Fluch der Dimensionalität” für QMC).

  • Vorteile: Beste Uniformität und Raumfüllung im mehrdimensionalen Raum. Schnellste Konvergenzrate für Integration (und oft beste Surrogatmodell-Güte) bei niedrigen bis moderaten Dimensionen.
  • Erweiterbarkeit (Extensibility): Ein herausragender Vorteil. Eine Sobol-Sequenz ist unendlich. Man kann N=100N=100 Punkte berechnen, das Modell testen und bei Bedarf N=100N=100 weitere Punkte hinzufügen. Die resultierende Menge von 200 Punkten behält die Eigenschaft der geringen Diskrepanz bei.
  • Nachteile: Deterministisch. Könnte theoretisch mit einer periodischen Zielfunktion ”kollidieren”. Der theoretische Vorteil nimmt in sehr hohen Dimensionen (s>20-40s > 20 \text{-} 40) ab.

2.4 Tabellarischer Vergleich

Tabelle 1: Mathematischer und praktischer Vergleich der Sampling-Methoden

EigenschaftMonte Carlo (MC)Latin Hypercube (LHS)Sobol-Sequenz (QMC)
TypPseudo-ZufälligStratifiziert-ZufälligDeterministisch (Quasi-Zufall)
ZielProbabilistische AbdeckungGleichverteilung der 1D-ProjektionenMinimierung der ss-dim. Diskrepanz
RaumfüllungGering (Cluster/Lücken)Mittel (Keine 1D-Cluster)Sehr Hoch (Beste Uniformität)
ErweiterbarkeitJa (trivial)Nein (Neugenerierung)Ja (Haupteigenschaft)
Konvergenzrate (Integrationsfehler)O(N1/2)O(N^{-1/2}) (langsam, ss-unabhängig)Besser als MC, variabelO(N1(logN)s)\approx O(N^{-1} (\log N)^s) (sehr schnell bei kleinem ss)
Ideal fürRobuste Tests, sehr hohe Dimensionen (s50s \gg 50)Standard-DoE, wenn NN feststeht, UQSurrogatmodell-Training, Sensitivitätsanalyse (Sobol-Ind.)


3. Sobol-Sequenzen ”from scratch”

Eine Sobol-Sequenz ist eine sogenannte Quasi-Zufalls-Sequenz (auch low-discrepancy sequence oder Sequenz mit geringer Diskrepanz genannt). Im Gegensatz zu Pseudo-Zufallszahlen (PRNGs), wie sie in Standard-Monte-Carlo-Simulationen (MC) verwendet werden, zielt eine Quasi-Zufalls-Sequenz nicht darauf ab, statistischen Zufall zu imitieren. Das Hauptziel einer Sobol-Sequenz ist es, einen ss-dimensionalen Raum (oft den Einheits-Hyperwürfel [0,1)s[0, 1)^s) so gleichmässig wie möglich abzudecken.

  • Pseudo-Zufall (Monte Carlo): Erzeugt tendenziell Lücken (areas of under-sampling) und Cluster (areas of over-sampling), besonders bei geringer Punktzahl.
  • Quasi-Zufall (Sobol): Ist deterministisch so konstruiert, dass jeder neue Punkt gezielt in die grösste existierende Lücke ”fällt”.

Diese Eigenschaft der gleichmässigen Abdeckung führt zu einer signifikant schnelleren Konvergenz bei numerischen Integrationen (Quasi-Monte-Carlo, QMC) und einer effizienteren Abtastung von Design-Spaces im Design of Experiments (DoE). Die Sobol-Sequenz ist eine digitale Sequenz (Basis 2). Ihre Konstruktion basiert auf der Arithmetik im endlichen Körper (Galois-Feld) F2\mathbb{F}_2, der nur aus den Elementen {0,1}\{0, 1\} besteht, wobei die Addition dem bitweisen XOR (\oplus) entspricht.

3.1 Primitive Polynome

Die Grundlage für jede Dimension jj der Sobol-Sequenz ist ein sorgfältig ausgewähltes primitives Polynom über F2\mathbb{F}_2. Ein solches Polynom vom Grad dd hat die Form:

P(x)=xd+a1xd1+a2xd2++ad1x+1P(x) = x^d + a_1 x^{d-1} + a_2 x^{d-2} + \dots + a_{d-1} x + 1

wobei alle Koeffizienten aka_k entweder 0 oder 1 sind (akF2a_k \in \mathbb{F}_2). Diese Polynome sind die ”Generatoren” für die Sequenz in dieser Dimension.

3.2 Richtungszahlen (Direction Numbers)

Aus jedem primitiven Polynom wird ein Satz von Richtungszahlen Vj,iV_{j,i} abgeleitet. Dies sind LL-Bit-Integer (in der Python-Implementierung L=32L=32), die als die ”magischen Konstanten” des Algorithmus dienen.

Die Richtungszahlen werden über eine Rekurrenzrelation generiert, die direkt vom Polynom P(x)P(x) abhängt:

  1. Initialwerte: Die ersten dd Richtungszahlen m1,,mdm_1, \dots, m_d werden als ungerade ganze Zahlen <2i< 2^i gewählt. Diese sind die Konstanten, die im Python-Code als SOBOL_INIT_M gespeichert sind.

  2. Rekurrenzrelation: Für i>di > d wird mim_i über eine XOR-basierte Rekurrenz berechnet, die die Koeffizienten aka_k des Polynoms nutzt:

    mi=(2a1mi1)(22a2mi2)(2d1ad1mid+1)(2dmid)midm_i = (2a_1 m_{i-1}) \oplus (2^2 a_2 m_{i-2}) \oplus \dots \oplus (2^{d-1} a_{d-1} m_{i-d+1}) \oplus (2^d m_{i-d}) \oplus m_{i-d}

In der Praxis (und im Python-Code) werden diese mim_i als LL-Bit-Integer Vj,iV_{j,i} gespeichert, die bereits an die richtige Bit-Position verschoben (left-shifted) sind, um die Division durch 2L2^L am Ende zu vereinfachen.

4. Algorithmus (Bratley & Fox)

Der Algorithmus von Bratley & Fox ist eine hocheffiziente Methode zur Erzeugung von Sobol-Punkten. Er ist deterministisch und basiert vollständig auf bitweisen Operationen (XOR, Bit-Shifts).

  • Stufe 1 (Setup): Nutzt primitive Polynome und eine XOR-Rekurrenz, um die ”magischen” Richtungszahlen VV für jede Dimension zu berechnen.
  • Stufe 2 (Iteration): Nutzt den Index der rechtesten Null (cc) von n1n-1, um XnX_n aus Xn1X_{n-1} und der einzelnen Richtungszahl VcV_c zu berechnen.

Das Ergebnis ist eine Sequenz, die den ss-dimensionalen Raum hervorragend gleichmässig abdeckt und sich ideal für QMC-Methoden und die Exploration von Design-Spaces eignet. Der folgende Python-Code verwendet nicht die naive Implementierung (die den Index nn direkt mit allen Richtungszahlen Vj,iV_{j,i} per XOR verknüpft). Stattdessen nutzt er einen schnellen iterativen Algorithmus (basierend auf Bratley und Fox, ACM Trans. Math. Softw. 659), der den nn-ten Punkt SnS_n sehr effizient aus dem vorherigen Punkt Sn1S_{n-1} berechnet. Der Algorithmus besteht aus zwei Stufen.

4.1 Stufe 1: Vorkalkulation der Richtungszahlen (V)

Bevor die Sequenz generiert wird, berechnet der Code ein 2D-Array V[dimensions][MAX_BITS]\text{V[dimensions][MAX\_BITS]}. V[j][i]Vj,iV[j][i] \equiv V_{j,i} Dies ist die ii-te LL-Bit-Richtungszahl für die jj-te Dimension.

  1. Initialwerte (i<di < d): Die ersten dd Werte (aus SOBOL_INIT_M) werden geladen und an die korrekte Bit-Position (ganz links) verschoben.

    Vj,i=mi(L1i)for i=0,,d1V_{j,i} = m_i \ll (L - 1 - i) \quad \text{for } i = 0, \dots, d-1
  2. Rekurrenz (idi \ge d): Die restlichen Vj,iV_{j,i} werden mit der bit-verschobenen Version der Rekurrenzrelation (siehe oben) berechnet. Der Python-Code implementiert dies als:

    Vj,i=(Vj,id)(Vj,idd)k=1d1(akVj,id+k)V_{j,i} = (V_{j,i-d}) \oplus (V_{j,i-d} \gg d) \oplus \bigoplus_{k=1}^{d-1} (a_k \cdot V_{j,i-d+k})

    (Wobei akVa_k \cdot V bedeutet: VV, falls ak=1a_k=1, und 00, falls ak=0a_k=0. Dies wird im Code durch die Bit-Maskierung von temp_a erreicht.)

Stufe 1: Vorkalkulation der Richtungszahlen

4.2 Stufe 2: Generierung der Sequenzpunkte

Der Kern des schnellen Algorithmus ist die iterative Erzeugung des nn-ten Punktes SnS_n.

Wir verwalten einen ss-dimensionalen Vektor von LL-Bit-Integern, XnX_n, der den ”Zustand” der Sequenz darstellt. Der normalisierte Punkt SnS_n ist einfach Sn=Xn/2LS_n = X_n / 2^L.

  1. Initialisierung: Der nullte Punkt S0S_0 ist der Ursprung.

    X0=(0,0,,0)X_0 = (0, 0, \dots, 0)
  2. Iteration (für n=1,2,,Nn = 1, 2, \dots, N): Um XnX_n aus Xn1X_{n-1} zu erhalten, wird eine einzige Richtungszahl pro Dimension verwendet.

    Xn=Xn1VcX_n = X_{n-1} \oplus V_c
  3. Finden des Index cc: Der entscheidende Schritt ist die Bestimmung des Index cc. Dieser Index cc ist definiert als:

    Der Index des rechtesten Null-Bits (rightmost zero-bit) in der Binärdarstellung von n1n-1.

    Beispiele:

    • n=1n=1: n1=0n-1 = 0 (0b000). Rechteste 0 ist bei Index c=0c=0.
    • n=2n=2: n1=1n-1 = 1 (0b001). Rechteste 0 ist bei Index c=1c=1.
    • n=3n=3: n1=2n-1 = 2 (0b010). Rechteste 0 ist bei Index c=0c=0.
    • n=4n=4: n1=3n-1 = 3 (0b011). Rechteste 0 ist bei Index c=2c=2.
    • n=5n=5: n1=4n-1 = 4 (0b100). Rechteste 0 ist bei Index c=0c=0.

    (Der Python-Code findet cc durch die while (k & 1)-Schleife.)

  4. Anwendung des XOR: Der nn-te Punkt XnX_n wird nun für jede Dimension jj separat berechnet, wobei derselbe Index cc verwendet wird:

    Xn,j=Xn1,jVj,cX_{n,j} = X_{n-1, j} \oplus V_{j,c}

    Der Unterschied zwischen den Dimensionen entsteht also, weil Vj,cV_{j,c} (das Array der Richtungszahlen) für jede Dimension jj einzigartig ist.

  5. Normalisierung: Der finale Punkt SnS_n für die Ausgabe wird durch Normalisierung gewonnen:

    Sn,j=Xn,j(1.0/2L)S_{n,j} = X_{n,j} \cdot (1.0 / 2^L)

Stufe 2: Generierung der Sequenzpunkte

4.3 Python Implementierung (Bratley & Fox)

import numpy as np
 
 # --- KONSTANTEN (Die ''Magischen Zahlen'' von Joe & Kuo, bis dim 6) ---
 # Diese Daten sind die ''Black Box'', die aus der mathematischen Theorie stammt.
 # new-joe-kuo-6-21201.txt
 
 # d = Grad des primitiven Polynoms
 SOBOL_DEGREES = [1, 2, 3, 3, 4, 4]
 # a = Koeffizienten des Polynoms (als einzelne Ganzzahl kodiert)
 SOBOL_COEFFS = [0, 1, 1, 3, 1, 3]
 # m = Die initialen Richtungszahlen (m_1, ..., m_d)
 SOBOL_INIT_M = [
 [1],
 [1, 3],
 [1, 3, 1],
 [1, 1, 1],
 [1, 1, 3, 3],
 [1, 3, 5, 13],
 ]
 # Maximale Anzahl an Bits (bestimmt die Praezision und Periodizitaet)
 MAX_BITS = 32
 # Normalisierungsfaktor (um von Integer auf [0, 1) zu kommen)
 NORMALIZER = 1.0 / (2**MAX_BITS)
 
 
 def sobol_sequence_from_scratch(num_points: int, dimensions: int) -> np.ndarray:
	 ''''''
	 Generiert eine Sobol-Sequenz ''from scratch'' unter expliziter
	 Verwendung von bitweisen Operationen und Richtungszahlen.
	 Basiert auf den Daten von S. Joe und F. Y. Kuo.
	 ''''''
	 
	 if dimensions > len(SOBOL_DEGREES):
		 raise ValueError(
		 f''Maximale Dimension {len(SOBOL_DEGREES)} ueberschritten. ''
		 ''Fuer mehr Dimensionen muessen die Konstanten erweitert werden.''
		 )
	 
	 # --- 1. VORBEREITUNG: GENERIERE DIE RICHTUNGSZAHLEN (V) ---
	 # V[j][i] ist die i-te Richtungszahl fuer die j-te Dimension.
	 # Wir berechnen sie aus den Konstanten (Polynomen).
	 
	 V = np.zeros((dimensions, MAX_BITS), dtype=np.uint64)
	 
	 for j in range(dimensions):
		 d = SOBOL_DEGREES[j]
		 a = SOBOL_COEFFS[j]
		 m = SOBOL_INIT_M[j]
	 
		 # 1a. Setze die initialen Werte (m_1 ... m_d)
		 # Wir muessen sie an die richtige Bit-Position schieben
		 for i in range(d):
			 # m_i wird zu V[j][i], nach links geshiftet
			 V[j, i] = np.uint64(m[i]) << (MAX_BITS - 1 - i)
		 
		 # 1b. Generiere die restlichen V's (i > d) ueber die Rekurrenzrelation
		 # V_i = a_1 V_{i-1} ^ a_2 V_{i-2} ^ ... ^ a_d V_{i-d} ^ (V_{i-d} >> d)
		 for i in range(d, MAX_BITS):
			 # V_{i-d} ^ (V_{i-d} >> d)
			 V[j, i] = V[j, i - d] ^ (V[j, i - d] >> d)
			 
			 # Wende die Koeffizienten 'a' an
			 temp_a = a
			 for k in range(d - 1):
				 # Wenn das k-te Bit von 'a' gesetzt ist, XOR anwenden
				 if (temp_a & 1):
					 V[j, i] = V[j, i] ^ V[j, i - (d - 1) + k]
					 temp_a >>= 1
	 
	 # --- 2. GENERIERUNG DER SEQUENZ ---
	 
	 # `X` speichert den aktuellen Punkt als Integer-Wert
	 X = np.zeros(dimensions, dtype=np.uint64)
	 
	 # `points` speichert die finale Sequenz (normalisierte Floats)
	 points = np.zeros((num_points, dimensions))
	 
	 # Der erste Punkt (i=0) ist immer der Ursprung [0, 0, ...]
	 # (bleibt bei points[0] als Nullen stehen)
	 
	 for i in range(1, num_points):
		 # 2a. Finde den Index 'c' des rechtesten Null-Bits in (i-1)
		 # Beispiel: i=1 -> (i-1)=0 (0b000) -> c=0
		 #          i=2 -> (i-1)=1 (0b001) -> c=1
		 #          i=3 -> (i-1)=2 (0b010) -> c=0
		 #          i=4 -> (i-1)=3 (0b011) -> c=2
		 
		 c = 0
		 k = i - 1  # (i-1) als Binaerzahl betrachten
		 while (k & 1):
		 	k >>= 1
		 	c += 1
		 	if c >= MAX_BITS:
		 		# Sollte bei normaler Nutzung nicht passieren
		 		print(f''Warnung: MAX_BITS ({MAX_BITS}) erreicht bei Punkt {i}'')
		 		continue
	 
	 # 2b. EXPLIZITE OPERATION: Generiere den neuen Punkt X_i
	 # X_i = X_{i-1} XOR V[c]
	 # (Wir wenden dies auf jede Dimension an)
	 for j in range(dimensions):
	 	X[j] = X[j] ^ V[j, c]
	 
	 # 2c. Speichere den normalisierten Punkt
	 # (Integer-Wert geteilt durch 2^MAX_BITS)
	 points[i, :] = X * NORMALIZER
	 
 return points