직선과 평면의 방정식 문서의 검증 코드다. if __name__ == "__main__": 아래가 자체 테스트다.
"""직선과 평면의 방정식 문서의 주장 검증."""
from fractions import Fraction as F
def cross(a, b):
return (a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0])
def dot(a, b):
return sum(x * y for x, y in zip(a, b))
def sub(a, b):
return tuple(x - y for x, y in zip(a, b))
def solve3(A, b):
"""크라메르 공식. 행렬식이 0이면 None."""
def det(m):
return (m[0][0] * (m[1][1] * m[2][2] - m[1][2] * m[2][1]) - m[0][1] * (m[1][0] * m[2][2] - m[1][2] * m[2][0])
+ m[0][2] * (m[1][0] * m[2][1] - m[1][1] * m[2][0]))
d = det(A)
if d == 0:
return None
out = []
for c in range(3):
M = [list(r) for r in A]
for r in range(3):
M[r][c] = b[r]
out.append(F(det(M), d))
return tuple(out)
def main():
# 문서의 예: x = u + 1, y = 2u − 1
pts = {u: (u + 1, 2 * u - 1) for u in (-1, 0, F(1, 2), 1)}
assert pts[-1] == (0, -3) and pts[0] == (1, -1) and pts[F(1, 2)] == (F(3, 2), 0) and pts[1] == (2, 1)
# 문서의 선분 예: x = −4u + 1, y = 3u + 2, z = 5u − 3
seg = lambda u: (-4 * u + 1, 3 * u + 2, 5 * u - 3)
assert seg(0) == (1, 2, -3) and seg(1) == (-3, 5, 2)
assert seg(F(1, 2)) == (-1, F(7, 2), F(-1, 2)) # 카드 C2
# 두 점을 지나는 직선 x = p0 + (p1 − p0)u
p0, p1 = (1, 2, -3), (-3, 5, 2)
assert all(seg(u) == tuple(a + (b - a) * u for a, b in zip(p0, p1)) for u in (0, F(1, 3), 2))
# 점이 직선 위에 있는지: u_x = u_y = u_z (a의 성분이 0이 아닐 때)
a, b = (-4, 3, 5), (1, 2, -3)
us = lambda p: tuple(F(pi - bi, ai) for pi, ai, bi in zip(p, a, b))
assert len(set(us(seg(F(2, 7))))) == 1 and len(set(us((0, 0, 0)))) > 1
# 성분이 0이면 나눌 수 없다: 방향 (1, 0, 0)
try:
F(1, 0)
raise AssertionError
except ZeroDivisionError:
pass
# 2차원 직선 2x + y − 4 = 0의 양쪽
f = lambda x, y: 2 * x + y - 4
assert f(1, 2) == 0 and f(0, 0) < 0 and f(3, 3) > 0 and f(0, 0) * f(3, 3) < 0
# 세 점을 지나는 평면: a'x + b'y + c'z + 1 = 0
sol = solve3([(2, 0, 0), (0, 3, 0), (0, 0, 6)], (-1, -1, -1))
assert sol == (F(-1, 2), F(-1, 3), F(-1, 6)) # x/2 + y/3 + z/6 = 1
# 원점을 지나는 평면이면 d = 0이라 나눌 수 없다: 세 점 (1,0,0), (0,1,0), (1,1,0)은 z = 0
assert solve3([(1, 0, 0), (0, 1, 0), (1, 1, 0)], (-1, -1, -1)) is None
# 법선: 세 점의 두 차이 벡터의 외적
P, Q, R = (2, 0, 0), (0, 3, 0), (0, 0, 6)
n = cross(sub(Q, P), sub(R, P))
assert n == (18, 12, 6) and dot(n, sub((1, 1, 1), P)) == 18 + 12 + 6 - 36
# 직선과 평면의 교점 u = −(b − x0)·n / (a·n)
a, b, x0, n = (1, 1, 1), (0, 0, 0), (0, 0, 2), (0, 0, 1)
u = F(-dot(sub(b, x0), n), dot(a, n)); assert u == 2
assert tuple(ai * u + bi for ai, bi in zip(a, b)) == (2, 2, 2)
a2 = (1, 1, 0)
assert dot(a2, n) == 0 and dot(sub(b, x0), n) != 0 # 평행, 만나지 않음
assert dot(a2, n) == 0 and dot(sub((5, 5, 2), x0), n) == 0 # 평면 위의 직선
print("ALL CHECKS PASSED")
if __name__ == "__main__":
main()