"""Modul zur Matrizenmultiplikation.""" import math import sys from numbers import Number def _validate_matrix(matrix: object, name: str) -> list[list[Number]]: """Prüft, ob es sich um eine gültige Matrix handelt. Args: matrix (object): Zu prüfende Matrix. name (str): Name der Matrix für die Fehlermeldung. Returns: list[list[Number]]: Validierte Matrix. Raises: TypeError: Wenn die Matrix keine Liste ist oder nicht-numerische Elemente enthält. ValueError: Wenn die Matrix leer ist oder unregelmäßige Zeilenlängen aufweist. """ if not isinstance(matrix, list): raise TypeError(f"'{name}' muss eine Liste von Listen sein, nicht {type(matrix).__name__}.") if len(matrix) == 0: raise ValueError(f"'{name}' darf nicht leer sein.") for index, row in enumerate(matrix): if not isinstance(row, list): raise TypeError(f"Zeile {index} von '{name}' muss eine Liste sein, nicht {type(row).__name__}.") if len(row) == 0: raise ValueError(f"Zeile {index} von '{name}' darf nicht leer sein.") if len(row) != len(matrix[0]): raise ValueError( f"Zeile {index} von '{name}' hat {len(row)} Spalten, erwartet werden {len(matrix[0])}." ) for column, value in enumerate(row): if isinstance(value, bool) or not isinstance(value, Number): raise TypeError( f"Element [{index}][{column}] von '{name}' muss eine Zahl sein, " f"nicht {type(value).__name__}." ) return matrix def matmul(matrix_a: list[list[Number]], matrix_b: list[list[Number]]) -> list[list[Number]]: """Multipliziert zwei Matrizen. Die Anzahl der Spalten von ``matrix_a`` muss mit der Anzahl der Zeilen von ``matrix_b`` übereinstimmen. Args: matrix_a (list[list[Number]]): Erste Matrix der Form m x n. matrix_b (list[list[Number]]): Zweite Matrix der Form n x p. Returns: list[list[Number]]: Ergebnis der Multiplikation der Form m x p. Raises: TypeError: Wenn eine der Eingaben keine gültige Matrix aus Zahlen ist. ValueError: Wenn die Matrizen leer sind oder die Dimensionen nicht zueinander passen. """ matrix_a = _validate_matrix(matrix_a, "matrix_a") matrix_b = _validate_matrix(matrix_b, "matrix_b") rows_a = len(matrix_a) columns_a = len(matrix_a[0]) rows_b = len(matrix_b) columns_b = len(matrix_b[0]) if columns_a != rows_b: raise ValueError( f"Dimensionen passen nicht: matrix_a hat {columns_a} Spalten, " f"matrix_b hat {rows_b} Zeilen." ) result: list[list[Number]] = [[0 for _ in range(columns_b)] for _ in range(rows_a)] for i in range(rows_a): for j in range(columns_b): total = 0 for k in range(columns_a): total += matrix_a[i][k] * matrix_b[k][j] result[i][j] = total return result def transpose(matrix: list[list[Number]]) -> list[list[Number]]: """Transponiert eine Matrix. Vertauscht Zeilen und Spalten der Matrix. Aus einer Matrix der Form m x n wird eine Matrix der Form n x m. Args: matrix (list[list[Number]]): Zu transponierende Matrix. Returns: list[list[Number]]: Transponierte Matrix der Form n x m. Raises: TypeError: Wenn die Eingabe keine gültige Matrix aus Zahlen ist. ValueError: Wenn die Matrix leer ist oder unregelmäßige Zeilenlängen aufweist. """ matrix = _validate_matrix(matrix, "matrix") rows = len(matrix) columns = len(matrix[0]) return [[matrix[i][j] for i in range(rows)] for j in range(columns)] def rotate(angle: float) -> list[list[float]]: """Ergänzter Kommentar""" """Erstellt die Rotationsmatrix für einen Winkel. Args: angle (float): Drehwinkel im Bogenmaß. Returns: list[list[float]]: 2x2-Rotationsmatrix, die auf einen Vektor angewendet werden kann. Raises: TypeError: Wenn ``angle`` keine Zahl ist. """ if isinstance(angle, bool) or not isinstance(angle, Number): raise TypeError(f"'angle' muss eine Zahl sein, nicht {type(angle).__name__}.") cosine = math.cos(angle) sine = math.sin(angle) return [[cosine, -sine], [sine, cosine]] def _parse_number(token: str) -> float: """Wandelt einen eingegebenen Text in eine Zahl um. Args: token (str): Einzelner Eingabewert. Returns: float: Zahlwert des Eingabetokens. Raises: ValueError: Wenn der Text keine gültige Zahl ist. """ try: return float(token) except ValueError as error: raise ValueError(f"'{token}' ist keine gültige Zahl.") from error def _read_matrix(name: str) -> list[list[float]]: """Liest eine Matrix zeilenweise aus der Konsole ein. Der Benutzer gibt zuerst die Anzahl der Zeilen an. Anschließend wird jede Zeile als Leerzeichen-getrennte Liste von Zahlen eingegeben. Bei ungültiger Eingabe wird die jeweilige Eingabe erneut abgefragt. Args: name (str): Name der Matrix für die Ausgabe. Returns: list[list[float]]: Eingelesene Matrix. """ print(f"\nEingabe der Matrix {name}:") while True: raw = input("Anzahl der Zeilen: ").strip() try: rows = int(raw) except ValueError: print(f"Fehler: '{raw}' ist keine ganze Zahl. Bitte erneut eingeben.") continue if rows <= 0: print("Fehler: Die Anzahl der Zeilen muss größer als 0 sein.") continue break matrix: list[list[float]] = [] expected_columns: int | None = None for index in range(rows): while True: raw_row = input(f"Zeile {index} (Zahlen durch Leerzeichen getrennt): ").strip() tokens = raw_row.split() if not tokens: print("Fehler: Die Zeile darf nicht leer sein. Bitte erneut eingeben.") continue try: row = [_parse_number(token) for token in tokens] except ValueError as error: print(f"Fehler: {error} Bitte erneut eingeben.") continue if expected_columns is None: expected_columns = len(row) elif len(row) != expected_columns: print( f"Fehler: Die Zeile hat {len(row)} Spalten, " f"erwartet werden {expected_columns}. Bitte erneut eingeben." ) continue matrix.append(row) break return matrix def _print_matrix(matrix: list[list[Number]]) -> None: """Gibt eine Matrix formatiert auf der Konsole aus. Args: matrix (list[list[Number]]): Auszugebende Matrix. """ for row in matrix: print(" [" + ", ".join(str(value) for value in row) + "]") def main() -> int: """Startet das Programm und liest zwei Matrizen zur Multiplikation ein. Returns: int: Exit-Code des Programms (0 bei Erfolg, 1 bei Fehler). """ print(rotate(45)) if __name__ == "__main__": sys.exit(main())