{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "7b52245d-145a-434c-8432-4a910ac37628",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-918f356c69a0adbd",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "# 3. Programmierübung: Zufallszahlen\n",
    "\n",
    "<div style=\"display:flex;\">\n",
    "    <div style=\"text-align: left\">\n",
    "        Willkommen zur ersten Programmierübung Einführung in Python 3.\n",
    "    </div>\n",
    "    <img style=\"float: right; margin: 0px 15px 15px 0px\" src=\"https://www.python.org/static/img/python-logo-large.c36dccadd999.png?1576869008\" width=\"100\" />\n",
    "</div>\n",
    "\n",
    "Wenn Sie Fragen oder Verbesserungsvorschläge zum Inhalt oder Struktur der Notebooks haben, dann können sie eine E-Mail an Phil Keier ([p.keier@hbk-bs.de](mailto:p.keier@hbk-bs.de?subject=[SigSys]%20Feedback%20Programmierübung&amp)) oder Martin Le ([martin.le@tu-bs.de](mailto:martin.le@tu-bs.de?subject=[SigSys]%20Feedback%20Programmierübung&amp)) schreiben.\n",
    "\n",
    "Link zu einem Python Spickzettel: [hier](https://s3.amazonaws.com/assets.datacamp.com/blog_assets/PythonForDataScience.pdf)\n",
    "\n",
    "Der Großteil des Python-Tutorials stammt aus der Veranstaltung _Deep Learning Lab_ und von [www.python-kurs.eu](https://www.python-kurs.eu/python3_kurs.php) und wurde für _Signale und Systeme_, sowie _Einführung in die Programmierung für Nicht Informatiker_ angepasst.\n",
    "\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c0f36367-a7b0-435f-819a-6013b305bc78",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-8003a63181c7928b",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "# Vorraussetzung\n",
    "\n",
    "Da wir einige Bibliotheken brauchen führen sie bitte folgende Zellen vor der Benutzung dieses Notebooks aus, da es sonst zu Fehlern kommt."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "2e2b133a-e725-4785-90ed-1a44e4bbaa75",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-5d81dd4fdb7076e1",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [],
   "source": [
    "# Visualisierungs Tools\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "\n",
    "# Pythons BuiltIn Library & Numpy\n",
    "import random \n",
    "import numpy as np"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "641b1f8a-1c77-43e5-ad86-81ade9932479",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-c7e6f6e837a94d41",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "# Warum Zufall?\n",
    "\n",
    "Für statistische Analysen jeglicher Art ist es wichtig seine Werkzeuge zu verstehen. Da nicht immer direkt ein Dataset vorliegt oder dieses zurzeit noch im Erstellungsprozess ist, gibt es die Möglichkeit die mathematischen und programmatischen Werzeuge zuvor an nachvollziebaren Zufallsdaten zu testen. Dabei wollen wir in dieser Übung lernen, was Zufall ist, wie Zufallsgeneratoren funktionieren und wie der Zufall auf bestimmte Art manipuliert werden kann.\n",
    "\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1534936a-813f-4045-930c-b7e1e979afa2",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-f387eeef09033ea3",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "# Was ist Zufall?\n",
    "\n",
    "Für Zufall gibt es viele Definitionen. Aber was genau ist Zufall? Das Würfeln eines Würfels zum Beispiel ist nicht zufällig. Jeder Würfelwurf kann vorhergesagt werden, vorausgesetzt, alle erforderlichen Daten sind vorhanden. Auch das Wetter ist nicht zufällig. Alle Ereignisse lassen sich vorhersagen, wenn genügend Daten vorhanden sind, das Problem ist nur, dass es nie genug Daten gibt. \n",
    "\n",
    "Jedes andere Beispiel kann mit der gleichen Argumentation als \"zufällig\" widerlegt werden. Daher werden diese Phänomene als pseudo-zufällig bezeichnet. \n",
    "\n",
    "Aber das nur am Rande. In der Physik gibt es ein Phänomen, das völlig unvorhersehbar ist. Wenn ein einzelnes Photon auf einen halbtransparenten Spiegel geschossen wird, kann __nie__ vorhergesagt werden, auf welchen der beiden Detektoren das Photon treffen wird. Diese Form der wirklich echten Zufalls wird von der Firma [ID Quntique](https://www.idquantique.com/random-number-generation/overview/) genutzt und verkauft Geräte, die das zuvor erklärte Quantenphänomen nutzen.\n",
    "\n",
    "Aber genug von der echten Koinzidenz und zurück zum Pseudo-Zufall und wie Computer damit umgehen.\n",
    "\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9575afd2-efdd-4ce5-be0a-d2eaf14d979f",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-915e9fd37a4dece5",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "# Pythons BuiltIn Random\n",
    "\n",
    "Unter dem Abschnitt Vorraussetzung haben wir bereits Pythons BuiltIn Library [random](https://docs.python.org/3.11/library/random.html) importiert. Schauen Sie gerne in die Dokumentation diese beinhält Erklärungen zu allen hier behandelten Funktionen.\n",
    "\n",
    "Das 'Hello World' Programm der Zufallszahlen ist das Simulieren eines Würfels. Dabei hat jede Seite eines Würfels dieselbe Auftrittswahrscheinlichkeit $\\frac{1}{\\text{Anzahl der Seiten des Würfels}}$. Für einen 6-Seitigen Würfel sind das die Werte `1...6`. Die Funktion [random.randint](https://docs.python.org/3.11/library/random.html#random.randint) gibt uns die Möglichkeit eine ganzzahlige Zufallszahl zwischen zwei ganzzahligen Werten zu ziehen. Simulieren wir im folgenden Beispiel zehn unabhängige Würfe (führen Sie die Zelle gerne mehrmals aus):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "acdd947d-de8a-4389-8b74-48836acc6602",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-2fced744f253cfc2",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Wurf: 1 => Ergebnis: 2\n",
      "Wurf: 2 => Ergebnis: 4\n",
      "Wurf: 3 => Ergebnis: 6\n",
      "Wurf: 4 => Ergebnis: 1\n",
      "Wurf: 5 => Ergebnis: 2\n",
      "Wurf: 6 => Ergebnis: 4\n",
      "Wurf: 7 => Ergebnis: 1\n",
      "Wurf: 8 => Ergebnis: 4\n",
      "Wurf: 9 => Ergebnis: 5\n",
      "Wurf: 10 => Ergebnis: 1\n"
     ]
    }
   ],
   "source": [
    "for i in range(1, 11):\n",
    "    wurf = random.randint(1,6)\n",
    "    print(\"Wurf: {} => Ergebnis: {}\".format(i, wurf))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fd9d16eb-65da-44a8-8878-882b9bb80d99",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-4b4a74f9b1b17afe",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Die Ergebnisse sind doch sehr Zufällig und nahezu nicht Vorrauszusehen.\n",
    "\n",
    "Möchte man nun die Physik beugen und einen gezinkten Würfel haben bietet 'random' auch hierfür eine Funktion. [random.seed](https://docs.python.org/3.11/library/random.html#random.randint) mit Seed ist dabei der Anfangswert des Zufallsgenerator gemeint. Wie dies technisch aufgebaut ist folgt im nächsten Kapitel. Dabei bewirkt der Aufruf von 'random.seed' das der Startwert gesetzt wird. Die Krux dabei ist das alle folgenden Zahlen nachdem setzen nicht mehr Zufällig sind - Sie sind Pseudo Random. Probieren wir dies mit unserem Würfel aus. Dabei wird der Seed Wert vor jedem Aufruf der 'random.randint' Funktion ausgeführt:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "cd53f1a3-f777-4e27-8d20-d3cfa1b97a7b",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-0ead1cf8d3a39192",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Wurf: 1 => Ergebnis: 6\n",
      "Wurf: 2 => Ergebnis: 6\n",
      "Wurf: 3 => Ergebnis: 6\n",
      "Wurf: 4 => Ergebnis: 6\n",
      "Wurf: 5 => Ergebnis: 6\n",
      "Wurf: 6 => Ergebnis: 6\n",
      "Wurf: 7 => Ergebnis: 6\n",
      "Wurf: 8 => Ergebnis: 6\n",
      "Wurf: 9 => Ergebnis: 6\n",
      "Wurf: 10 => Ergebnis: 6\n"
     ]
    }
   ],
   "source": [
    "for i in range(1, 11):\n",
    "    random.seed(42) # Startwert wird vor jedem Aufruf gesetzt\n",
    "    wurf = random.randint(1,6)\n",
    "    print(\"Wurf: {} => Ergebnis: {}\".format(i, wurf))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0d682554-152f-439c-9c57-8d6950704dec",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-303e2d200f7022c2",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Und genauso wird die Physik gebeugt!\n",
    "\n",
    "Probieren wir es im nächsten Schritt selber aus und simulieren einen 10-Seitigen Würfel.\n",
    "\n",
    "**Aufgabe**: Fühle die gegebene leere Liste `rands` mit 10 Zufälligen Werten zwischen 1 & 10, mittels der Funktion `random.randint`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "85e986e4-368b-4a56-b5fe-a4c2f1268ab8",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-cd2a81fd471a6040",
     "locked": false,
     "schema_version": 3,
     "solution": true,
     "task": false
    }
   },
   "outputs": [],
   "source": [
    "rands = list()\n",
    "# BEGIN SOLUTION\n",
    "rands = [random.randint(1,10) for _ in range(10)]\n",
    "# END SOLUTION"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "id": "120c91d4-6c93-449d-9e12-c812ee97edcd",
   "metadata": {
    "nbgrader": {
     "grade": true,
     "grade_id": "cell-fe7e8399a07b78cd",
     "locked": true,
     "points": 1,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[2, 1, 5, 4, 4, 3, 2, 9, 2, 10]\n"
     ]
    }
   ],
   "source": [
    "# Hier werden ihre Lösungen getestet\n",
    "print(rands)\n",
    "assert len(rands) == 10"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "384ecb1d-04a4-410d-a0bc-3bb9a066e502",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-94196c05d162e8f2",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Die Funktion [random.shuffle](https://docs.python.org/3.11/library/random.html#random.shuffle) kann eine Liste zufällig in-place umsortieren. Wichtig (!!) dabei wird der Zustand der Liste verändert!. Folgendes Beispiel Sortiert ein Array mit geordneten Zahlen `1..10` Zufällig um:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "id": "51a2973d-b9f2-4a2f-a0bc-8a4837dc388b",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-3290d35ecac9a91f",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Geordnete Zahlen von 1-10: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\n",
      "Zufällig Umsortierte Zahlen von 1-10: [5, 4, 3, 10, 6, 2, 8, 9, 1, 7]\n"
     ]
    }
   ],
   "source": [
    "numbers = [i for i in range(1, 11)]\n",
    "print(\"Geordnete Zahlen von 1-10:\", numbers)\n",
    "\n",
    "# Umsortieren mittels random.shuffle\n",
    "random.shuffle(numbers)\n",
    "print(\"Zufällig Umsortierte Zahlen von 1-10:\", numbers)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f5c12549-1986-401f-ba0b-420bbb10a285",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-b4fd6773031076b4",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Soll die Liste unberührt bleiben und trotzdem eine zufällige Sequenz aus dieser generiert werden wird die Funktion [random.sample](https://docs.python.org/3.11/library/random.html#random.shuffle) verwendet. Dazu wird der Funktion eine Liste gegeben und die länge der Ausgabeliste."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "id": "7ac3c609-0bbd-4511-81ca-ccf88c70450b",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-0e2d89c53dc58435",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Liste: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] => 6 Element großes Sample der Liste: [9, 7, 4, 8, 5, 3]\n"
     ]
    }
   ],
   "source": [
    "samplesize = 6\n",
    "numbers = [i for i in range(1, 11)]\n",
    "sample = random.sample(numbers, samplesize)\n",
    "print(\"Liste: {} => 6 Element großes Sample der Liste: {}\".format(numbers, sample))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a4465e36-927c-4deb-8807-5aa854e7649c",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-e695e92905a78fc4",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "## Absolut Zufällige Floats\n",
    "\n",
    "Die Funktion [random.random](https://docs.python.org/3.11/library/random.html#random.random) gibt eine Flieskommazahl zwischen 0 & 1 zurück:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "id": "385c5337-1c5f-42cc-930b-f0007e2a57c0",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-1bdf1f5401e3ceff",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "10 Zufällige Zahlen:\n",
      "0.8094304566778266\n",
      "0.006498759678061017\n",
      "0.8058192518328079\n",
      "0.6981393949882269\n",
      "0.3402505165179919\n",
      "0.15547949981178155\n",
      "0.9572130722067812\n",
      "0.33659454511262676\n",
      "0.09274584338014791\n",
      "0.09671637683346401\n"
     ]
    }
   ],
   "source": [
    "print(\"10 Zufällige Zahlen:\")\n",
    "for _ in range(10):\n",
    "    print(random.random())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "72559deb-f72f-4c14-8da7-ec95fcd74a39",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-dcfeaecec3828dc0",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Visualisieren wir diese Verteilung einmal für 100 Samples:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "id": "7f3b5b5d-858a-4021-87ec-4dcfb167df41",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-003196d2cdb70499",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 34324 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "x = [random.random() for _ in range(20)] # Hier werden die Zufallszahlen generiert\n",
    "plt.plot(range(20), x)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "04f5de6c-1c10-4401-bd47-ade13028fd17",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-14846e0aed52cf25",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Der Graph lässt sich interpretieren indem man sich die Verteilung ansieht. Diese wechselt rapide zwischen den Werten und folgt keiner smoothen Verteilung."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "60ee2157-bb02-42d6-a5a5-d7d9048355ba",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-4a2cf0b4ee4947a1",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "## Distribution Functions\n",
    "\n",
    "Die Funktion [random.uniform](https://docs.python.org/3.11/library/random.html#random.uniform) erzeugt Zufallszahlen in dem Bereich seiner Eingabewerte. Während `random.random` nur Zahlen zwischen `0...1` erzeugt, lässt sich mit `random.uniform` Zufallswerte im Bereich `a...b` erzeugen. Dabei erfolgt die Berechnung, wie in der Dokumentation beschrieben, $\\frac{a}{(b-a) \\cdot \\text{random()}}$. Nutzen wir folgend die Werte eines 6-Seitigen Würfels und Visualisieren wir die Verteilung wieder. Achten Sie auf die Achsen:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "id": "c5d2d8c0-101d-416c-bb3f-24f0d49b31c8",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-2048ce159893a872",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 32292 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "x = [random.uniform(1,6) for _ in range(20)] # Hier werden die Zufallszahlen generiert\n",
    "plt.plot(range(20), x)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "79dff9c5-510c-45a2-92ab-a22a5086c4dd",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-12b409cc047bfe76",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Auch häufig verwendet werden Zufallszahlen die aus einer [Gauss Verteilung](https://de.wikipedia.org/wiki/Normalverteilung) entstehen. Die Funktion [random.gauss](https://docs.python.org/3.11/library/random.html#random.gauss) erzeugt dabei die Werte rund um einen Standardwert $\\mu$. Der Wert $\\mu$ ist in der Funktion `random.gauss` standardmässig auf $\\mu = 0$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "id": "1c705bcc-e0c0-44d5-a59a-e49a54dfcb77",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-535ba1d9d8974ead",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 29944 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "x = [random.gauss() for _ in range(20)] # Hier werden die Zufallszahlen generiert\n",
    "plt.plot(range(20), x)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6cb70992-db57-4e3e-a88a-d4e27adf1307",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-2d63ebf3cbf6b805",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Nun die Werte rund um $\\mu = 6$:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "id": "a6bce87e-b15c-41fd-ba38-ad75fb39f51b",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-60aff446b4976944",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 37144 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "x = [random.gauss(mu=6) for _ in range(20)] # Hier werden die Zufallszahlen generiert\n",
    "plt.plot(range(20), x)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9fcdb0dd-4478-482d-99fb-d846f216badd",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-8f5026ab0971e642",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "**Aufgabe**: Gegeben sei die Liste `gaussians`; Setzen Sie den Seed des Zufallsgenerator auf den Wert `420` und füllen Sie die Liste `gaussians` mit `20` Elementen mittels der Funktion `random.gauss`. Dabei soll $\\mu = 50$ sein."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "id": "91bea9ab-d14b-4cea-8fc0-bddb0c746d3f",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-f5899e4f04d1fb8a",
     "locked": false,
     "schema_version": 3,
     "solution": true,
     "task": false
    }
   },
   "outputs": [],
   "source": [
    "gaussians = list()\n",
    "# BEGIN SOLUTION\n",
    "random.seed(420)\n",
    "gaussians = [random.gauss(mu=50) for _ in range(20)]\n",
    "# END SOLUTION"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "id": "c47bf25f-ca5a-4b55-bd15-dbd0c0b62809",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-bcce24d15f74a12f",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 32560 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Visualisierung ihrer Lösung\n",
    "plt.plot(range(len(gaussians)), gaussians)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "id": "5f48c0c9-ecbc-4322-80ad-89cfd9371f5b",
   "metadata": {
    "nbgrader": {
     "grade": true,
     "grade_id": "cell-4614be8448c91fdc",
     "locked": true,
     "points": 1,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [],
   "source": [
    "# Hier werden ihre Lösungen getestet\n",
    "assert len(gaussians) == 20\n",
    "### BEGIN HIDDEN TESTS\n",
    "random.seed(420)\n",
    "assert gaussians == [random.gauss(mu=50) for _ in range(20)]\n",
    "### END HIDDEN TESTS"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ec32f2e6-3bd4-4dee-8282-825af1d82787",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-8e8fed104346eadf",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "# Pseudo Randomness\n",
    "\n",
    "Zufallszahlen in Computern werden über Formeln berechnet. Einer dieser Generatoren ist der _Linear Congruent Generator (LCG)_, dessen mathematische Grundlage leicht verdaulich ist.\n",
    "\n",
    "$$ X_{n+1} = (aX_n + c) \\;mod\\; m; \\quad n \\geq 0 $$\n",
    "\n",
    "Wenn $ c = 0 $ dann nennt man den Generator auch _Multiplicative Congruent Generator (MCG)_.\n",
    "\n",
    "Die Werte haben folgenden nutzen in der Funktion:\n",
    "\n",
    "- $X_n$ ist der Startwert oder seed\n",
    "- $X_{n+1}$ ist der folgewert der im nächsten schritt für $X_n$ eingesetzt wird\n",
    "- $a$ ist der Vorfaktor vom Startwert dieser wird skaliert, deshalb wird er skalar gennant\n",
    "- $c$ ist das hinzuaddierte Offset\n",
    "- $m$ ist der Restklassenring oder auch Modulus genannt"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6a7e3d5c-101d-4829-8d15-d463b0a584ff",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-e192e7a8c3fd28d5",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "## Aufgabe\n",
    "\n",
    "Schreibe einen _Linear Congruent Generator_ mit dem funktionsnamen lcg. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "id": "75b3086c-dec9-46a7-8f46-34ffdadbb6bf",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-94fcba89968e033d",
     "locked": false,
     "schema_version": 3,
     "solution": true,
     "task": false
    }
   },
   "outputs": [],
   "source": [
    "# BEGIN SOLUTION\n",
    "def lcg(seed: int, scalar: int, modulus: int, offset: int) -> int:\n",
    "    ''' \n",
    "    Linear Congruential Generators\n",
    "\n",
    "    X(n+1) = (a X(n) + c) mod m; n >= 0\n",
    "\n",
    "    m > 0; \n",
    "    0 <= a < m;\n",
    "    c > 0; a > 0\n",
    "\n",
    "    '''\n",
    "    assert modulus > 0, \"Modulus must be greater than 0\"\n",
    "    assert 0 <= scalar and scalar < modulus, \"Scalar must be in range 0 <= a < m\"\n",
    "    assert seed >= 0, \"Seed must be greater than 0\"\n",
    "    return (scalar*seed+offset) % modulus\n",
    "# END SOLUTION"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "id": "df92409c-6299-4d1d-bc5b-06a53301ab2b",
   "metadata": {
    "nbgrader": {
     "grade": true,
     "grade_id": "cell-abb6e32ca8dab869",
     "locked": true,
     "points": 1,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3089810780120156248\n"
     ]
    }
   ],
   "source": [
    "# Hier werden ihre Lösungen getestet ...\n",
    "print(lcg(3935559000370003845, 3203021881815356449, 2**64-1, 11742185885288659963))\n",
    "### BEGIN HIDDEN TESTS\n",
    "def lcg_test(seed: int, scalar: int, modulus: int, offset: int) -> int:\n",
    "    ''' \n",
    "    Linear Congruential Generators\n",
    "\n",
    "    X(n+1) = (a X(n) + c) mod m; n >= 0\n",
    "\n",
    "    m > 0; \n",
    "    0 <= a < m;\n",
    "    c > 0; a > 0\n",
    "\n",
    "    '''\n",
    "    assert modulus > 0, \"Modulus must be greater than 0\"\n",
    "    assert 0 <= scalar and scalar < modulus, \"Scalar must be in range 0 <= a < m\"\n",
    "    assert seed >= 0, \"Seed must be greater than 0\"\n",
    "    return (scalar*seed+offset) % modulus\n",
    "\n",
    "s = lcg(3935559000370003845, 3203021881815356449, 2**64-1, 11742185885288659963)\n",
    "t = lcg_test(3935559000370003845, 3203021881815356449, 2**64-1, 11742185885288659963)\n",
    "\n",
    "assert s == t\n",
    "### END HIDDEN TESTS"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "90c4de8f-e3b8-4d23-bf17-9b5aa530edbf",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-6882494a5373136e",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "## Aufgabe\n",
    "\n",
    "Nutze die vorher geschriebene `lcg` Funktion, um einen `mcg` korrekt zu implementiert."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "id": "262922a5-ccb8-41e5-a0b9-440317653ac3",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-09dc983805e666ec",
     "locked": false,
     "schema_version": 3,
     "solution": true,
     "task": false
    }
   },
   "outputs": [],
   "source": [
    "# BEGIN SOLUTION\n",
    "def mcg(seed: int, scalar: int, modulus: int) -> int:\n",
    "    '''\n",
    "    Multiplicative Congruential Generator\n",
    "    or Lehmer Generator\n",
    "\n",
    "    Just the same as lcg with the Property c=0 (offset = 0)\n",
    "    '''\n",
    "    return lcg(seed, scalar, modulus, 0)\n",
    "# END SOLUTION"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "id": "d7ffef5c-d97f-410b-857d-4c8d7ad24bfd",
   "metadata": {
    "nbgrader": {
     "grade": true,
     "grade_id": "cell-d8973bd8652557a9",
     "locked": true,
     "points": 1,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "9794368968541047900\n"
     ]
    }
   ],
   "source": [
    "# Hier werden ihre Lösungen getestet\n",
    "print(mcg(3935559000370003845, 3203021881815356449, 2**64-1))\n",
    "### BEGIN HIDDEN TESTS\n",
    "def lcg_test(seed: int, scalar: int, modulus: int, offset: int) -> int:\n",
    "    ''' \n",
    "    Linear Congruential Generators\n",
    "\n",
    "    X(n+1) = (a X(n) + c) mod m; n >= 0\n",
    "\n",
    "    m > 0; \n",
    "    0 <= a < m;\n",
    "    c > 0; a > 0\n",
    "\n",
    "    '''\n",
    "    assert modulus > 0, \"Modulus must be greater than 0\"\n",
    "    assert 0 <= scalar and scalar < modulus, \"Scalar must be in range 0 <= a < m\"\n",
    "    assert seed >= 0, \"Seed must be greater than 0\"\n",
    "    return (scalar*seed+offset) % modulus\n",
    "    \n",
    "def mcg_test(seed: int, scalar: int, modulus: int) -> int:\n",
    "    '''\n",
    "    Multiplicative Congruential Generator\n",
    "    or Lehmer Generator\n",
    "\n",
    "    Just the same as lcg with the Property c=0 (offset = 0)\n",
    "    '''\n",
    "    return lcg_test(seed, scalar, modulus, 0)\n",
    "\n",
    "s2 = mcg(3935559000370003845, 3203021881815356449, 2**64-1)\n",
    "t2 = mcg_test(3935559000370003845, 3203021881815356449, 2**64-1)\n",
    "\n",
    "assert s2 == t2\n",
    "### END HIDDEN TESTS"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2c029c46-f4a6-4fa7-93dd-f4585ee0d6e8",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-e3049cbe7be08332",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Mittels Pythons Generator Syntax können wir einen einfachen Generator mit der vorangegangenen Aufgabe erstellen:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "id": "1d037d57-8641-49b3-b3cc-60bd64e12f36",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-1cbf76ef6815788d",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [],
   "source": [
    "def lcg_gen(seed: int, scalar: int, modulus: int, offset: int) -> int:\n",
    "    ''' \n",
    "    Linear Congruential Generators\n",
    "\n",
    "    X(n+1) = (a X(n) + c) mod m; n >= 0\n",
    "\n",
    "    m > 0; \n",
    "    0 <= a < m;\n",
    "    c > 0; a > 0\n",
    "\n",
    "    '''\n",
    "    assert modulus > 0, \"Modulus must be greater than 0\"\n",
    "    assert 0 <= scalar and scalar < modulus, \"Scalar must be in range 0 <= a < m\"\n",
    "\n",
    "    while seed > 1:\n",
    "        seed = (scalar*seed+offset) % modulus\n",
    "        assert seed >= 0\n",
    "        yield seed"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2801d225-a6c2-409c-aac9-7161d0beec01",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-b1c2720d7b6cae5f",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Mit den magischen Werten (Woher diese kommen erfahren Sie im nächsten Kapitel) \n",
    "\n",
    "- seed = $3935559000370003845$\n",
    "- scalar = $3203021881815356449$\n",
    "- modulus = $2^{64}-1$\n",
    "- offset = $11742185885288659963$\n",
    "\n",
    "lässt sich folgende Zufallsfolge erzeugen (Bitte Keine Angst vor großen Zahlen haben):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "id": "e1073b6e-9a40-48fd-a11d-03442c3045fb",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-c915d2dee5c571e7",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1. LCG Zufallswert => 3089810780120156248\n",
      "2. LCG Zufallswert => 8356396685252565260\n",
      "3. LCG Zufallswert => 1921117399837525548\n",
      "4. LCG Zufallswert => 14806858147081821235\n",
      "5. LCG Zufallswert => 2557599628047639428\n",
      "6. LCG Zufallswert => 16453652254840064460\n",
      "7. LCG Zufallswert => 15995401842808378843\n",
      "8. LCG Zufallswert => 681272290641816305\n",
      "9. LCG Zufallswert => 10955466795170118648\n",
      "10. LCG Zufallswert => 13714992071537968180\n"
     ]
    }
   ],
   "source": [
    "gen = lcg_gen(3935559000370003845, 3203021881815356449, 2**64-1, 11742185885288659963) # Generator definieren\n",
    "for i in range(1, 11):\n",
    "    print(\"{}. LCG Zufallswert => {}\".format(i, next(gen))) "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "47c6c8e6-4223-4936-a67e-b1242f9f5aed",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-6565f25687e7129b",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Wenn wir diese einmal Visualisieren:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 65,
   "id": "708b40c4-7878-49af-a8a6-9a6204a5f83a",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-75adf84e2d46024f",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 55960 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "gen = lcg_gen(3935559000370003845, 3203021881815356449, 2**64-1, 11742185885288659963) # Generator definieren\n",
    "\n",
    "n = 10 # Wie viele Zufallszahlen\n",
    "lcgs = [next(gen) for _ in range(n)] # LCG Zufallszahlen\n",
    "random.seed(42)\n",
    "rands = [random.random()*1e19 for _ in range(n)] # Pythons Zufallszahlen + Skalierung\n",
    "\n",
    "plt.plot(range(len(lcgs)), lcgs, color='r', label='LCG')\n",
    "plt.plot(range(len(rands)), rands, color='g', label='random.random')\n",
    "plt.title(\"Vergleich Zufallszahlen LCG & Python Random\")\n",
    "plt.legend()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "51e20ffc-9c08-46e6-b4c1-3cef6804c6d6",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-bc024d97d8babd60",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Wie aus der Grafik zu entnehmen folgen Beide Zufallsgeneratoren einer akkuraten Zufallsverteilung. \n",
    "\n",
    "Damit haben wir einen eigenständig einen Zufallsgenerator erstellt den wir nach unseren Belieben anpassen können. \n",
    "\n",
    "Kommen wir im nächsten Kapitel nun auf die (stand 2023) moderneste Familie der Zufallsgeneratoren."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8b778f85-7976-40f7-af83-09fb81e20a8c",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-21bc2fd7ce86eaa8",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "---\n",
    "\n",
    "# A Family of Better Random Number Generators\n",
    "\n",
    "<div style=\"display: flex; justify-content: center;\">\n",
    "    <figure style=\"margin-top: 30px;\">\n",
    "        <img src=\"https://www.cs.hmc.edu/~oneill/images/melissa1.jpg\" width=\"300\">\n",
    "        <figcaption>\n",
    "            <p style=\"text-align:center; font-size: 20px;\">\n",
    "                <strong>Melissa E. O’Neill</strong>\n",
    "            </p>\n",
    "        </figcaption>\n",
    "    </figure>\n",
    "    <div style=\"margin-left: 40px; max-width: 60%; word-wrap:break-word;\">\n",
    "        <br/>\n",
    "            <p><i>Linear Congruent Generators</i> zeichnen sich durch ihre Stabilität und Geschwindigkeit als Hervorragende Zufallsgeneratoren. Doch 2014 gelang Melissa E. O'Neil ein neuer durchbruch in der Konzeption von Pseudozufallsgeneratoren. Das Problem mit existierenden Zufallsgeneratoren ist entweder ihre Stabilität (Wie vorhersehbar die Zufallszahlen sind) oder ihrer Geschwindigkeit (Wie lange der Zufallsgenerator braucht um die nächste Zufallszahl zu errechnen).</p>\n",
    "            <p>Ihr Durchbruch gelang indem Sie die Vorteile eines <i>Linear Congruent Generators</i> mit dem eines <i>XorShift Generators</i> verband. Dadurch erreichte Sie nicht nur eine Normalverteilung in den generierten Zufallszahlen (und eine damit einhergende Stabilität), Sie hatte auch eine Family von schnellen einfachen Algorithmen entwickeln. Diese nennen sich <i>PCG - Permuted Congruential Generator</i>.</p>\n",
    "            <p>Die Implementierungsdetails lassen wir im nächsten Schritt aus, da diese sich nicht einfach in Python umzusetzen sind. Auf der Webseite <a href=\"https://www.pcg-random.org\" >pcg-random.org</a> lassen sich implemtierungen für C & C++ finden. Als weiterführende Literatur ist <a href=\"https://www.pcg-random.org/pdf/hmc-cs-2014-0905.pdf\" >PCG: A Family of Simple Fast Space-Efficient Statistically Good Algorithms for Random Number Generation - Melissa E. O’Neill</a> als Literatur angegeben.</p>\n",
    "    </div>\n",
    "</div>\n",
    "\n",
    "Zum Glück hat _Numpy_ eine Implementierung des _PCG_. Diese findet sich unter [Numpy PCG64](https://numpy.org/doc/stable/reference/random/bit_generators/pcg64.html)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "219946a9-88b6-409e-964a-a342932db73b",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-41b305bbec0288ce",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "## Verwendung\n",
    "\n",
    "Um NumPys PCG implementation zu verwenden muss zuerst das NumPy Modul random importiert werden.\n",
    "\n",
    "Wir benötigen, wie in der Dokumentation beschrieben, nur die Funktionen `Generator, PCG64 & SeedSequence`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 66,
   "id": "12d2e7bf-161e-42b5-9267-bf1965681fa7",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-2f9512ef29cb80c4",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [],
   "source": [
    "from numpy.random import Generator, PCG64, SeedSequence"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7c528b83-9553-4350-9109-884a4706402e",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-f8c8e7bc1524f45f",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "- __Generator__: eine wrapper Funktion die einen `Bit Generator` (wie PCG64) entgegennimmt und daraus Zufallszahlen konzipiert.\n",
    "\n",
    "Eine Liste aller Numpy Bit Generatoren finden Sie in der Dokumentation unter [Bit Generatoren](https://numpy.org/doc/stable/reference/random/bit_generators/index.html).\n",
    "\n",
    "Alle Funktionen die bereits bekannt aus dem Kapitel zu Pythons Random Library sind, können mit dem Generator verwendet werden!\n",
    "  \n",
    "- __PCG64__: der verwendete `Bit Generator`.\n",
    "- __SeedSequence__: eine Funktion die verschiedene Zufällige Startwerte Generiert. Dabei wird sichergestellt, dass beim erstellen der `SeedSequence` mit gleichen Startwert immer der gleiche Output geliefert wird."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "97a90f84-c40c-4f3b-9757-b0bb51ee77cf",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-c5098307e1ba361c",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "## Aufgabe\n",
    "\n",
    "Definieren Sie eine Variable `sg`, welche eine `SeedSequence` mit dem Startwert `42` hat. Nutzen Sie die verlinkte Dokumentation [Numpy PCG64](https://numpy.org/doc/stable/reference/random/bit_generators/pcg64.html) als Referenz."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "id": "5a03977d-40fc-4240-9798-d021dc7a751b",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-e8a43a6d3693907b",
     "locked": false,
     "schema_version": 3,
     "solution": true,
     "task": false
    }
   },
   "outputs": [],
   "source": [
    "# BEGIN SOLUTION\n",
    "sg = SeedSequence(42)\n",
    "# END SOLUTION"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 70,
   "id": "b26b54b4-f1ee-4755-844f-8cf146843ec5",
   "metadata": {
    "nbgrader": {
     "grade": true,
     "grade_id": "cell-2e31a0d8fd0aac43",
     "locked": true,
     "points": 1,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [],
   "source": [
    "# Hier werden ihre Lösungen getestet ...\n",
    "assert isinstance(sg, SeedSequence)\n",
    "assert sg.entropy == 42"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2803a09b-3e01-4928-862f-f78c2e923f8c",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-fee790dabde97467",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Mit der `SeedSequence.spawn` Funktion können Sie im folgenden einen Generator nutzen. Die Funktion nimmt einen Parameter entgegen. Dieser teilt der Funktion mit, wie viele Zufallszahlen erzeugt werden sollen. Der Rückgabewert ist eine Liste über die Iteriert werden kann. Ein Beispiel:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 77,
   "id": "386dd1be-3a43-4648-8b14-950d52a11b17",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-134aaab222b656fa",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Sequenz 0: SeedSequence(\n",
      "    entropy=69,\n",
      "    spawn_key=(0,),\n",
      ")\n",
      "\n",
      "Sequenz 1: SeedSequence(\n",
      "    entropy=69,\n",
      "    spawn_key=(1,),\n",
      ")\n",
      "\n",
      "Sequenz 2: SeedSequence(\n",
      "    entropy=69,\n",
      "    spawn_key=(2,),\n",
      ")\n",
      "\n"
     ]
    }
   ],
   "source": [
    "for i, s in enumerate(SeedSequence(69).spawn(3)):\n",
    "    print(\"Sequenz {}: {}\\n\".format(i, s))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a77a85af-d45f-469c-9488-59f6ed5a4fc7",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-0061578957a10bc2",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Die einzelnen Sequenzen nutzen wir im folgenden als Input für den PCG64:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 81,
   "id": "67c0773d-8540-4223-9455-617ecc187870",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-9bcea16e80118785",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<numpy.random._pcg64.PCG64 object at 0x7fc940cfb270>\n"
     ]
    }
   ],
   "source": [
    "seq = SeedSequence(69)\n",
    "pcg = PCG64(seq.spawn(1)[0])\n",
    "print(pcg)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6d8904ea-768d-43bb-a2f8-24e7736ba889",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-9530aa102fe716a5",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Um daraus eine Zufallszahl zu erstellen benötigen Sie nur noch die Generator Funktion:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 86,
   "id": "36cb2bb9-5685-430b-aa9c-3572ed5224fe",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-73351514a3bd2599",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.28914167495902166"
      ]
     },
     "execution_count": 86,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "gen = Generator(pcg)\n",
    "gen.random()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fc687570-043f-4e71-bd02-65c4a1031295",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-ff6de24546886d7e",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "## Aufgabe\n",
    "\n",
    "Nutzen Sie ihre aus letzter Aufgabe erstellte `SeedSequence sg` und erzeugen Sie mittels `Generator` & `PCG64`, 10 Zufallszahlen in der vorgegebenen Liste `pcgs`.\n",
    "Als Referenz schauen Sie gerne in das Beispiel der Dokumentation [Numpy PCG64](https://numpy.org/doc/stable/reference/random/bit_generators/pcg64.html)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 90,
   "id": "6a24d0d7-56da-4d9b-b497-2413a8c896ec",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-0cac842816238f9b",
     "locked": false,
     "schema_version": 3,
     "solution": true,
     "task": false
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[0.9167441575549085,\n",
       " 0.4674907799518424,\n",
       " 0.07123920291270869,\n",
       " 0.7639328676507445,\n",
       " 0.017567502091441867,\n",
       " 0.25302055214458075,\n",
       " 0.6361856125062569,\n",
       " 0.0015791460415535141,\n",
       " 0.7096566546463475,\n",
       " 0.2983032265878607]"
      ]
     },
     "execution_count": 90,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pcgs = list()\n",
    "# BEGIN SOLUTION\n",
    "sg = SeedSequence(42)\n",
    "for s in sg.spawn(10):\n",
    "    pcgs.append(Generator(PCG64(s)).random())\n",
    "pcgs\n",
    "# END SOLUTION"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 91,
   "id": "95aca16a-a704-4c1a-8a48-b764854bd63b",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-02df0473f204f817",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 33636 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Visualisierung ihrer Lösung\n",
    "plt.plot(range(len(pcgs)), pcgs, color='r', label='PCG')\n",
    "plt.title(\"PCG Random Numbers\")\n",
    "plt.legend()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 94,
   "id": "fec16481-85a1-4718-8ff5-a5400db85ca5",
   "metadata": {
    "nbgrader": {
     "grade": true,
     "grade_id": "cell-fd3c924c2929b92c",
     "locked": true,
     "points": 1,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [],
   "source": [
    "# Hier werden ihre Lösungen getestet\n",
    "assert len(pcgs) == 10\n",
    "### BEGIN HIDDEN TESTS\n",
    "assert pcgs == [Generator(PCG64(s)).random() for s in SeedSequence(42).spawn(10)]\n",
    "### END HIDDEN TESTS"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7d5c7255-9804-40d8-8709-e4c809dd12b7",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-b9c4e995c23c5967",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "# Simulation Verkehrsmodell\n",
    "\n",
    "Mit dem letzten Kapitel wollen wir eine reale Simulation des Verkehrsaufkommen einer Funkantenne Nachbilden. Dabei beziehen wir uns auf das Nachrichtentechnische Modell der Verkehrstheorie. \n",
    "\n",
    "Eine Erklärung:\n",
    "\n",
    "Verschiedene Teilnehmer nutzen einen Kanal an einer Funktantenne zu Unterschiedlichen Zeiten. Dabei wird die Belegungdauer oder Verkehrsmenge mit $Y$ beschrieben. Bezieht man diese Größe auf einen (Zeit)Dauer wird dies Angebot genannt und mit $A$ konnotiert. Die Einheit hierfür ist das [__Erlang__](https://de.wikipedia.org/wiki/Erlang_(Einheit)).\n",
    "\n",
    "Dabei beschreibt ein Erlang in der Praxis die Belegung eines Senders auf einem Kommunikationskanal für eine Stunde.\n",
    "\n",
    "Das Beispiel von Wikipedia umschreibt dies mit 1000 Gesprächen die jeweils 2 Minuten innerhalb einer Stunde dauern. Oder $$\\frac{1000 \\cdot 2 \\;\\text{min}}{60 \\;\\text{min}} = 33\\frac{1}{3}\\;\\text{Erl}$$\n",
    "\n",
    "Mittels Zufallszahlen können wir die Teilnehmer an der Antenne modellieren. Dazu nehmen wir als Referenz 1 Stunde. Der Wert der Zufallszahlen soll dann Sinngemäß die Auslastung für jede Minute in Prozent darstellen."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "77ced0c5-9364-4ebc-84e4-da9c5d4a5663",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-cee3e9bba1efef06",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "## Aufgabe - Zufallswerte generieren\n",
    "\n",
    "Nutzen Sie ihr Wissen aus vorangegangem Kapitel und füllen Sie die Liste `client` mit 60 zufälligen Werten aus einem PCG64. Nutzen Sie als Startwert `420` für die `SeedSequence`. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 112,
   "id": "2fdefdfc-68fd-4b4a-954b-62a6a8bb92d5",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-1f4cc8b24b379755",
     "locked": false,
     "schema_version": 3,
     "solution": true,
     "task": false
    }
   },
   "outputs": [],
   "source": [
    "clients = list()\n",
    "# BEGIN SOLUTION\n",
    "clients = [Generator(PCG64(s)).random() for s in SeedSequence(420).spawn(60)]\n",
    "# END SOLUTION"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 124,
   "id": "db2aff39-dada-42bf-b0ac-b4503dd934dd",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-23031668cfe4f02d",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 51960 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Visualisierung ihrer Lösung\n",
    "plt.plot(range(len(clients)), clients, color='r', label='Clients')\n",
    "plt.title(\"Verkehrsaufkommen an einer Antenne (in % pro minute)\")\n",
    "plt.legend()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 119,
   "id": "bd6d44ae-7039-408f-ae2c-c1c1fac2670f",
   "metadata": {
    "nbgrader": {
     "grade": true,
     "grade_id": "cell-0b0f9c7e58266631",
     "locked": true,
     "points": 1,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [],
   "source": [
    "# Hier werden ihre Lösungen getestet\n",
    "assert len(clients) == 60\n",
    "### BEGIN HIDDEN TESTS\n",
    "assert clients == [Generator(PCG64(s)).random() for s in SeedSequence(420).spawn(60)]\n",
    "### END HIDDEN TESTS"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c6c6db2b-ebf4-4120-8b21-0b365649f03d",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-b531233e3e53a390",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "## Aufgabe - Verkehrswert berechnen\n",
    "\n",
    "Der Verkehrswert berechnet sich mit $$y = \\frac{Y}{t}$$\n",
    "\n",
    "Dabei ist:\n",
    "- $Y$ die Verkehrsmenge\n",
    "- $t$ die Beobachtungsdauer (in minuten)\n",
    "\n",
    "Der Verkehrswert beschreibt die mittlere Nutzung pro Minute.\n",
    "\n",
    "Nutzen Sie die gegebene Formel und die Liste `clients` um den Verkehrswert zu berechnen. Speichern Sie das Ergebnis in der Variablen `y`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 122,
   "id": "6e75099e-4323-48a2-a1f7-37bd9ebc0a3b",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-6aaff0962b39ecd4",
     "locked": false,
     "schema_version": 3,
     "solution": true,
     "task": false
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.5221268844976396"
      ]
     },
     "execution_count": 122,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "y = float()\n",
    "# BEGIN SOLUTION\n",
    "y = sum(clients)/len(clients)\n",
    "# END SOLUTION\n",
    "y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 123,
   "id": "8ed49831-157a-479a-8c63-5d641cf72ab8",
   "metadata": {
    "nbgrader": {
     "grade": true,
     "grade_id": "cell-1195f89b67e2abed",
     "locked": true,
     "points": 1,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [],
   "source": [
    "# Hier werden ihre Lösungen getestet\n",
    "### BEGIN HIDDEN TESTS\n",
    "assert y == 0.5221268844976396\n",
    "### END HIDDEN TESTS"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "369c0f47-49c9-4ae1-bc0d-e97c88f27288",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-4f5da9b1a5c1552e",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Plotten wir im folgenden den errechneten Verkehrswert über das simulierte Verkehrsaufkommen:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 126,
   "id": "363ddd24-8438-4d4a-8f06-f250c5822da7",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-bf861fec9c51a479",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 55180 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.plot(range(len(clients)), clients, color='r', label='Clients')\n",
    "plt.plot(range(len(clients)), [y for _ in range(len(clients))], color='g', label='Verkehrswert')\n",
    "plt.title(\"Verkehrsaufkommen an einer Antenne (in % pro minute)\")\n",
    "plt.legend()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b24dc8a1-71ad-415a-a33f-675fcc7c5d10",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-bfe9fce5b3a939dc",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Behalten Sie diese Simulation im Hinterkopf. In der Übung zur Matplotlib bauen wir diese weiter aus."
   ]
  }
 ],
 "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.12.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
