172 lines
5.7 KiB
Python
172 lines
5.7 KiB
Python
"""Mathematische Hilfsfunktionen für Matrixoperationen."""
|
|
|
|
import math
|
|
|
|
|
|
def matmul(matrix_a: list[list[float]], matrix_b: list[list[float]]) -> list[list[float]]:
|
|
"""Berechnet das Matrixprodukt zweier Matrizen (matrix_a @ matrix_b).
|
|
|
|
:param matrix_a: Die linke Matrix.
|
|
:param matrix_b: Die rechte Matrix.
|
|
:return: Die Ergebnismatrix der Multiplikation.
|
|
:raises ValueError: Wenn eine Matrix leer ist, Zeilen ungleich lang sind
|
|
oder die Spaltenanzahl von matrix_a nicht der Zeilenanzahl
|
|
von matrix_b entspricht.
|
|
"""
|
|
if not matrix_a or not matrix_b or not matrix_a[0] or not matrix_b[0]:
|
|
raise ValueError("Beide Matrizen müssen nicht-leer sein.")
|
|
|
|
rows_a, cols_a = len(matrix_a), len(matrix_a[0])
|
|
rows_b, cols_b = len(matrix_b), len(matrix_b[0])
|
|
|
|
if any(len(row) != cols_a for row in matrix_a) or any(len(row) != cols_b for row in matrix_b):
|
|
raise ValueError("Alle Zeilen einer Matrix müssen dieselbe Länge besitzen.")
|
|
|
|
if cols_a != rows_b:
|
|
raise ValueError("Matrixdimensionen stimmen für die Multiplikation nicht überein.")
|
|
|
|
result: list[list[float]] = []
|
|
for i in range(rows_a):
|
|
result_row: list[float] = []
|
|
for j in range(cols_b):
|
|
dot_product = sum(matrix_a[i][k] * matrix_b[k][j] for k in range(cols_a))
|
|
result_row.append(dot_product)
|
|
result.append(result_row)
|
|
return result
|
|
|
|
|
|
def transpose(matrix: list[list[float]]) -> list[list[float]]:
|
|
"""Transponiert eine Matrix, sodass alle Zeilen zu Spalten und Spalten zu Zeilen werden.
|
|
|
|
:param matrix: Die zu transponierende Matrix.
|
|
:return: Die transponierte Matrix.
|
|
:raises ValueError: Wenn die Matrix leer ist oder die Zeilen ungleichmäßig
|
|
viele Spalten besitzen.
|
|
"""
|
|
if not matrix or not matrix[0]:
|
|
raise ValueError("Die Matrix darf nicht leer sein.")
|
|
|
|
num_cols = len(matrix[0])
|
|
if any(len(row) != num_cols for row in matrix):
|
|
raise ValueError("Alle Zeilen müssen dieselbe Länge besitzen.")
|
|
|
|
num_rows = len(matrix)
|
|
return [
|
|
[matrix[row_idx][col_idx] for row_idx in range(num_rows)]
|
|
for col_idx in range(num_cols)
|
|
]
|
|
|
|
|
|
def rot_2d(angle: float) -> list[list[float]]:
|
|
"""Berechnet die 2D-Rotationsmatrix für einen gegebenen Drehwinkel.
|
|
|
|
Die Rotationsmatrix R(theta) lautet:
|
|
[[cos(theta), -sin(theta)],
|
|
[sin(theta), cos(theta)]]
|
|
|
|
:param angle: Der Rotationswinkel im Bogenmaß (Radiant).
|
|
:return: Die 2x2-Rotationsmatrix als verschachtelte Liste.
|
|
"""
|
|
cos_theta = math.cos(angle)
|
|
sin_theta = math.sin(angle)
|
|
return [
|
|
[cos_theta, -sin_theta],
|
|
[sin_theta, cos_theta],
|
|
]
|
|
|
|
if __name__ == "__main__":
|
|
a1 = [
|
|
[3, 4, -1, 4],
|
|
[-2, 2, 5, 1],
|
|
]
|
|
b1 = [
|
|
[1, 3, -2],
|
|
[2, 5, 1],
|
|
[-1, 4, -4],
|
|
[2, 3, 6],
|
|
]
|
|
expected_c1 = [
|
|
[20, 37, 26],
|
|
[-1, 27, -8],
|
|
]
|
|
c1 = matmul(a1, b1)
|
|
assert c1 == expected_c1, f"Fehler in Beispiel 1: Erwartet {expected_c1}, erhalten {c1}"
|
|
print("Test 1 (Skript-Beispiel) erfolgreich bestanden!")
|
|
|
|
a2 = [
|
|
[1, 2],
|
|
[3, 4],
|
|
]
|
|
identity = [
|
|
[1, 0],
|
|
[0, 1],
|
|
]
|
|
c2 = matmul(a2, identity)
|
|
assert c2 == a2, f"Fehler in Beispiel 2: Erwartet {a2}, erhalten {c2}"
|
|
print("Test 2 (Einheitsmatrix) erfolgreich bestanden!")
|
|
|
|
incompatible_b = [
|
|
[1, 2],
|
|
[3, 4],
|
|
]
|
|
try:
|
|
matmul(a1, incompatible_b)
|
|
raise AssertionError("Fehlerfall wurde nicht abgefangen!")
|
|
except ValueError:
|
|
print("Test 3 (Fehlerfall inkompatible Dimensionen) erfolgreich abgefangen!")
|
|
|
|
# Tests für Transpose
|
|
example_matrix = [
|
|
[1, 2, 3],
|
|
[4, 5, 6],
|
|
]
|
|
expected_transposed = [
|
|
[1, 4],
|
|
[2, 5],
|
|
[3, 6],
|
|
]
|
|
assert transpose(example_matrix) == expected_transposed, (
|
|
f"Erwartet: {expected_transposed}, Erhalten: {transpose(example_matrix)}"
|
|
)
|
|
print("Test 4 (transpose) erfolgreich bestanden!")
|
|
|
|
# Test transpose: Leere Matrix
|
|
try:
|
|
transpose([])
|
|
raise AssertionError("Leere Matrix wurde nicht abgefangen!")
|
|
except ValueError:
|
|
print("Test 5 (transpose leere Matrix) erfolgreich abgefangen!")
|
|
|
|
# Test transpose: Ungleichmäßige Zeilenlänge
|
|
uneven_matrix = [
|
|
[1, 2, 3],
|
|
[4, 5],
|
|
]
|
|
try:
|
|
transpose(uneven_matrix)
|
|
raise AssertionError("Ungleichmäßige Matrix wurde nicht abgefangen!")
|
|
except ValueError:
|
|
print("Test 6 (transpose ungleichmäßige Matrix) erfolgreich abgefangen!")
|
|
|
|
# Test 7: Rotation um 0 Radianten (entspricht der Einheitsmatrix)
|
|
rot_0 = rot_2d(0.0)
|
|
assert math.isclose(rot_0[0][0], 1.0, abs_tol=1e-9)
|
|
assert math.isclose(rot_0[0][1], 0.0, abs_tol=1e-9)
|
|
assert math.isclose(rot_0[1][0], 0.0, abs_tol=1e-9)
|
|
assert math.isclose(rot_0[1][1], 1.0, abs_tol=1e-9)
|
|
print("Test rot_2D (0 Radiant) erfolgreich bestanden!")
|
|
|
|
# Test 8: Rotation um 90 Grad (pi / 2) -> [[0, -1], [1, 0]]
|
|
rot_90 = rot_2d(math.pi / 2)
|
|
assert math.isclose(rot_90[0][0], 0.0, abs_tol=1e-9)
|
|
assert math.isclose(rot_90[0][1], -1.0, abs_tol=1e-9)
|
|
assert math.isclose(rot_90[1][0], 1.0, abs_tol=1e-9)
|
|
assert math.isclose(rot_90[1][1], 0.0, abs_tol=1e-9)
|
|
print("Test rot_2D (pi / 2 Radiant) erfolgreich bestanden!")
|
|
|
|
# Test 9: Drehung des Vektors (1, 0) um 90 Grad ergibt (0, 1) via matmul
|
|
point = [[1.0], [0.0]]
|
|
rotated_point = matmul(rot_90, point)
|
|
assert math.isclose(rotated_point[0][0], 0.0, abs_tol=1e-9)
|
|
assert math.isclose(rotated_point[1][0], 1.0, abs_tol=1e-9)
|
|
print("Test rot_2D (Vektordrehung mit matmul) erfolgreich bestanden!") |