덧셈정리 ↔ 복소수 곱 ↔ 회전 행렬 문서의 검증 코드다. if __name__ == "__main__": 아래가 자체 테스트다.
"""덧셈정리 ↔ 복소수 곱 ↔ 회전 행렬 검증.
문서: 13.덧셈정리 ↔ 복소수 곱 ↔ 회전 행렬 (비교 표, 대응 관계, 어디까지 같은가, 얻는 것, 전이 문제, 카드 C1~C3)
주장 1: 덧셈정리와 R_β R_α = R_{α+β}, e^{iα}e^{iβ} = e^{i(α+β)} (무작위 각 1000개).
주장 2: a + bi <-> [[a,-b],[b,a]]: 곱이 대응, 행렬식 = |z|², 켤레 <-> 전치, i <-> R90, R90² = -I.
주장 3: 드무아브르 (cos α + i sin α)^n = cos nα + i sin nα, R_α^n = R_{nα}, 배각 공식.
주장 4: 반사·전단은 [[a,-b],[b,a]] 꼴이 아니다. 2D 회전·복소수 곱은 교환된다.
주장 5: 3D — Rx(90°)Rz(90°) ≠ Rz(90°)Rx(90°), e1의 두 결과 (0,0,1)과 (0,1,0).
주장 6: 전이 문제 — 3 sin(ωt) + 4 sin(ωt + 90°) = 5 sin(ωt + 53.13°) (여러 t에서).
주장 7: 카드 C1 — (1+2i)(3-i) = 5+5i, 행렬 곱 [[5,-5],[5,5]].
주장 8: 구현 — d * exp(i t)가 행렬 회전과 같다.
"""
import cmath
import math
import random
def R(t):
return [[math.cos(t), -math.sin(t)], [math.sin(t), math.cos(t)]]
def mm(A, B):
return [[sum(A[i][k] * B[k][j] for k in range(len(B))) for j in range(len(B[0]))] for i in range(len(A))]
def M(z):
return [[z.real, -z.imag], [z.imag, z.real]]
def close(A, B, tol=1e-9):
return all(abs(a - b) < tol for ra, rb in zip(A, B) for a, b in zip(ra, rb))
def main():
rng = random.Random(13)
for _ in range(1000):
a, b = rng.uniform(-10, 10), rng.uniform(-10, 10)
assert abs(math.cos(a + b) - (math.cos(a) * math.cos(b) - math.sin(a) * math.sin(b))) < 1e-12
assert abs(math.sin(a + b) - (math.sin(a) * math.cos(b) + math.cos(a) * math.sin(b))) < 1e-12
assert close(mm(R(b), R(a)), R(a + b))
assert abs(cmath.exp(1j * a) * cmath.exp(1j * b) - cmath.exp(1j * (a + b))) < 1e-12
print("[OK] 주장 1·카드 C2: 덧셈정리 세 가지")
for _ in range(500):
z = complex(rng.uniform(-5, 5), rng.uniform(-5, 5))
w = complex(rng.uniform(-5, 5), rng.uniform(-5, 5))
assert close(mm(M(z), M(w)), M(z * w))
det = z.real * z.real + z.imag * z.imag
assert abs(det - abs(z) ** 2) < 1e-9
Mt = [list(r) for r in zip(*M(z))]
assert close(Mt, M(z.conjugate()))
R90 = [[0, -1], [1, 0]]
assert M(1j) == [[0, -1], [1, 0]] == R90 and mm(R90, R90) == [[-1, 0], [0, -1]]
print("[OK] 주장 2: 복소수 <-> 행렬")
for _ in range(300):
a, n = rng.uniform(-3, 3), rng.randint(0, 12)
assert abs(complex(math.cos(a), math.sin(a)) ** n - complex(math.cos(n * a), math.sin(n * a))) < 1e-9
P = [[1.0, 0.0], [0.0, 1.0]]
for _ in range(n):
P = mm(P, R(a))
assert close(P, R(n * a), 1e-9)
z2 = complex(math.cos(a), math.sin(a)) ** 2
assert abs(z2.real - (math.cos(a) ** 2 - math.sin(a) ** 2)) < 1e-12 and abs(z2.imag - 2 * math.sin(a) * math.cos(a)) < 1e-12
print("[OK] 주장 3: 드무아브르와 배각")
is_cx = lambda A: A[0][0] == A[1][1] and A[0][1] == -A[1][0]
assert not is_cx([[1, 0], [0, -1]]) and not is_cx([[1, 1], [0, 1]])
for _ in range(200):
a, b = rng.uniform(-5, 5), rng.uniform(-5, 5)
assert close(mm(R(a), R(b)), mm(R(b), R(a)))
print("[OK] 주장 4: 복소수가 되는 행렬, 2D 교환")
Rx = [[1, 0, 0], [0, 0, -1], [0, 1, 0]]
Rz = [[0, -1, 0], [1, 0, 0], [0, 0, 1]]
assert mm(Rx, Rz) != mm(Rz, Rx)
e1 = [[1], [0], [0]]
assert [r[0] for r in mm(Rx, mm(Rz, e1))] == [0, 0, 1]
assert [r[0] for r in mm(Rz, mm(Rx, e1))] == [0, 1, 0]
print("[OK] 주장 5·카드 C3: 3D 비교환")
z = 3 + 4j
A, phi = abs(z), math.degrees(cmath.phase(z))
assert abs(A - 5) < 1e-12 and abs(phi - 53.13) < 0.01
w = 2.0
for _ in range(200):
t = rng.uniform(0, 10)
lhs = 3 * math.sin(w * t) + 4 * math.sin(w * t + math.pi / 2)
assert abs(lhs - 5 * math.sin(w * t + math.radians(phi))) < 1e-9
print("[OK] 주장 6: 전이 문제")
assert (1 + 2j) * (3 - 1j) == 5 + 5j
assert mm([[1, -2], [2, 1]], [[3, 1], [-1, 3]]) == [[5, -5], [5, 5]]
print("[OK] 주장 7·카드 C1")
for _ in range(200):
d = complex(rng.uniform(-3, 3), rng.uniform(-3, 3))
t = rng.uniform(-4, 4)
r = d * cmath.exp(1j * t)
v = mm(R(t), [[d.real], [d.imag]])
assert abs(r.real - v[0][0]) < 1e-9 and abs(r.imag - v[1][0]) < 1e-9
print("[OK] 주장 8: 복소수로 회전 구현")
print("ALL CHECKS PASSED")
if __name__ == "__main__":
main()