From da878ec3e6bedee1fd1e556f076b14a5d09de3dc Mon Sep 17 00:00:00 2001 From: Nils Pukropp Date: Tue, 30 Jan 2024 05:01:05 +0100 Subject: [PATCH] added sorting exercise --- loops/sort/README.md | 84 +++++++++++++++++++++++++++++++++ loops/sort/solution/README.md | 87 +++++++++++++++++++++++++++++++++++ loops/sort/solution/sort.py | 27 +++++++++++ loops/sort/sort.py | 9 ++++ loops/sort/test_sort.py | 22 +++++++++ 5 files changed, 229 insertions(+) create mode 100644 loops/sort/README.md create mode 100644 loops/sort/solution/README.md create mode 100644 loops/sort/solution/sort.py create mode 100644 loops/sort/sort.py create mode 100644 loops/sort/test_sort.py diff --git a/loops/sort/README.md b/loops/sort/README.md new file mode 100644 index 0000000..6240cf5 --- /dev/null +++ b/loops/sort/README.md @@ -0,0 +1,84 @@ +# [Sortierte Datenstrukturen](src/branch/main/loops/sort/sort.py) + +## `selection_sort` + +**Selection Sort** basiert auf dem Konzept durch jedes Element in einer Liste zu gehen um dann das kleineste Element in den übrigen zu suchen und mit dem aktuellen Element zu tauschen. + +- Wichtig ist dass dabei die übergebene Liste nicht verändert wird und eine neue sortierte erstellt wird + - Manuell erstellen mit loop oder `deepcopy` + +Beispiel: +``` +arr[] = 64 25 12 22 11 + +// Find the minimum element in arr[0...4] +// and place it at beginning +11 25 12 22 64 + +// Find the minimum element in arr[1...4] +// and place it at beginning of arr[1...4] +11 12 25 22 64 + +// Find the minimum element in arr[2...4] +// and place it at beginning of arr[2...4] +11 12 22 25 64 + +// Find the minimum element in arr[3...4] +// and place it at beginning of arr[3...4] +11 12 22 25 64 +``` + +
+Hilfe 1: Pseudocode + +``` +for e_i in xs + kleinstes_e = e_i + for e_j in xs mit: j = i + 1 + if kleinstes_e > e_j + kleinstes_e = e_j + vertausche e_i und kleinstes_e +``` + +
+ +## `binary_search` + +Hierbei handelt es sich um einen Algorithmus welcher die Position von einem Wert in einem sortierten Array schnell findet. + +Beispiele: +```python +binary_search([1, 2, 3, 4, 5], 4) # 3, was der index von 4 ist +binary_search(["a", "b", "c", "d"], "b") # 1, was der index von "b" ist +``` + +- Dazu definieren wir eine linke Grenze `left` mit dem Anfangswert `0` und eine rechte Grenze `right` mit dem Anfangswert `len(xs) - 1`, damit können wir unser gesuchtes Element immer weiter eingrenzen. +- Jetzt wissen wir durch die Sortierung dass `left <= right` sein muss. +- Also gehen wir jetzt durch unsere Liste und berechnen die Mitte `middle` von `left` und `right` aus und vergleichen diese mit unserem gesuchten Wert. + - Ist `middle` kleiner als unser Wert dann können wir `left = middle + 1` setzen + - Ist `middle` größer als unser Wert dann können wir `right = middle - 1` setzen + - Sonst haben wir unseren Wert, nämlich `middle`, gefunden und können diesen zurückgeben +- Wenn der Wert nicht existiert wird `None` zurückgegeben +- Für leere Listen soll auch `None` zurückgegeben werden + + +
+Hilfe 1: Pseudocode + +``` +linker_index = 0 +rechter_index = länge - 1 +solange linker_index <= rechter_index dann + mitte = (linker_index + rechter_index) / 2 + Wenn Liste[mitte] < gesuchter Wert dann + linker_index = mitte + 1 + Wenn Liste[mitte] > gesuchter Wert dann + rechter_index = mitte - 1 + Sonst + return Liste[mitte] +return Nichts +``` + +
+ +## [Hier gehts zur Lösung](src/branch/main/loops/sort/solution) \ No newline at end of file diff --git a/loops/sort/solution/README.md b/loops/sort/solution/README.md new file mode 100644 index 0000000..cf2e917 --- /dev/null +++ b/loops/sort/solution/README.md @@ -0,0 +1,87 @@ +# Solution + +## `selection_sort` + +Zunächst müssen wir eine neue Liste mit den selben Elementen erstellen, am schnellsten geht das mit `copy.deepcopy` welche eine vollständige Copie erstellt. + +```python +from copy import deepcopy + +xss = deepcopy(xs) +``` + +Wir müssen durch jedes Element gehen, also + +```python +for i in range(len(xss)): + # ... +``` +Nun wollen wir in den übrigen Elementen ($j = i + 1$) das kleinste finden + +```python +for i in range(len(xss)): + min_i = i # index vom kleinsten Element, + # das aktuelle i falls dieses schon das kleinste ist + for j in range(i + 1, len(xss)): + # ... +``` + +Jetzt vergleichen wir nur noch jedes j-te Element mit dem aktuellen kleinsten + +```python +for i in range(len(xss)): + min_i = i # index vom kleinsten Element, + # das aktuelle i falls dieses schon das kleinste ist + for j in range(i + 1, len(xss)): + if xss[j] < xss[min_i]: + min_i = j # kleineres element gefunden +``` + +Nun können wir einfach das i-te mit dem min_i vertauschen (entweder ist es i oder wir haben ein kleineres gefunden) + +```python + xss[i], xss[min_i] = xss[min_i], xss[i] # tauschen i mit min_i +``` + +und am Ende können wir `xss` zurückgeben + +```python +return xss +``` + +## `binary_search` + +Hierfür definieren wir zunächst `left = 0` und `right = len(xs) - 1`, dann können wir die Schleifenbedingung definieren als + +```python +while left <= right: + # ... +``` + +weil sobald diese nicht mehr gilt konnten wir den Wert nicht finden. (Links und Rechts tauschen) und wir können `None` zurückgeben. + +Nun berechnen wir den mittleren Index mit dem integer division (oder auch floor weil es die Zahl hinterm Komma verwirft). + +```python + middle = (left + right) // 2 +``` + +Nun müssen wir nur noch `xs[middle]` mit `value` vergleichen + +```python + if xs[middle] < value: + left = middle + 1 + elif xs[middle] > value: + right = middle - 1 +``` + +Und wenn `xs[middle] == value` ist haben wir unseren Index `middle` gefunden. + +```python + if xs[middle] < value: + left = middle + 1 + elif xs[middle] > value: + right = middle - 1 + else: + return middle +``` \ No newline at end of file diff --git a/loops/sort/solution/sort.py b/loops/sort/solution/sort.py new file mode 100644 index 0000000..54133f1 --- /dev/null +++ b/loops/sort/solution/sort.py @@ -0,0 +1,27 @@ +from copy import deepcopy +from typing import Iterator, Optional + + +def selection_sort[T](xs: Iterator[T]) -> Iterator[T]: + length = len(xs) + xss = deepcopy(xs) + for i in range(length): + min_i = i + for j in range(i + 1, length): + if xss[j] < xss[min_i]: + min_i = j + xss[i], xss[min_i] = xss[min_i], xss[i] + return xss + +def binary_search[T](xs: list[T], value: T) -> Optional[int]: + left = 0 + right = len(xs) - 1 + while left <= right: + middle = (left + right) // 2 + if xs[middle] < value: + left = middle + 1 + elif xs[middle] > value: + right = middle - 1 + else: + return middle + return None \ No newline at end of file diff --git a/loops/sort/sort.py b/loops/sort/sort.py new file mode 100644 index 0000000..70e4776 --- /dev/null +++ b/loops/sort/sort.py @@ -0,0 +1,9 @@ +from typing import Iterator, Optional + + +def selection_sort[T](xs: Iterator[T]) -> Iterator[T]: + return [] + + +def binary_search[T](xs: list[T], value: T) -> Optional[int]: + return None \ No newline at end of file diff --git a/loops/sort/test_sort.py b/loops/sort/test_sort.py new file mode 100644 index 0000000..b74b3af --- /dev/null +++ b/loops/sort/test_sort.py @@ -0,0 +1,22 @@ +from random import randint +from typing import Iterator +from sort import selection_sort, binary_search + +def get_random_collection(min: int, max: int, size: int) -> Iterator[int]: + return [randint(min, max) for _ in range(size)] + +def test_selection_sort(): + xs = [5, 4, 3, 2, 1, 0] + assert list(selection_sort(xs)) == sorted(xs) + assert xs == [5, 4, 3, 2, 1, 0], "list was modified in `selection_sort` return a copy instead" + xs = get_random_collection(0, 100, 100) + print(xs) + assert list(selection_sort(xs)) == sorted(xs) + +def test_binary_search(): + xs = sorted(set(get_random_collection(0, 10000, 100))) + for i, e in enumerate(xs): + assert binary_search(xs, e) == i + assert binary_search([], 1) == None + assert binary_search([2], 1) == None + assert binary_search([2, 3], 1) == None \ No newline at end of file