Files

255 lines
5.9 KiB
Python

from math import cos, sin
from typing import TypeAlias
Number: TypeAlias = int | float
Matrix: TypeAlias = list[list[Number]]
def matmul(A: Matrix, B: Matrix) -> Matrix:
"""Multiply two matrices.
Args:
A: The first matrix.
B: The second matrix.
Returns:
The resulting matrix product.
Raises:
ValueError: If either matrix is empty, not rectangular,
or if their dimensions are incompatible.
"""
if not A or not B:
raise ValueError("Matrices must not be empty.")
if not A[0] or not B[0]:
raise ValueError("Matrices must have at least one column.")
cols_A = len(A[0])
cols_B = len(B[0])
if any(len(row) != cols_A for row in A):
raise ValueError("Matrix A is not rectangular.")
if any(len(row) != cols_B for row in B):
raise ValueError("Matrix B is not rectangular.")
if cols_A != len(B):
raise ValueError(
"The number of columns in A must equal the number of rows in B."
)
rows_A = len(A)
rows_B = len(B)
result: Matrix = [
[0 for _ in range(cols_B)]
for _ in range(rows_A)
]
for i in range(rows_A):
for j in range(cols_B):
for k in range(rows_B):
result[i][j] += A[i][k] * B[k][j]
return result
def transpose(matrix: Matrix) -> Matrix:
"""Return the transpose of a matrix.
Parameters
----------
matrix : List[List[Number]]
Matrix to transpose.
Returns
-------
List[List[Number]]
Transposed matrix where rows become columns.
Raises
------
ValueError
If the input matrix is empty or not rectangular.
"""
if not matrix:
raise ValueError("Cannot transpose an empty matrix.")
n_rows = len(matrix)
n_cols = len(matrix[0])
if any(len(row) != n_cols for row in matrix):
raise ValueError("Input matrix is not rectangular.")
return [[matrix[i][j] for i in range(n_rows)] for j in range(n_cols)]
def rotate_matrix(matrix: Matrix) -> None:
"""Rotate a square matrix 90 degrees clockwise in-place.
The algorithm combines transposition and row reversal,
running in O(n²) time with O(1) additional space.
Parameters
----------
matrix : Matrix
Square matrix to be modified.
Raises
------
ValueError
If the matrix is empty or not square.
"""
n = len(matrix)
if n == 0:
raise ValueError("Matrix must not be empty.")
for row in matrix:
if len(row) != n:
raise ValueError("Matrix must be square.")
# Transpose the matrix
for i in range(n):
for j in range(i + 1, n):
matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]
# Reverse each row
for row in matrix:
row.reverse()
def rot_2D(theta: float) -> Matrix:
"""Create a rotation matrix for a 2D rotation.
Parameters
----------
theta : float
Rotation angle in radians.
Returns
-------
Matrix
A 2x2 rotation matrix.
"""
return [
[cos(theta), -sin(theta)],
[sin(theta), cos(theta)],
]
def rot_3D(angle: float, axis: str) -> Matrix:
"""Create a 3D rotation matrix around the specified axis.
Args:
angle: Rotation angle in radians.
axis: Axis of rotation ('x', 'y', or 'z').
Returns:
A 3x3 rotation matrix.
Raises:
ValueError: If the specified axis is invalid.
"""
c = cos(angle)
s = sin(angle)
if axis == "x":
return [
[1.0, 0.0, 0.0],
[0.0, c, -s],
[0.0, s, c],
]
elif axis == "y":
return [
[c, 0.0, s],
[0.0, 1.0, 0.0],
[-s, 0.0, c],
]
elif axis == "z":
return [
[c, -s, 0.0],
[s, c, 0.0],
[0.0, 0.0, 1.0],
]
else:
raise ValueError("Axis must be 'x', 'y', or 'z'.")
def project_ortho(
point: tuple[float, float, float],
scale: float = 1,
) -> tuple[float, float]:
"""Project a 3D point orthogonally onto the xy-plane.
Args:
point: A 3D point containing x, y, and z coordinates.
scale: Scaling factor for the projected coordinates.
Returns:
The scaled 2D coordinates of the projected point.
"""
x, y, z = point
return (x * scale, y * scale)
def project_perspective(
point: tuple[float, float, float],
focal_length: float = 1.0,
scale: float = 1.0,
camera_distance: float = 4.0,
) -> tuple[float, float]:
"""Project a 3D point onto a 2D plane using perspective.
Args:
point: The 3D point containing x, y, and z coordinates.
focal_length: Controls the strength of the perspective.
scale: Scaling factor for the projected coordinates.
camera_distance: Distance offset of the camera.
Returns:
The projected 2D coordinates.
Raises:
ValueError: If the projection denominator is zero.
"""
x, y, z = point
depth = z + camera_distance
if depth == 0:
raise ValueError("Projection depth must not be zero.")
projected_x = x * focal_length / depth * scale
projected_y = y * focal_length / depth * scale
return (projected_x, projected_y)
if __name__ == "__main__":
# Beispiel Multiplikation
A1 = [[1, 2, 3], [4, 5, 6]]
B1 = [[7, 8], [9, 10], [11, 12]]
expected1 = [[58, 64], [139, 154]]
assert matmul(A1, B1) == expected1
print("Beispiel 1 erfolgreich:", matmul(A1, B1))
# Beispiel Rotation
matrix = [[1, 2], [3, 4]]
expected = [[3, 1], [4, 2]]
rotate_matrix(matrix)
assert matrix == expected
print("Rotation erfolgreich:", matrix)
# Beispiel Transponierung
matrix = [[1, 2, 3], [4, 5, 6]]
expected = [[1, 4], [2, 5], [3, 6]]
assert transpose(matrix) == expected
print("Transponierung erfolgreich:", transpose(matrix))