Version 0

This commit is contained in:
hilderyo103369 committed 2026-10-19 17:42:28 +02:00
commit d49bccdd7a
1 file changed
+234
+234
View File
@@ -0,0 +1,234 @@
"""Rechen-Hilfen für Matrizen.
Dieses Modul stellt vier Funktionen bereit:
* :func:`matmul` – multipliziert zwei (auch nicht-quadratische)
Matrizen.
* :func:`transpose` – transponiert eine Matrix.
* :func:`rotate` – rotiert eine Matrix um 90 Grad im Uhrzeigersinn.
* :func:`rot_2D` – erzeugt die 2x2-Rotationsmatrix für einen Winkel.
Die Implementierung validiert alle Eingaben:
* Alle Zeilen einer Matrix müssen gleich lang sein.
* Bei :func:`matmul` muss die Spaltenzahl der ersten Matrix der
Zeilenzahl der zweiten Matrix entsprechen – die klassische Regel
der Matrixmultiplikation.
* Alle Elemente müssen ``int`` oder ``float`` sein – andernfalls wird
ein :class:`TypeError` ausgelöst.
Nicht kompatible Matrizen führen zu einem :class:`ValueError`.
Unter ``__main__`` befindet sich ein kleiner Testrahmen zum schnellen
manuellen Ausprobieren.
Für dieses Rechenmodul werden keine externen Abhängigkeiten benötigt.
"""
from __future__ import annotations
import math
__all__ = ["matmul", "transpose", "rotate", "rot_2D"]
def _validate(matrix: list[list[float]], name: str) -> tuple[int, int]:
"""Prüfe eine Matrix und gib ihre Form ``(Zeilen, Spalten)`` zurück.
Akzeptiert auch die leere Matrix ``[]`` (Form ``(0, 0)``). Alle
Zeilen müssen gleich lang und alle Elemente müssen ``int`` oder
``float`` sein.
Raises
------
ValueError
Wenn die Zeilen ungleich lang sind.
TypeError
Wenn ein Element keine Zahl ist.
"""
if not matrix:
return (0, 0)
n_rows = len(matrix)
n_cols = len(matrix[0])
for row in matrix:
if len(row) != n_cols:
raise ValueError(
f"Alle Zeilen von {name} müssen gleich lang sein "
f"(erwartet {n_cols}, erhalten {len(row)})"
)
for x in row:
if not isinstance(x, (int, float)):
raise TypeError(
f"Elemente von {name} müssen Zahlen sein, "
f"aber {x!r} ist {type(x).__name__}"
)
return (n_rows, n_cols)
# ================= Matrixmultiplikation =================
def matmul(
a: list[list[float]], b: list[list[float]]
) -> list[list[float]]:
"""Berechne das Produkt ``a @ b`` zweier Matrizen.
Die Matrizen dürfen auch rechteckig sein. Beide Eingaben werden
nicht verändert.
Parameters
----------
a, b : list[list[float]]
Zweidimensionale Listen mit Zahlen. Die Spaltenzahl von
``a`` muss der Zeilenzahl von ``b`` entsprechen. Die leere
Matrix (``[]``) ist als Operand nicht erlaubt.
Returns
-------
list[list[float]]
Das Matrixprodukt ``a @ b``.
Raises
------
ValueError
Wenn eine der Matrizen leer ist, Zeilen ungleich lang sind oder
die Dimensionen nicht zusammenpassen.
TypeError
Wenn ein Element keine Zahl ist.
"""
rows_a, cols_a = _validate(a, "'a'")
rows_b, cols_b = _validate(b, "'b'")
if rows_a == 0 or rows_b == 0:
raise ValueError(
"Leere Matrizen sind als Operanden der Multiplikation "
"nicht erlaubt"
)
if cols_a != rows_b:
raise ValueError(
f"Spaltenzahl von 'a' ({cols_a}) und Zeilenzahl von 'b' "
f"({rows_b}) müssen übereinstimmen"
)
# Initialisiere die Ergebnismatrix (rows_a x cols_b) mit Nullen.
result: list[list[float]] = [[0] * cols_b for _ in range(rows_a)]
# Zeilenweise durchlaufen (i), dann das gemeinsame Element (k):
# ikj-Reihenfolge ist cache-freundlicher als das klassische ijk.
for i in range(rows_a):
row_result = result[i]
for k in range(cols_a):
aik = a[i][k]
if aik == 0:
continue
b_row = b[k]
for j in range(cols_b):
row_result[j] += aik * b_row[j]
return result
# ================= Transposition =================
def transpose(matrix: list[list[float]]) -> list[list[float]]:
"""Gib die Transponierte einer Matrix zurück.
Parameters
----------
matrix : list[list[float]]
Zweidimensionale Liste mit Zahlen. Die leere Matrix ``[]``
wird als ``[]`` zurückgegeben.
Returns
-------
list[list[float]]
Die transponierte Matrix.
Raises
------
ValueError
Wenn die Zeilen ungleich lang sind.
TypeError
Wenn ein Element keine Zahl ist.
"""
n_rows, n_cols = _validate(matrix, "matrix")
return [[matrix[i][j] for i in range(n_rows)] for j in range(n_cols)]
# ================= Rotation um 90 Grad =================
def rotate(matrix: list[list[float]]) -> list[list[float]]:
"""Rotiere eine Matrix um 90 Grad im Uhrzeigersinn.
Die erste Zeile wird zur letzten Spalte, die letzte Zeile zur
ersten Spalte. Die Eingabe wird nicht verändert.
Parameters
----------
matrix : list[list[float]]
Zweidimensionale Liste mit Zahlen. Die leere Matrix ``[]``
wird als ``[]`` zurückgegeben.
Returns
-------
list[list[float]]
Die um 90 Grad im Uhrzeigersinn rotierte Matrix.
Raises
------
ValueError
Wenn die Zeilen ungleich lang sind.
TypeError
Wenn ein Element keine Zahl ist.
"""
return [row[::-1] for row in transpose(matrix)]
# ================= 2D-Rotationsmatrix =================
def rot_2D(theta: float) -> list[list[float]]:
"""Erzeuge die Rotationsmatrix für eine Drehung um ``theta``.
Die Matrix dreht einen Punkt in der Ebene um den Winkel ``theta``
um den Ursprung:
.. math::
\\begin{pmatrix} \\cos\\theta & -\\sin\\theta \\\\
\\sin\\theta & \\cos\\theta \\end{pmatrix}
Parameters
----------
theta : float
Drehwinkel im Bogenmaß (Radiant).
Returns
-------
list[list[float]]
Die 2x2-Rotationsmatrix.
"""
cos_t = math.cos(theta)
sin_t = math.sin(theta)
return [[cos_t, -sin_t], [sin_t, cos_t]]
# ================= Testrahmen =================
if __name__ == "__main__":
# matmul mit rechteckigen Matrizen
matrix_a = [
[3, 4, -1, 4],
[-2, 2, 5, 1],
]
matrix_b = [
[1, 3, -2],
[2, 5, 1],
[-1, 4, -4],
[2, 3, 6],
]
matrix_c = matmul(matrix_a, matrix_b)
print("Ergebnis C = A * B:")
for row in matrix_c:
print(row)
# rot_2D: Drehung um 90 Grad gegen den Uhrzeigersinn
rotation = rot_2D(math.pi / 2)
point = [[1.0], [0.0]]
print("rot_2D-Ergebnis:", matmul(rotation, point))