diff --git a/_config.yml b/_config.yml
index aaed9fdf7219baa6635f762228068341ba9179c1..00aa7f21783ee635e250f2c7fedce2254413d6a9 100644
--- a/_config.yml
+++ b/_config.yml
@@ -53,7 +53,7 @@ sphinx:
     execution_show_tb: True
     latex_elements:
       preamble: |+
-        \pgfplotsset{compat=1.17}
+        \pgfplotsset{compat=1.18}
     language: de
     html_css_files: ["custom.css"]
     bibtex_bibfiles:
diff --git a/_toc.yml b/_toc.yml
index 3ff3c9243db368375092ce64604ff7e27a9b6a0b..01021af93e0d986f0cbb208028356328fc73a810 100644
--- a/_toc.yml
+++ b/_toc.yml
@@ -7,3 +7,10 @@ parts:
   - file: was-ist-data-science
   - file: intro-stochastik
   - file: intro-python
+- caption: Diskrete Stochastik und Numpy
+  numbered: true
+  chapters:
+  - file: stochastik-programmieren
+  - file: zufallsvariablen-verteilungen
+  - file: erwartungswert
+  - file: numpy
diff --git a/erwartungswert.md b/erwartungswert.md
new file mode 100644
index 0000000000000000000000000000000000000000..c0a1b8d1785295df11994fd8c3ea34c53307ae8a
--- /dev/null
+++ b/erwartungswert.md
@@ -0,0 +1,313 @@
+# Erwartungswert und Varianz
+
+## Abweichung quantifizieren
+
+Gegeben zwei Vektoren $x,y \in \mathbb{R}^n$ (wir stellen uns vor: $x$ sind unsere Daten, $y$ das, was wir erwarten),
+ist die mittlere Abweichung interessant: $\sum \frac{x_i - y_i}{n}$.
+Ein Problem dieser Größe ist, dass sich positive mit negativen Abweichungen herauskürzen.
+
+### Absolute Abweichung
+
+:::{admonition} Definition
+Die *mittlere absolute Abweichung* (mean absolute error) ist
+$\texttt{mae}(v,w) := \sum_{i=1}^n \dfrac{\left| v_i - w_i \right|}{n}$.
+
+Für eine Zahl $c \in \mathbb{R}$ schreiben wir auch $\texttt{mae}(v,c):=\texttt{mae}(v,(c)_{i=1}^n)$.
+:::
+
+:::{admonition} Beispiel
+Wenn $v = (1,2,3,4)$ und $c=2$, so ist $\texttt{mae}(v,c) = \sum_{i=1}^4 \dfrac{|i-2|}{4} = \dfrac{4}{4} = 1$.
+:::
+
+Es ist natürlich sofort für einen Vektor $v$ interessant, welcher Vektor $w$ die geringste mittlere absolute Abweichung hat. Wenn man an $w$ jedoch keine weiteren Bedingungen stellt, ist mit $w=v$ das Minimum schnell gefunden. Wir werden dieser Frage später wieder begegnen und begnügen uns jetzt damit, zu fragen, welches $c \in \mathbb{R}$ den Ausdruck $\texttt{mae}(v,c)$ minimiert. Graphisch können wir uns vorstellen, als $x$-Achse die Zahlen von $1$ bis $n$ zu verwenden, und dort jeweils die Werte $v_i$ aufzutragen. Dann entspricht die Wahl eines $c \in \mathbb{R}$ einer horizontalen Gerade, und wir fragen uns, welche solche Gerade den geringsten gemeinsamen Abstand zu allen Punkten hat.
+
+:::{admonition} Definition
+$\texttt{median}(v) := \min_{c \in \mathbb{R}} \texttt{mae}(v,c)$ ist \`\`der'' *Median* von $v$.
+:::
+
+Diese Definition mag ungewohnt sein, aber sie erfüllt die folgende Eigenschaft:
+
+:::{admonition} Proposition
+Wenn $v \in \mathbb{R}^n$ sortiert ist, also $v_i < v_j$ für $ < j$, dann ist mit $v' := (v_2,\dots,v_{n-1}) \in \mathbb{R}^n$ ein neuer (kürzerer) Vektor definiert, sodass für alle $c \in \mathbb{R}$ mit $v_1 \leq c \leq v_n$ gilt:
+
+$\texttt{mae}(v,c) = \texttt{mae}(v',c) + \dfrac{v_n - v_1}{n}$.
+:::
+
+Da der zweite Summand nicht von $c$ abhängt, ist er für die Bestimmung des Minimums unerheblich.
+Durch induktive Anwendung dieser Erkenntnis bleibt uns nur noch, $\texttt{mae}(v,c)$ für Vektoren der Länge $1$ und $2$ zu bestimmen.
+Es ist $\texttt{mae}((v_1),c) = |v_1 - c|$, was für $c = v_1$ minimal wird,
+und $\texttt{mae}((v_1,v_2),c) = \frac{v_2 - v_1}{2}$, was für jedes $c$ zwischen $v_1$ und $v_2$ minimal wird. Also hängt der Median (einer sortierten Liste) nur vom mittleren / von den zwei mittleren Werten ab. Man definiert meist den Median so, dass in diesem Fall die Wahl $\texttt{median}((v_1,v_2)) = \dfrac{v_1+v_2}{2}$ getroffen wird (und so wollen wir es im Folgenden auch handhaben).
+
+:::{admonition} Beweis
+$$
+\texttt{mae}(v,c) &= \phantom{\dfrac{|v_1 - c|}{n} +\ } \sum_{i=1}^{n} \dfrac{|v_i -c |}{n} \\
+                  &= \dfrac{|v_1 - c|}{n} + \sum_{i=2}^{n-1} \dfrac{|v_i -c |}{n} + \dfrac{|v_n - c|}{n} \\
+                  &= \sum_{i=2}^{n-1} \dfrac{|v_i -c |}{n} + \dfrac{-(v_1 - c) + (v_n - c)}{n} \\
+                  &= \texttt{mae}(v',c) + \dfrac{v_n - c + c - v_1}{n}.
+$$
+:::
+
+Da der Median also relativ schlecht die Daten wiederspiegelt (von den meisten Daten ist er völlig unabhängig!), brauchen wir ein besseres Maß.
+*Idee*: wir quadrieren die individuellen Fehlerterme in der Summe, damit große Fehler einen größeren Teil zur Summe beitragen. Praktisch: durch das Quadrieren muss man nicht mehr explizit den Absolutbetrag nehmen (das geschieht automatisch).
+
+### Quadratische Abweichung
+
+:::{admonition} Definition
+Für Vektoren $v,w \in \mathbb{R}^n$ ist die *mittlere quadratische Abweichung* (mean squared error) definiert als
+
+$$
+\texttt{mse}(v,w) := \sum_{i=1}^n \dfrac{{\left(v_i - w_i\right)}^2}{n}
+$$
+
+und für $c \in \mathbb{R}^n$ schreiben wir $\texttt{mse}(v,c) := \texttt{mse}(v, (c)_{i=1}^n)$.
+:::
+
+:::{admonition} Proposition
+Für einen Vektor $v \in \mathbb{R}^n$ minimiert der (arithmetische) *Mittelwert* von $v$ die mittlere quadratische Abweichung:
+
+$\texttt{mean}(v) := \sum_{i=1}^n \dfrac{v_i}{n} = \min_{c \in \mathbb{R}} \texttt{mse}(v,c)$.
+:::
+
+:::{admonition} Beweis
+Wir schreiben ein beliebiges $c \in \mathbb{R}$ als $c = \texttt{mean}(v) + \delta$ und zeigen dann, dass $\texttt{mse}(v,c)$ minimal wird für $\delta = 0$.
+Dazu faktorisieren wir aus: $(v_i - \texttt{mean}(v) - \delta)^2 = (v_i - \texttt{mean}(v))^2 - 2\delta (v_i - \texttt{mean}(v)) + \delta^2$ und teilen die Summe entsprechend auf:
+
+$$
+\texttt{mse}(v,c) &= \texttt{mse}(v,\texttt{mean}(v) + \delta) = \sum_{i=1}^n \dfrac{(v - (\texttt{mean}(v) + \delta))^2}{n} \\
+                  &= \dfrac{1}{n} \sum_{i=1}^n \left( {\left(v_i - \texttt{mean}(v)\right)}^2 - 2\delta \left(v_i - \texttt{mean}(v)\right) + \delta^2 \right) \\
+                  &= \left( \dfrac{1}{n} \sum_{i=1}^n \left( (v_i - \texttt{mean}(v))^2 \right) \right) - 2\delta \left( \sum_{i=1}^n \frac{v_i}{n} - \texttt{mean}(v) \right) + \delta^2 \\
+                  &= \texttt{mse}(v,\texttt{mean}(v)) - 2\delta(\texttt{mean}(v) - \texttt{mean}(v)) + \delta^2 \\
+                  &= \texttt{mse}(v,\texttt{mean}(v)) + \delta^2
+$$
+
+und dieser Ausdruck wird nur für $\delta=0$ minimal, weil $\delta^2 \geq 0$.
+:::
+
+
+### Sample-Standardabweichung
+
+:::{admonition} Definition
+Sei $v \in \mathbb{R}^n$. Die Quadratwurzel der mittleren quadratischen Abweichung (root mean squared error) vom Mittelwert nennen wir $\texttt{rmse}$ (oder auch Sample-Standardabweichung):
+
+$\texttt{rmse}(v) = \sqrt{\texttt{mse}\left( v, \texttt{mean}(v) \right)}$.
+:::
+
+:::{admonition} Proposition
+Für $v \in \mathbb{R}^n$ mit $\texttt{mean}(v)=0$ (ein *zentriertes* Sample, etwa $v := w - (\texttt{mean}(w))_{i=1}^n$ für ein beliebiges $w \in \mathbb{R}^n$) 
+ist $\texttt{rmse}(v)^2 = \texttt{mse}(v,0) = \frac{1}{n} \sum_{i=1}^n v_i^2$ und der Vektor $v' := {\left( \dfrac{v_i}{\texttt{rmse}(v)} \right)}_{i=1}^n$ erfüllt $\texttt{mean}(v')=0$ und $\texttt{rmse}(v')=1$ (ein *normiertes* Sample).
+:::
+
+:::{admonition} Beweis
+Übungsaufgabe.
+:::
+
+### Informationsgehalt
+
+Ob ein Vektor $v \in \mathbb{R}^n$ für uns Informationen enthält, hängt davon ab, wie viel wir über $v$ bereits wissen.
+Wenn wir z.B. wissen, dass $v$ ein Sample einer auf $\{0,1\}$ gleichverteilten Zufallsvariablen ist, wird uns nicht überraschen, wenn $v$ auch so aussieht. Jeder Eintrag von $v$ entspricht genau einem Bit Information. Wenn aber die Zufallsvariable nicht gleichverteilt ist, sondern z.B. auf den Zahlen $\{1,\dots, 26\}$ der Verteilungshäufigkeit der Buchstaben in der deutschen Sprache entspricht (mit $1$ für "a", $26$ für "z" usw.), so enthält ein Eintrag von $v$ potentiell weniger Informationen. Das "e" ist so häufig, dass wir wenig überrascht sind, wenn wir eins sehen. Ein "x" hingegen würde uns sehr überraschen, aber das kommt ja auch nicht so oft vor. Der mittlere Informationsgehalt ist also geringer als bei einer Gleichverteilung.
+
+
+
+
+## Momente einer Zufallsvariable
+
+### Erwartungswert, allgemein
+
+:::{admonition} Definition
+Sei $X \colon \Omega \to \mathbb{R}$ eine reelle Zufallsvariable, dann ist der *Erwartungswert* definiert als
+
+$\mathbb{E}X := \mathbb{E}(X) := \int_{\Omega} X dP = \int_{\Omega} X(\omega) d P(\omega) \in \mathbb{R}$
+
+also als das Integral der Abbildung $X$ bezüglich des Maßes $P$.
+:::
+
+Dieses Integral existiert im Allgemeinen nicht, d.h. es gibt Zufallsvariablen, für die sich kein Erwartungswert bestimmen lässt.
+
+### Erwartungswert, diskret
+
+Wenn $X \colon \Omega \to \mathbb{R}$ eine diskrete reelle Zufallsvariable ist (also $\Omega$ ein diskreter Wahrscheinlichkeitsraum, höchstens abzählbar),
+so ist $\mathbb{E}(X) = \sum_{\omega \in \Omega} X(\omega) P({\omega})$ (das Integral vereinfacht sich zu einer Summe).
+
+Wenn man nicht "in $\Omega$" arbeiten möchte, kann man diese Summe auch umstellen und über alle Werte $\{x_i\}_{i\in I}$ summieren:
+$\mathbb{E}(X) = \sum_{i \in I} x_i P(X = x_i)$.
+
+:::{admonition} Beispiel
+Der Erwartungswert einer Gleichverteilung auf $\Omega = \{1,\dots,n\}$ ist $\sum_{i=1}^n \frac{i}{n}$, also $\frac{n(n+1)}{2n} = \frac{n+1}{2}$.
+:::
+
+Es gibt einen inneren Zusammenhang zwischen dem Erwartungswert und den Stichproben einer Zufallsvariable:
+
+:::{admonition} Satz
+**Gesetz der großen Zahlen**
+
+Ist eine unendliche Folge $(X_i)_{i \in \mathbb{N}}$ von Zufallsvariablen $\Omega \to \mathbb{R}$ gegeben, so sagt man, dass diese Folge einem *starken Gesetz der großen Zahlen* genügt, wenn die Mittelwert-Zufallsvariablen $\overline{X_n} := \dfrac{1}{n} \sum_{i=1}^n \left( X_i - \mathbb{E}(X_i) \right)$ gegen $0$ konvergieren, und zwar $P$-fast sicher (d.h. $P(\lim_{n\to\infty} \overline{X_n} = 0) = 1$).
+
+Wenn die $X_i$ nun unabhängig und identisch verteilt sind ($X_i = X$ für festes $X$) mit existierendem Erwartungswert $\mathbb{E}(X)$,
+dann genügen die $X_i$ einem starken Gesetz der großen Zahlen, d.h.
+dann konvergiert $\overline{X_n}$, die Abweichung des arithmetischen Mittels einer Stichprobe von $X$ der Größe $n$ vom Erwartungswert, $P$-fast sicher gegen $0$.
+Das bedeutet umgekehrt auch: Die Wahrscheinlichkeit, dass das Stichprobenmittel für beliebig große $n$ vom Erwartungswert abweicht, ist $0$.
+:::
+
+Aus der $P$-fast sicheren Konvergenz folgt auch die stochastische Konvergenz:
+$\lim_{n\to\infty} P\left(|\overline{X_n}| \geq \epsilon \right) = 0$ für jedes $\epsilon > 0$.
+
+Wir wollen uns im Folgenden nicht mehr über Konvergenzbegriffe der Stochastik den Kopf zerbrechen (das kann man bei Bedarf immer noch nachholen und das lohnt sich auch je nach Anwendung), sondern in gewohnter Tradition das gute Gefühl mitnehmen, dass der Erwartungswert eine geeignete Größe ist, um erwartungsvoll eine Stichprobe zu analysieren. Je größer die Stichprobe, desto geringer dürfte die Abweichung des Stichprobenmittels vom Erwartungswert sein - so will es das Gesetz. Umgekehrt findet man also zu jeder vorgegebenen Schranke $\epsilon > 0$ eine Stichprobengröße $n$ sodass das Stichprobenmittel einer zufällig gezogenen Stichprobe fast immer um weniger als $\epsilon$ vom Erwartungswert abweicht.
+
+
+:::{admonition} Lemma
+**Rechenregeln für den Erwartungswert:**
+
+0 . Der Erwartungswert ist linear in $X$, also für $\alpha,\beta \in \mathbb{R}$ und $X,Y \colon \Omega \to \mathbb{R}$ Zufallsvariable ist
+
+$$
+\mathbb{E}\left( \alpha X + \beta Y \right) = \alpha \mathbb{E} X + \beta \mathbb{E}Y.
+$$
+
+1. Der Erwartungswert ist monoton in $X$, also für $X \leq Y$ ist $\mathbb{E}X \leq \mathbb{E}Y$.
+
+2. $\sigma$-Additivität: für eine Folge $X_i \geq 0$ ist $\mathbb{E}(\sum_{i \geq 1} X_i) = \sum_{i \geq 1} \mathbb{E}(X_i)$.
+
+3. Produktregel: Sind $X$ und $Y$ unabhängig, so ist $\mathbb{E}(XY) = \mathbb{E}(X)\mathbb{E}(Y)$.
+
+**Ohne Beweis**
+:::
+
+Dabei haben wir noch nicht erklärt, was *unabhängig* bedeutet (unabhängig von den pmf im diskreten Fall), und werden das auch erst weiter unten behandeln.
+
+
+
+
+### Varianz
+
+:::{admonition} Definition
+Sei $X \colon \Omega \to \mathbb{R}$ eine Zufallsvariable. Dann heißt
+
+$\mathbb{V}X := \mathbb{V}(X) := Var(X) := \mathbb{E}\left( {\left(X - \mathbb{E}X\right)}^2 \right)$ die *Varianz* von $X$.
+:::
+
+:::{admonition} Proposition
+Es ist $\mathbb{V}(X) = \mathbb{E}\left( X^2 \right) - {\left(\mathbb{E}X\right)}^2$.
+:::
+
+:::{admonition} Beweis
+Es ist ${\left(X - \mathbb{E}X\right)}^2 = X^2 - 2X\mathbb{E}X + {\left(\mathbb{E}X\right)}^2$ und mit der Linearität des Erwartungswerts also
+
+$$
+\mathbb{V}(X) &= \mathbb{E}\left( {\left(X - \mathbb{E}X\right)}^2 \right) \\
+              &= \mathbb{E}\left( X^2 \right) - 2\mathbb{E}\left( X \right)\mathbb{E}\left( X \right) + {\left(\mathbb{E}X\right)}^2 \mathbb{E}\left( 1 \right) \\
+              &= \mathbb{E}\left( X^2 \right) - {\left(\mathbb{E}X\right)}^2.
+$$
+:::
+
+Für eine zentrierte Zufallsvariable, d.h. mit $\mathbb{E}(X) = 0$, ist offensichtlich $\mathbb{V}(X) = \mathbb{E}(X^2)$.
+
+:::{admonition} Definition
+Man nennt allgemein auch $\mathbb{E}(X^n)$ das $n$-te *Moment* und $\mathbb{E}((X - \mathbb{E}X)^n)$ das $n$-te *zentrale Moment*.
+Der Erwartungswert ist also das erste Moment und die Varianz das zweite zentrale Moment.
+Das dritte zentrale Moment heißt auch *Schiefe* (skewness) und das fünfte *Wölbung*.
+Die Schiefe ist ein Maß dafür, wie symmetrisch die Verteilung um den Erwartungswert ist.
+Man nennt $\sigma(X) := \sqrt{\mathbb{V}(X)}$ die *Standardabweichung* von X.
+:::
+
+Die Varianz hilft uns bei der Einschätzung, wie wahrscheinlich gewisse Abweichungen vom Erwartungswert sind.
+
+:::{admonition} Satz
+**(Tschebyscheff-Ungleichung)**
+
+Für $X$ eine reelle Zufallsvariable, $c \in \mathbb{R}$ ist
+
+$$
+P\left( | X - \mathbb{E} X | \geq c \right) \leq \dfrac{\mathbb{V}X}{c^2}.
+$$
+
+**ohne Beweis**.
+:::
+
+Dabei ist der Spezialfall $c=\sigma$ trivial, weil auf der rechten Seite nur $1$ steht, und jede Wahrscheinlichkeit $\leq 1$ ist.
+Der Spezialfall $c = k\sigma$ mit $k \in \mathbb{N}$ vereinfacht sich allerdings zu
+
+$$
+P\left( | X - \mathbb{E} X | \geq k\sigma \right) \leq \dfrac{1}{k^2}.
+$$
+
+Daran sehen wir: Abweichungen von $2\sigma$ haben eine Wahrscheinlichkeit $\leq \frac{1}{4}$, und Abweichungen von $3\sigma$ bereits nur noch $\leq \frac{1}{9}$. Wenn wir also Abweichungen in Vielfachen von $\sigma$ messen, so ist die Wahrscheinlichkeit für so große Abweichungen näherungsweise umgekehrt quadratisch proportional (so steht's ja oben schon in der Ungleichung). Damit haben wir auch ein Kriterium zur Hand, um in Daten fehlerhafte Datenpunkte auszusortieren - wenn Abweichung größer $k\sigma$ mit $k > 1$ häufiger als erwartet zu sehen sind, dann gehören diese Datenpunkte zumindest nicht zu der Verteilung, die man erwartet.
+
+Wendet man das Gesetz der großen Zahlen auf die Zufallsvariable ${\left(X-\mathbb{E}(X)\right)}^2$ an, so sieht man, dass auch die Standardabweichung gegen die Sample-Standardabweichung konvergiert. Damit ist also die Sample-Standardabweichung ein guter Ersatz, wenn sich die wahre Varianz der Zufallsvariable nicht bestimmen lässt.
+
+
+### Entropie
+
+:::{admonition} Definition
+Bei einem diskreten Wahrscheinlichkeitsraum $\Omega$ nennen wir für $x \in \Omega$ die *Information* von $x$
+
+$$
+I(x) := -\log_b(P(\{x\}).
+$$
+
+Dabei ist die Basis $b$ des Logarithmus in der Regel $2$, aber letztlich nicht so wichtig.
+Für eine Zufallsvariable $X \colon \Omega' \to \Omega$ können wir nun die Verkettung $I \circ X \colon \Omega' \to \mathbb{R}$ betrachten,
+eine reelle Zufallsvariable. Ihr Erwartungswert ist die mittlere Information von $X$, wir sagen auch *Entropie*
+
+$$
+H(X) := \mathbb{E}\left( I(X) \right) &= \sum_{\omega' \in \Omega'} -\log_b(P(\{X(\omega')\})P(X(\omega')) \\
+                                      &= - \sum_{\omega \in \Omega} \log_b(P(\{\omega\}))P(\{\omega\})
+$$
+:::
+
+:::{admonition} Beispiel
+Bei einer Gleichverteilung auf $\Omega = \{1,\dots,n\}$ ist für $x\in\Omega$ stets $P(\{x\}) = \frac{1}{n}$,
+also $I(x) = -\log_2(P(\{x\})) = -\log_2\frac{1}{n} = \log_2(n)$ und somit
+$H(X) = \sum_{i=1}^n \frac{\log_2(n)}{n} = \log_2(n)$.
+Diese Zahl verrät uns, dass wir im Mittel $\log_2(n)$ bits an Information brauchen, um ein Sample der Größe $n$ zu rekonstruieren, wenn wir die Verteilung von $X$ kennen. Es gibt andere Verteilungen, die es erlauben mit im Mittel weniger Information ein Sample zu rekonstruieren. Genau genommen sogar jede, denn die Gleichverteilung hat maximale Entropie unter allen diskreten Verteilungen. Sie korrespondiert dazu, dass wir maximal wenig wissen und daher alle Ergebnisse als gleich wahrscheinlich annehmen.
+:::
+
+Am besten versteht man die Entropie als eine relative Größe, und das werden wir später noch tun.
+
+
+### Unabhängigkeit
+
+:::{admonition} Definition
+Sei $\Omega$ ein Wahrscheinlichkeitsraum. Ereignisse $A,B \subseteq \Omega$ heißen *voneinander unabhängig*, wenn
+
+$$
+P(A\cap B) = P(A) \cdot P(B).
+$$
+
+Allgemeiner heißt eine Familie $(A_i)_{i\in I}$, $A_i \subseteq \Omega$ von Ereignissen *unabhängig*, wenn
+für jede endliche Teilindexmenge $J \subseteq I$ gilt:
+
+$$
+P\left( \bigcap_{j \in J} A_j \right) = \prod_{j\in J} P(A_j).
+$$
+:::
+
+Dabei macht es im Allgemeinen einen echten Unterschied, ob eine Familie unabhängig oder nur paarweise unabhängig ist, daher ist die Definition mit Familien wichtig.
+
+:::{admonition} Definition
+Sei $\Omega$ ein Wahrscheinlichkeitsraum.
+Eine Familie von Zufallsvariablen $(Y_i)_{i \in I}$, $Y_i \colon \Omega \to \Omega_i$ heißt *unabhängig*, wenn
+für beliebige Ereignisse $B_i \subseteq \Omega_i$ die Familie $\left( \{Y_i \in B_i\} \right)_{i \in I}$ unabhängig in $\Omega$ ist.
+:::
+
+:::{admonition} Beispiel
+Für $I = \{1,\dots,n\}$ und $\Omega_i$ jeweils diskret ist $Y_1,\dots,Y_n$ unabhängig genau dann wenn für alle Ergebnisse $\omega_i \in \Omega_i$ gilt:
+
+$$
+P(Y_1=\omega_1, \dots, Y_n=\omega_n) = \prod_{i=1}^n P(Y_i = \omega_i).
+$$
+:::
+
+In diesem Zusammenhang taucht auch sehr oft ein weiterer Begriff auf:
+
+:::{admonition} Definition
+Sei $\Omega$ ein Wahrscheinlichkeitsraum. Für Ereignisse $A,B \subseteq \Omega$ heißt
+
+$$
+P(A | B) := \dfrac{P(A\cap B)}{P(B)}
+$$
+
+die *bedingte Wahrscheinlichkeit* von $A$ bedingt $B$.
+:::
+
+Klar: $A$ und $B$ sind unabhängig voneinander genau dann wenn $P(A | B) = P(A)$.
+Bedingte Wahrscheinlichkeiten treten also stets dann auf (bzw. sind besonders interessant), wenn zwei Ereignisse (bzw. Zufallsvariablen) nicht unabhängig sind.
diff --git a/images/random_number.png b/images/random_number.png
new file mode 100644
index 0000000000000000000000000000000000000000..71b768c0c5bf06ccc5186edb4f99bac62237d9dd
Binary files /dev/null and b/images/random_number.png differ
diff --git a/numpy.ipynb b/numpy.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..c40a31b44839411ac40ecf09b331f28876ad9baf
--- /dev/null
+++ b/numpy.ipynb
@@ -0,0 +1,521 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Numpy"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "In diesem kurzen Abschnitt lernen wir die wesentlichen Ideen bei Numpy kennen.\n",
+    "Eine längere und bessere (und englischere) Fassung davon sind die [Numpy fundamentals](https://numpy.org/devdocs/user/basics.html) im Numpy User Guide, die Sie am besten im Anschluss überfliegen sollten.\n",
+    "Ein richtiges (und sehr gutes) Lehrbuch für alle, die mit Python vertraut sind (und als das betrachten wir uns zu diesem Punkt der Vorlesung) ist [Nicolas Rougier's From Python to Numpy](https://www.labri.fr/perso/nrougier/from-python-to-numpy/), wo für uns zunächst Kapitel 3 relevant ist.\n",
+    "\n",
+    "Kurz lässt sich sagen, dass mit Numpy [Array-orientierte Programmierung](https://en.wikipedia.org/wiki/Array_programming) (auch: *Vektorisierung*) in Python möglich wird.\n",
+    "\n",
+    "## Arrays\n",
+    "<!--\n",
+    "Zur Probability mit Numpy ist das hier (auf deutsch) gut geeignet:\n",
+    "https://www.python-kurs.eu/python_numpy_wahrscheinlichkeit.php\n",
+    "-->\n",
+    "Numeric Python (Numpy) wird meist als `np` abgekürzt importiert:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "[0.42327034 0.18083199 0.98939518 0.25440161 0.34240241 0.37819465\n",
+      " 0.11030633 0.9302959  0.07195987 0.18855029]\n",
+      "<class 'numpy.ndarray'>\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "array([0.42327034, 0.18083199, 0.98939518, 0.25440161, 0.34240241,\n",
+       "       0.37819465, 0.11030633, 0.9302959 , 0.07195987, 0.18855029])"
+      ]
+     },
+     "execution_count": 1,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "import numpy as np\n",
+    "sample = np.random.random(10)\n",
+    "print(sample)\n",
+    "print(type(sample))\n",
+    "sample"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Viele nützliche Hilfsfunktionen sind in Numpy enthalten, die wiederum Numpy-eigene Datenstrukturen (den Array) verarbeiten."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "mean    = 4.4748 \n",
+      "expected= 4.5\n",
+      "std²    = 8.27496496 \n",
+      "variance= 8.25\n"
+     ]
+    }
+   ],
+   "source": [
+    "sample = np.random.randint(low=0, high=10, size=5000)\n",
+    "print(\"mean    =\", np.mean(sample),   \"\\nexpected=\", 9/2)\n",
+    "print(\"std²    =\", np.std(sample)**2, \"\\nvariance=\", 99/12)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Es gibt auch eine `arange`-Methode, sie erzeugt aber keine Range-Objekte in Numpy:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "[1 2 3 4 5 6] <class 'numpy.ndarray'> int64\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "array([1, 2, 3, 4, 5, 6])"
+      ]
+     },
+     "execution_count": 3,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "myRange = np.arange(1, 7)\n",
+    "print(myRange, type(myRange), myRange.dtype)\n",
+    "myRange"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Der Numpy-Array `ndarray` trägt im Gegensatz zur Python-Liste einen festen Datentyp, den alle Elemente gemeinsam haben. Das können alle Numpy-datatypes (dtypes) sein, z.B. `double` (kompatibel mit dem Python-`float`) oder `float32` (auf den meisten Plattformen kompatibel mit dem C-`float`)  oder `long` (kompatibel mit dem Python-`int`)."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "3.9101130639901385\n",
+      "4.628806502034422\n"
+     ]
+    }
+   ],
+   "source": [
+    "import timeit\n",
+    "ordinary_list = [1,2,3,4,5,6,5,4,3,2,1]*10\n",
+    "def sort_array(dtype):\n",
+    "    a = np.array(ordinary_list, dtype)\n",
+    "    a.sort()\n",
+    "print(timeit.timeit(lambda : sort_array(np.byte)))\n",
+    "print(timeit.timeit(lambda : sort_array(np.float64)))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Dadurch, dass der Datentyp präzise bekannt ist, kann Numpy darauf optimierte Algorithmen, direkt in C implementiert, verwenden.\n",
+    "\n",
+    "Was es noch für `dtype`s gibt und wie sie eingesetzt werden, können wir der Dokumentation entnehmen:\n",
+    "* [\"Structured Arrays\"](https://numpy.org/doc/stable/user/basics.rec.html)\n",
+    "* [\"Data types\" in den \"Numpy fundamentals\"](https://numpy.org/doc/stable/user/basics.types.html)\n",
+    "* [\"Scalars\"](https://numpy.org/doc/stable/reference/arrays.scalars.html)\n",
+    "* [\"Data type objects (dtype)\"](https://numpy.org/doc/stable/reference/arrays.dtypes.html)\n",
+    "* [\"numpy.dtype\"](https://numpy.org/doc/stable/reference/generated/numpy.dtype.html)\n",
+    "\n",
+    "Es gibt noch mehr Wege, Arrays zu erzeugen, außer mit `arange` oder durch konvertieren einer Python-Sequenz. Z.B. lässt sich mit `ones` ein Array gefüllt mit $1$ und mit `zeros` ein Array gefüllt mit $0$ erzeugen."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Der Grund dafür, dass das Array den Namen `ndarray` trägt, ist, dass es für \\`\\`$n$-dimensional array'' steht.\n",
+    "Wenn man in Python eine Matrix speichern möchte, würde man das als Liste der Zeilenvektoren (oder der Spaltenvektoren) tun, etwa"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "[[1, 0, 0], [0, 1, 0], [0, 0, 1]] <class 'list'>\n",
+      "1\n",
+      "0\n"
+     ]
+    }
+   ],
+   "source": [
+    "matrix = [[1,0,0], [0,1,0], [0,0,1]]  # Einheitsmatrix\n",
+    "print(matrix, type(matrix))\n",
+    "quarkix = matrix      # eine Kopie\n",
+    "print(quarkix[0][0])\n",
+    "quarkix[0][0] = 0     # wir ändern den oberen linken Eintrag\n",
+    "print(matrix[0][0])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Daran sehen wir ein Problem: Python behandelt unsere Matrix wie eine Liste (so haben wir es ja auch hingeschrieben), also wird beim kopieren der Liste der Inhalt (die Zeilenvektoren) nicht mitkopiert (sondern nur die Pointer darauf)."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "[[1 0 0]\n",
+      " [0 1 0]\n",
+      " [0 0 1]] <class 'numpy.ndarray'>\n",
+      "1\n",
+      "2\n"
+     ]
+    }
+   ],
+   "source": [
+    "npmatrix = np.identity(3, int)  # Einheitsmatrix\n",
+    "print(npmatrix, type(npmatrix))\n",
+    "npquarkix = npmatrix[:]\n",
+    "print(npquarkix[0][0])\n",
+    "npquarkix[0][0] = 2     # wir ändern den oberen linken Eintrag\n",
+    "print(npmatrix[0][0])\n",
+    "# Mit einer echten Kopie wäre das nicht passiert:\n",
+    "real_copy = npmatrix.copy()\n",
+    "real_copy[0][0] = 1\n",
+    "assert npmatrix[0][0] != 1"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Es ist wichtig, festzustellen, dass der Numpy-Array das gleiche Verhalten an den Tag legt wie unsere Python-Liste-von-Listen. Wir können gleich damit indizieren und slicen, und es gibt das gleiche Problem beim Kopieren über die Slicing-Syntax.\n",
+    "\n",
+    "Der `shape`-Parameter sagt uns, welche Form unser Numpy-Array hat. Dabei handelt es sich um ein $d$-Tupel, wobei $d$ die Dimension ist. Eine Matrix ist $2$-dimensional, ein Vektor $1$-dimensional und ein Skalar $0$-dimensional."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 7,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "(3, 3)\n",
+      "[2 0 0] (3,)\n",
+      "2 ()\n"
+     ]
+    }
+   ],
+   "source": [
+    "print(npmatrix.shape)\n",
+    "print(npmatrix[0], npmatrix[0].shape)\n",
+    "print(npmatrix[0][0], npmatrix[0][0].shape)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "In Numpy kann man noch etwas feiner slicen.\n",
+    "Die Allgemeine Syntax ist `[start:stop:step, ..]` wobei man mit dem Komma getrennt über die Achsen geht. Ein zweidimensionaler Array hat zwei Achsen, wobei Achse $0$ von oben nach unten und Achse $1$ von links nach rechts indiziert ist. Während \"step\" auch mit Python-Listen funktioniert, ist das indizieren mit mehreren Achsen eine Spezialität von Numpy."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 8,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "not in shape: [1 2 3 4 5 6 7 8 9]\n",
+      "in much better shape:\n",
+      "[[1 2 3]\n",
+      " [4 5 6]\n",
+      " [7 8 9]]\n",
+      "Zeilenvektor Zeile 0: [1 2 3]\n",
+      "Spaltenvektor Spalte 0: [1 4 7]\n",
+      "Spalten 1-2:\n",
+      "[[2 3]\n",
+      " [5 6]\n",
+      " [8 9]]\n",
+      "Alle Zeilen, Schrittweite 2\n",
+      "[[1 2 3]\n",
+      " [7 8 9]]\n"
+     ]
+    }
+   ],
+   "source": [
+    "matrix = list(range(1,10))\n",
+    "npmatrix = np.array(matrix)\n",
+    "print(\"not in shape:\", npmatrix)\n",
+    "npmatrix.shape = (3,3)\n",
+    "print(\"in much better shape:\\n\"+ str(npmatrix))\n",
+    "print(\"Zeilenvektor Zeile 0:\", npmatrix[0])\n",
+    "print(\"Spaltenvektor Spalte 0:\", npmatrix[:,0])\n",
+    "print(\"Spalten 1-2:\\n\"+ str(npmatrix[:,1:]))\n",
+    "print(\"Alle Zeilen, Schrittweite 2\\n\"+ str(npmatrix[0::2]))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "In klassischem Python-Code würden wir auf einen Eintrag einer Matrix zugreifen mit `matrix[x][y]`, und das funktioniert so auch in Numpy. Allerdings wird dabei zunächst ein weiteres Listenobjekt `matrix[x]` erzeugt (beim Slicing auch zusätzlicher Speicher dafür belegt) und dann darauf `[y]` aufgerufen. Es ist daher grundsätzlich effizienter, direkt Numpy's `[x,y]` zu verwenden.\n",
+    "\n",
+    "Numpy erzeugt bewusst keine Kopien beim Slicing, sondern nur eine andere Sichtweise auf den gleichen Speicherbereich (daher auch das oben beobachtete Verhalten bei `[:]`). Ob zwei Arrays auf den gleichen Speicherbereich verweisen, lässt sich mit `np.may_share_memory` prüfen. Dabei bedeutet ein positives Ergebnis keineswegs, dass die Arrays voneinander abhängig sind - so verweisen die erste und die zweite Spalte einer Matrix auch auf den gleichen Speicherbereich, nämlich die ganze Matrix. Wenn man nun einen der beiden Vektoren ändert, bleibt der andere unverändert - die ganze Matrix aber ändert sich mit."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 9,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "May share memory (but actually don't): (array([[1, 2, 3],\n",
+      "       [7, 8, 9]]), array([4, 5, 6])) True\n",
+      "<class 'list'>\n",
+      "<class 'numpy.ndarray'>\n",
+      "[[1 2 3]\n",
+      " [0 0 0]\n",
+      " [7 8 9]]\n"
+     ]
+    }
+   ],
+   "source": [
+    "npmatrix = np.array(list(range(1,10))).reshape(3,3)\n",
+    "candidates = (npmatrix[0::2], npmatrix[1])\n",
+    "print(\"May share memory (but actually don't):\",\n",
+    "      candidates, np.may_share_memory(*candidates))\n",
+    "print(type([0,0,0]))     # vor der Zuweisung\n",
+    "npmatrix[1] = [0,0,0]\n",
+    "print(type(npmatrix[1])) # nach der Zuweisung\n",
+    "print(npmatrix)          # die ganze Matrix ist wie verändert"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Ausführliche Informationen zum Slicing und Indizieren liefert [die Dokumentation](https://numpy.org/doc/stable/user/basics.indexing.html)."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Broadcasting\n",
+    "\n",
+    "Während für Python-Listen der Additionsoperator die Listenkonkatenation ist, und damit die Multiplikation von Listen mit Skalaren definiert ist, ist die Multiplikation von zwei Listen undefiniert.\n",
+    "Für Numpy-Arrays sind deutlich mehr arithmetische Operationen verfügbar:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 10,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "[[1 0 0]\n",
+      " [0 1 0]\n",
+      " [0 0 1]] = E\n",
+      "[[0 0 0]\n",
+      " [1 1 1]\n",
+      " [1 1 1]] = A\n",
+      "[[1 0 0]\n",
+      " [1 2 1]\n",
+      " [1 1 2]] = E + A\n",
+      "[[0 0 0]\n",
+      " [0 1 0]\n",
+      " [0 0 1]] = EA\n",
+      "[[1 0 0]\n",
+      " [1 4 1]\n",
+      " [1 1 4]] = (E+A)(E+A)\n"
+     ]
+    }
+   ],
+   "source": [
+    "E = np.identity(3, int)\n",
+    "print(E, \"= E\")\n",
+    "A = np.ones((3,3), int)\n",
+    "A[0] = [0,0,0]\n",
+    "print(A, \"= A\")\n",
+    "print(E + A, \"= E + A\")\n",
+    "print(E * A, \"= EA\")\n",
+    "print((E+A)**2, \"= (E+A)(E+A)\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Wenn man das aufmerksam nachverfolgt, stellt man fest, dass diese Rechnungen keine Matrizenmultiplikationen sind,\n",
+    "sondern schlicht elementweise erfolgt sind - so sind die Operationen auf Arrays definiert.\n",
+    "Besonders tückisch ist dies:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 11,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "[1 1 1] = v\n",
+      "[[0 0 0]\n",
+      " [1 1 1]\n",
+      " [1 1 1]] = A*v (aber nicht die Matrixmultiplikation)\n"
+     ]
+    }
+   ],
+   "source": [
+    "v = np.ones(3, int)\n",
+    "print(v, \"= v\")\n",
+    "print(A*v, \"= A*v (aber nicht die Matrixmultiplikation)\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Um explizit mit Matrizenkalkül zu rechnen, hat man früher den Numpy-Datentyp `matrix` verwendet, aber dieser ist als `deprecated` (veraltet) markiert und wird in zukünftigen Numpy-Versionen abgeschafft. Heutzutage nutzt man die Methode `np.matmul` oder den Infix-Operator `@`."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 12,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "[0 3 3]\n",
+      "[0 3 3]\n"
+     ]
+    }
+   ],
+   "source": [
+    "print(np.matmul(A,v))\n",
+    "print(A@v)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Für viele Probleme ist es sehr hilfreich, nicht den Matrizenkalkül zu verwenden, sondern elementweise arithmetische Operationen auszuführen.\n",
+    "*Broadcasting* ist ein Mechanismus, der diesen elementweisen Kalkül etwas praktischer macht.\n",
+    "So ist die Operation ($n \\times n$-array) * ($n$-Vektor) automatisch interpretiert, indem der $n$-Vektor $n$-fach kopiert wird, sodass die Multiplikation einer jeden Zeile des linken Arrays mit dem Vektor (elementweise) durchgeführt wird.\n",
+    "\n",
+    "Dazu ist es wirklich hilfreich, einmal [die Dokumentation](https://numpy.org/doc/stable/user/basics.broadcasting.html) zu überfliegen."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {
+    "tags": []
+   },
+   "source": [
+    "## ufuncs\n",
+    "\n",
+    "`ufunc` steht für \"universal function\" und bezeichnet eine Methode, die auf Numpy Arrays vektorisiert laufen kann.\n",
+    "\n",
+    "Mit jeder `ufunc` lässt sich z.B. `reduce` durchführen, wo entlang einer Achse des Arrays die `ufunc` auf die resultierenden kleineren Arrays angewandt wird.\n",
+    "\n",
+    "Um selbst eine `ufunc` zu schreiben, muss man [C-Code programmieren](https://numpy.org/doc/stable/user/c-info.ufunc-tutorial.html) oder aber [einen Wrapper um eine Python-Methode legen](https://numpy.org/doc/stable/reference/generated/numpy.frompyfunc.html).\n",
+    "\n",
+    "Es lohnt sich, einen kurzen Blick auf alle bereits definierten `ufunc`s zu werfen:\n",
+    "[Available ufuncs](https://numpy.org/doc/stable/reference/ufuncs.html#available-ufuncs)"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.9.12"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/stochastik-programmieren.ipynb b/stochastik-programmieren.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..7ba713d6ac14571f56040ed1c2d9b7fd9c148edb
--- /dev/null
+++ b/stochastik-programmieren.ipynb
@@ -0,0 +1,200 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "id": "04598fd5-c190-479e-a252-457a3ed02c52",
+   "metadata": {},
+   "source": [
+    "# Stochastik programmieren"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "9b4163a3-d84b-42ec-b927-6888e3bc159b",
+   "metadata": {},
+   "source": [
+    "Wir wollen uns nun kurz damit beschäftigen, wie sich mit Python Stichproben von Zufallsexperimenten simulieren lassen.\n",
+    "\n",
+    "## Wahrscheinlichkeitsmaß implementieren"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "id": "1b9aa059-0486-4d8b-aff3-37a5f9cbb5d8",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from fractions import Fraction\n",
+    "\n",
+    "def P(A, Omega):\n",
+    "    \"\"\"Die Wahrscheinlichkeit für das Ereignis A,\n",
+    "       gegeben gleich wahrscheinliche Ergebnisse aus einem Ergebnisraum Ω.\"\"\"\n",
+    "    return Fraction(len(A & Omega), len(Omega))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "2080149c-7800-4728-acb3-579a89f46971",
+   "metadata": {},
+   "source": [
+    "Wir werden damit nun einen Würfelwurf programmieren:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "id": "a03a347d-86fb-4098-98ba-c7c1cd3ed715",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Gerade Würfelaugen: {2, 4, 6}\n",
+      "Wahrscheinlichkeit für gerade Augenzahl: 1/2\n"
+     ]
+    }
+   ],
+   "source": [
+    "W = {1, 2, 3, 4, 5, 6}\n",
+    "gerade = set((x*2 for x in range(1,4)))\n",
+    "print(\"Gerade Würfelaugen:\",gerade)\n",
+    "\n",
+    "print(\"Wahrscheinlichkeit für gerade Augenzahl:\", P(gerade, W))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "1a17807c-4adb-4482-8836-1a892b6af8d6",
+   "metadata": {},
+   "source": [
+    "Damit haben wir nun das Wahrscheinlichkeitsmaß. Wenn wir eine Stichprobe ziehen wollen, müssen wir noch irgendwo den \"Zufall\" her bekommen.\n",
+    "\n",
+    "## Zufall importieren\n",
+    "\n",
+    "Python bietet mit dem `random`-Modul eine Schnittstelle zu Pseudozufallszahlen. Die Methode `random.random` ist ein direkt in C implementierter Mersenne Twister. Wenn man \"echte\" Zufallszahlen braucht, etwa für kryptografische Zwecke, gibt es dazu das `secrets`-Modul. Den Seed für den Mersenne Twister kann man angeben, und sollte man auch, um Zufallssimulationen reproduzierbar zu machen."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "id": "05a3b6f7-e7b6-4034-b3f9-807b20b47d6c",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Help on built-in function random:\n",
+      "\n",
+      "random() method of random.Random instance\n",
+      "    random() -> x in the interval [0, 1).\n",
+      "\n",
+      "0.7803255204450154\n",
+      "0.13436424411240122\n"
+     ]
+    }
+   ],
+   "source": [
+    "from random import random as r\n",
+    "help(r)\n",
+    "print(r())\n",
+    "\n",
+    "import random\n",
+    "random.seed(1)\n",
+    "very_random = r()\n",
+    "print(very_random)\n",
+    "assert very_random == 0.13436424411240122"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "c862bd54-0c4f-4100-8654-4e86a6e3d3e1",
+   "metadata": {},
+   "source": [
+    "![XKCD 221: Random Number](images/random_number.png \"RFC 1149.5 specifies 4 as the standard IEEE-vetted random number.\")\n",
+    "\n",
+    "[Link zum Comic (Randall Munroe, CC-BY-NC 2.5)](https://xkcd.com/221)\n",
+    "\n",
+    "Aus einer (Pseudo)zufallszahl zwischen $0$ und $1$ (man beachte: evtl. $0$ aber nie $1$) lassen sich zufällige Würfelwürfe erzeugen:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "id": "c453b1a1-9693-401c-ab9e-fb39a13c81a2",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "[6, 5, 2, 3, 3, 4, 5, 1, 1, 6, 3, 5, 1, 3, 5, 2, 6, 6, 1, 1, 4, 6, 3, 2, 3, 1, 2, 3, 3, 2, 2, 2, 3, 2, 1, 6, 4, 4, 2, 6, 6, 1, 2, 5, 5, 6, 3, 5, 5, 2, 4, 6, 6, 4, 4, 1, 2, 5, 3, 2, 4, 5, 5, 3, 3, 4, 5, 4, 3, 3, 1, 1, 5, 6, 4, 3, 2, 4, 6, 5, 4, 6, 2, 4, 6, 4, 3, 2, 4, 6, 1, 5, 5, 6, 5, 5, 4, 4, 3, 1]\n"
+     ]
+    }
+   ],
+   "source": [
+    "from math import floor\n",
+    "def transform_unit_to_dice(x):\n",
+    "    return floor(1 + 6*x)\n",
+    "\n",
+    "assert list(range(1,7)) == [transform_unit_to_dice((x-1)/6)\n",
+    "                            for x in range(1,7)]\n",
+    "\n",
+    "print([transform_unit_to_dice(r()) for n in range(100)])\n",
+    "    "
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "beac5e48-9b2d-410b-9ddd-a9898347d71b",
+   "metadata": {},
+   "source": [
+    "Damit man solche Transformationen nicht andauernd programmieren muss, kann man hier auch auf `random.randint(1,6)` oder auch auf `random.choice(range(1,7))` oder `random.randrange(1,7)` zurückgreifen. Dabei ist `randint` ein Kürzel für das entsprechende `randrange` und `choice` ist etwas allgemeiner.\n",
+    "\n",
+    "Wir wollen aber festhalten: gegeben eine gleichverteilte \"Zufallsvariable\" $X=$`random.random` mit Werten in $[0,1)$ haben wir eine Abbildung $t =$`transform_unit_to_dice` konstruiert und implementiert, die Werte in $\\{1,2,3,4,5,6\\}$ hat und $t(X)$ ist gleichverteilt. Die mathematische Abbildung $t$ ist eine Zufallsvariable, wir behandeln die Verknüpfung $t \\circ X$ als Zufallsvariable, die den Würfel modelliert.\n",
+    "\n",
+    "Nun könnte man sich beschweren: `random.random()` nimmt gar keinen Parameter, ist also keine mathematische Abbildung von einem Definitionsbereich in die Menge $[0,1)$. Tatsächlich müssen wir uns vorstellen, dass es eine Abbildung $X \\colon \\Omega \\to [0,1)$ ist, und auf $\\Omega$ ein irgendwie geartetes Wahrscheinlichkeitsmaß definiert ist, sodass durch $X$ auf $[0,1)$ die Gleichverteilung induziert wird. Die Menge $\\Omega$ spielt für uns keine konkrete Rolle - da \"kommt der Zufall her\" und in der Notation `random.random()` sehen wir schon, dass wir eben kein konkretes Element von $\\Omega$ einsetzen, sondern pseudozufällig eins ziehen und das in $X$ einsetzen."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "77aab03c-2b4f-4e83-bf3c-bc7b8cb3d5f4",
+   "metadata": {},
+   "source": [
+    "## Größere Stichproben"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "d2571d1b-b44c-436d-a72f-f8385a1e4255",
+   "metadata": {},
+   "source": [
+    "Die Methode `random.sample(population, k)` erlaubt es eine Stichprobe der Größe $k$ aus einer Population (einer Urne) zu ziehen - ziehen mit Zurücklegen. Für $k=1$ entspricht das einer Gleichverteilung auf der Population.\n",
+    "Mit der Methode `random.choices(population, weights=None, *, cum_weights=None, k=1)` kann man das ziehen aus der Population $k$-mal sampeln (und dabei anstelle einer geeigneten Population auch Gewichte vergeben)."
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.9.12"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/zufallsvariablen-verteilungen.md b/zufallsvariablen-verteilungen.md
new file mode 100644
index 0000000000000000000000000000000000000000..05ba7c5547b538ba22c122af8731bff418ed7373
--- /dev/null
+++ b/zufallsvariablen-verteilungen.md
@@ -0,0 +1,177 @@
+# Zufallsvariablen und Verteilungen
+
+Das typische 'Interface' für Wahrscheinlichkeitsverteilungen sind Zufallsvariablen.
+Im Unterschied zu einer bloßen Verteilung spielt bei einer Zufallsvariable auch eine Rolle, welche konkreten Werte als Ergebnis eintreten können.
+
+## Zufallsvariablen
+
+Wir erinnern an die Definition, nun in der Notation, die wir im weiteren Verlauf verwenden möchten:
+
+:::{admonition} Definition
+Gegeben eine Menge $\Omega$ mit einem Wahrscheinlichkeitsmaß $P_\Omega$,
+heißt eine Abbildung $X \colon \Omega \to \mathcal{X}$ in eine Menge $\mathcal{X}$
+**Zufallsvariable**, wenn für jedes Ereignis $A \subseteq \mathcal{X}$ auch $X^{-1}(A) \subseteq \Omega$ ein Ereignis ist.
+:::
+
+Zur Erinnerung: Wenn $\Omega$ und $\mathcal{X}$ endliche Mengen sind, dann nennen wir jede Teilmenge Ereignis. Dann ist auch jede solche Abbildung eine Zufallsvariable.
+Wir werden später sehen: der Begriff Zufallsvariable wird genau dann genutzt, wenn wir an Erwartungswerten oder höheren Momenten interessiert sind.
+
+### Induziertes Wahrscheinlichkeitsmaß
+
+:::{admonition} Definition
+Gegeben eine Zufallsvariable $X \colon \Omega \to \mathcal{X}$
+heißt das Wahrscheinlichkeitsmaß auf $\mathcal{X}$, das durch
+
+$$P_X \colon A \mapsto P_\Omega\left( X^{-1}(A) \right)$$
+
+gegeben ist, die **Verteilung von $X$**.
+:::
+
+:::{admonition} Proposition
+Das ist tatsächlich immer ein Wahrscheinlichkeitsmaß.
+:::
+
+:::{admonition} Beweis
+Wir prüfen die Axiome:
+1. Es ist $X^{-1}(\mathcal{X}) = \Omega$, daher ist $P_X(\mathcal{X}) = P_\Omega\left( X^{-1}(\mathcal{X}) \right) = P_\Omega(\Omega) = 1$.
+2. Seien $A_1,\dots$ eine Folge von paarweise disjunkten Ereignissen in $\mathcal{X}$, dann ist
+
+$$X^{-1}\left(\bigcup_{i \geq 1} A_i\right) = \left\{\omega \in \Omega\ \mid\ X(\omega) \in A_i \text{ für ein } i \right\} = \bigcup_{i \geq 1} X^{-1}(A_i)$$
+
+und damit 
+
+$$P_X\left(\bigcup_{i\geq 1} A_i\right) = P_\Omega\left( X^{-1} \left(\bigcup_{i \geq 1} A_i\right) \right) = P_\Omega\left( \bigcup_{i \geq 1} X^{-1}(A_i) \right) $$
+
+$$ = \sum_{i \geq 1} P_\Omega \left( X^{-1} A_i \right) = \sum_{i \geq 1} P_X(A_i).$$
+
+:::
+
+:::{admonition} Beispiel
+In einem Raum spielen $30$ Kinder. Davon haben $10$ einen blauen Pulli an,
+$10$ einen roten, $5$ einen gelben und weitere $5$ einen schwarzeln Pulli an.
+Wir modellieren $\Omega =$ Menge der Kinder, $X \colon \Omega \to \{r,g,b,s\}$ die Abbildung,
+die jedes Kind auf den Anfangsbuchstaben der Pullifarbe abbildet.
+Die Verteilung von $X$ ist nun definiert, es ist ein Wahrscheinlichkeitsmaß auf der Menge $\{r,g,b,s\}$.
+Wir können beispielhaft berechnen:
+
+$$P_X\left(\{b,r\}\right) = P_\Omega\left(X^{-1} \left(\{b,r\}\right) \right) = \frac{20}{30}$$
+
+wobei wir implizit angenommen haben, dass wir die Kinder alle gleich behandeln,
+also gleichverteilt ein Kind herausgreifen, dessen Pullifarbe wir betrachten,
+wenn wir ein Ergebnis von $X$ betrachten.
+
+Wenn uns nur die Verteilung $P_X$ interessiert, können wir $\Omega$ ignorieren,
+denn die selbe Verteilung lässt sich auch mit anderen Mengen $\Omega$ erreichen,
+auch mit Verteilungen, die keine Gleichverteilung sind.
+:::
+
+## Wahrscheinlichkeitsmassefunktion
+
+Die typische Art, im Alltag mit Wahrscheinlichkeiten umzugehen, ist die der Wahrscheinlichkeitsdichtefunktion, oft mit $p(x)$ im Kontrast zu $P$ bezeichnet.
+Für diskrete Verteilungen wird diese allerdings als Wahrscheinlichkeitsmassefunktion bezeichnet, ebenfalls mit der Notation $p(x)$:
+
+:::{admonition} Definition
+Eine Funktion $p \colon \Omega \to [0,1]$ heißt *Wahrscheinlichkeitsmassefunktion* (*pmf*, probability mass function) einer Verteilung $P$ auf $\Omega$,
+wenn für alle $\omega \in \Omega$ gilt: $p(\omega) = P(\{\omega\})$.
+:::
+
+:::{admonition} Beispiel
+Im vorigen Beispiel haben wir $P_X(\{r\}) = 10$, $P_X(\{g\}) = 5$, $P_X(\{b\}) = 10$, $P_X(\{s\}) = 5$ und daher ist
+
+$$p(r):=10,\ p(g):=5,\ p(b):=10,\ p(s):=5$$
+
+die Definition einer pmf für $P_X$. Man sagt auch, $p$ ist die pmf von $X$.
+
+Die pmf der Gleichverteilung auf $\Omega$ ist $p(\omega) = \frac{1}{|\Omega|}$.
+:::
+
+:::{admonition} Beispiel
+Wir erwarten eine eingehende Nachricht der Länge $3$ Zeichen. Diese drei Zeichen modellieren wir als Zufallsvariablen X,Y und Z.
+Wir wissen vorab, dass jedes Zeichen nur aus einem endlichen Zeichenvorrat kommen kann.
+Für jede Zufallsvariable bezeichnen wir diesen Wertebereich jeweils mit $\mathcal{X},\mathcal{Y}$ und $\mathcal{Z}$.
+Wir notieren die pmf jeweils mit $p(x),\ p(y),\ p(z)$ mit $x \in \mathcal{X}, y \in \mathcal{Y}$ und $z \in \mathcal{Z}$.
+
+Wir können auch die ganze Nachricht als eine einzige Zufallsvariable auffassen: XYZ.
+Dabei kann es nun passieren, dass gewisse Buchstabenfolgen wahrscheinlicher sind, als ihre Bestandteile.
+Wenn $X=s$ und $Y=o$ sind, dann ist $Z=s$ sicher wahrscheinlicher, als wenn wir beliebige Werte für $X$ und $Y$ haben.
+Insgesamt ist $XYZ=sos$ wahrscheinlicher als das Produkt der Ereignisse $X=s$, $Y=o$, $Z=s$.
+Wenn wir, wie üblich, die pmf von $XYZ$ mit $p(x,y,z)$ notieren, so heißt das:
+
+$$p(x,y,z) \neq p(x)p(y)p(z)$$
+
+und wir sagen dazu auch: die Zufallsvariablen bzw. ihre Verteilungen sind nicht unabhängig.
+:::
+
+### Induziertes Wahrscheinlichkeitsmaß
+
+:::{admonition} Proposition
+Wenn $X \colon \Omega \to \mathcal{X}$ eine Zufallsvariable ist und $P_\Omega$ eine pmf $p_\Omega \colon \Omega \to [0,1]$ hat,
+so ist $p_X(x) := \sum_{\omega \in X^{-1}\left(\{ x \}\right)} p_\Omega(\omega)$
+eine pmf von $X$.
+:::
+
+## Wahrscheinlichkeitsvektor
+
+:::{admonition} Beispiel
+Wenn wir die Kinderpullifarben in eine Reihenfolge bringen,
+also z.B. $r \mapsto 1,\ g \mapsto 2,\ b\mapsto 3,\ s \mapsto 4$,
+dann haben wir damit eine Abbildung $c \colon \mathcal{X} = \{r,g,b,s\} \to \mathcal{C} = \{1,2,3,4\}$ definiert.
+Die pmf der Zufallsvariable $c$ bzw. $c \circ X$ ist gegeben durch $p(i)$ für $i \in \mathcal{C}$.
+Diese Information lässt sich auch als Vektor auffassen.
+:::
+
+:::{admonition} Proposition
+Auf einer angeordneten endlichen Menge $\Omega = \{\omega_1, \dots, \omega_n\}$ ist ein Wahrscheinlichkeitsmaß
+bereits durch den Vektor $p \in \mathbb{R}^n$ mit $p_i := p(\omega_i)$ eindeutig bestimmt.
+:::
+
+:::{admonition} Proposition
+Sei $\Delta_n := \left\{p \in \mathbb{R}^n \ \mid\ 0 \leq p_i \leq 1 \text{ für alle } i=1,\dots,n,\ \text{ und } \sum_{i=1}^n p_i = 1 \right\}$
+und $\Omega = \{\omega_1, \dots, \omega_n\}$ beliebig. 
+Dann definiert jedes $p \in \Delta_n$ eine Wahrscheinlichkeitsverteilung auf $\Omega$.
+:::
+
+:::{admonition} Beispiel
+Die Gleichverteilung entspricht $p \in \Delta_n$ mit $p_i = \frac{1}{n}$, das ist der geometrische Mittelpunkt von $\Delta_n$.
+
+Die Dirac-Verteilung mit Masse an $\xi \in \Omega$ entspricht $p \in \Delta_n$ mit $p_i = 1$ falls $\omega_i = \xi$ und $p_i=0$ sonst.
+Geometrisch ist das ein Eckpunkt von $\Delta_n$ (davon gibt es $n$ Stück).
+:::
+
+Es ist also $\Delta_n$ der Raum aller möglichen Wahrscheinlichkeitsverteilungen auf einer $n$-elementigen Menge. Für $n=3$ kann man sich das zweidimensionale gefüllte Dreieck $\Delta_3 \subset \mathbb{R}^3$ noch gut vorstellen.
+
+:::{admonition} Beispiel
+Wenn wir auf einer endlichen Menge $\Omega$ durch den Vektor $p \in \Delta_n$ eine Verteilung gegeben haben,
+können wir ein Ereignis $A \subseteq \Omega$ ebenfalls durch einen Vektor charakterisieren,
+die Bitmaske $\chi(A) \in \{0,1\}^n$ gegeben durch $\chi(A)_i = 1$ wenn $\omega_i \in A$, sonst $\chi(A)_i = 0$.
+Die Wahrscheinlichkeit $P(A)$ ist dann gegeben als
+
+$$P(A) = \sum_{i=1, \omega_i \in A}^n p(\omega_i) = \sum_{i=1}^n p(\omega_i) \chi(A)_i = p \cdot \chi(A).$$
+
+:::
+
+## Von Daten zu Modellen
+
+Die einfachste Form, in der uns Daten vorliegen können, ist eine einzelne Zahl oder eine Folge von $N$ Zahlen. Das nennen wir gern einen $N$-dimensionalen Vektor oder wir stellen uns die Spalte einer Tabelle vor.
+In der Statistik spricht man von einer Stichprobe der Größe $N$. Wir werden später auch noch mit Tabellen zu tun haben, die mehr als eine Spalte enthalten, sodass die Daten in Form von komplizierteren Zeilen vorliegen, aber zunächst wollen wir im einfacheren eindimensionalen Setting bleiben.
+
+:::{admonition} Beispiel
+Gegeben eine Stichprobe von $N$ Münzwürfen $(x_1,\dots,x_N) \in \{0,1\}^N$ können wir die Frage stellen:
+'Welche Erfolgswahrscheinlichkeit $\pi$ lag der Münze zugrunde?'
+
+Eine mögliche Schätzung wäre $\frac{1}{2}$, ohne die Daten zu betrachten.
+
+Wenn wir die Stichprobe betrachten, können wir $e := \sum_{i=1}^n x_i$, die Anzahl der Erfolge, berechnen.
+Dann ist $N-e$ die Anzahl der Misserfolge und $\hat{\pi} := \frac{e}{N}$ eine Schätzung für die Erfolgswahrscheinlichkeit $\pi$,
+wenn wir davon ausgehen, dass immer die selbe Münze geworfen wurde.
+:::
+
+
+Nun werden wir Daten selten in Form von Bitfolgen mit völlig unabhängigen Bits interpretieren.
+Zum Beispiel die menschliche Körpergröße, gemessen in Zentimetern, ist eine relle Zahl größer $0$.
+Gegeben eine Meßreihe, also eine Stichprobe der Größe $N$ von Körpergrößen, können wir ein statistisches Modell bilden.
+Dabei reden wir hier nicht über Kausalität oder Korrelationen mit anderen Daten, wie z.B. Geschlecht oder Alter.
+Trotzdem können wir Modelle bilden, die uns eine gewisse Vorhersage erlauben.
+Ein Ansatz ist die zusammenfassende Statistik, die aus der Stichprobe eine einzelne Zahl extrahiert, die uns die Daten verlustbehaftet komprimieren lässt.
+Welche Zahl die richtige ist, um Daten zusammenzufassen / zu modellieren, hängt von unserem Ziel ab.
+Daher wollen wir uns im Folgenden mit Erwartungswert und Varianz aus einer datenzentrierten Perspektive befassen.