Version 0
This commit is contained in:
commit
d49bccdd7a
1 file changed
+234
+234
@@ -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))
|
||||||
|
|
||||||
Reference in new issue
Block a user