NumPy를 이용한 벡터의 합, 크기, 내적과 외적
| [ | \boldsymbol a | _2](#boldsymbol-a_2) |
| [ | \boldsymbol a\times\boldsymbol b | ](#boldsymbol-atimesboldsymbol-b-1) |
이번 강의가 끝나면 다음을 할 수 있어야 한다.
벡터(vector)는 크기와 방향을 함께 나타내는 물리량이다. 3차원 벡터 $\boldsymbol a$는 다음과 같이 세 성분으로 나타낼 수 있다.
\[[\boldsymbol a]= \begin{bmatrix} a_x\\a_y\\a_z \end{bmatrix}\]Python에서는 1차원 NumPy 배열로 저장한다.
import numpy as np
a = np.array([3.0, 4.0, 5.0])
print(a)
print(a.shape)
print(a.ndim)
벡터 성분은 앞선 강의에서 배운 인덱싱으로 선택한다.
print(a[0])
print(a[1])
print(a[2])
두 벡터의 합과 차는 같은 위치의 성분끼리 계산한다.
\[[\boldsymbol a]+[\boldsymbol b] = \begin{bmatrix} a_x+b_x\\ a_y+b_y\\ a_z+b_z \end{bmatrix}\]a = np.array([3.0, 4.0, 5.0])
b = np.array([3.0, -5.0, -2.0])
vector_sum = a + b
vector_difference = a - b
print(vector_sum)
print(vector_difference)
결과는 각각 $[6,-1,3]$과 $[0,9,7]$이다. 두 배열의 shape이 같아야 성분별로 자연스럽게 더하고 뺄 수 있다.
벡터에 하나의 수인 스칼라 $c$를 곱하면 모든 성분에 $c$가 곱해진다.
\[c[\boldsymbol a] = \begin{bmatrix} ca_x\\ca_y\\ca_z \end{bmatrix}\]a = np.array([1.0, 2.0, 3.0])
scaled = 0.5 * a
print(scaled)
이는 앞선 NumPy 강의에서 배운 배열과 스칼라의 브로드캐스팅이다. 양의 값을 곱하면 방향은 같고 크기가 변한다. 음의 값을 곱하면 방향도 반대가 된다.
벡터 $\boldsymbol a$의 크기(Euclidean norm)는
\[\|\boldsymbol a\|_2 = \sqrt{a_x^2+a_y^2+a_z^2}\]이다.
줄여서 다음과 같이 표기하기도 한다.
\[|\boldsymbol a|\]벡터 $(3,4,0)$의 크기를 직접 계산하면
\[\sqrt{3^2+4^2+0^2}=5\]이다.
NumPy의 원소별 연산과 집계 연산을 이용할 수 있다.
a = np.array([3.0, 4.0, 0.0])
magnitude = np.sqrt((a**2).sum())
print(magnitude)
NumPy가 제공하는 np.linalg.norm()을 사용하면 더 간단하다.
magnitude = np.linalg.norm(a)
print(magnitude)
크기가 1인 벡터를 단위벡터(unit vector)라고 한다. 0이 아닌 벡터를 그 크기로 나누면 같은 방향의 단위벡터를 얻는다.
\[\widehat{\boldsymbol a} = \frac{\boldsymbol a}{\|\boldsymbol a\|_2}\]def unit_vector(vector):
magnitude = np.linalg.norm(vector)
if magnitude == 0:
raise ValueError("zero vector has no direction")
return vector / magnitude
a = np.array([3.0, 4.0, 0.0])
a_unit = unit_vector(a)
print(a_unit)
print(np.linalg.norm(a_unit))
영벡터는 방향이 없으므로 단위벡터로 만들 수 없다.
두 벡터의 내적(dot product)은 같은 위치의 성분을 곱한 뒤 모두 더한 값이다.
\[\boldsymbol a\mathbin{\cdot}\boldsymbol b = \sum_{i=1}^{n}a_i b_i\]3차원에서는
\[\boldsymbol a\mathbin{\cdot}\boldsymbol b =a_1b_1+a_2b_2+a_3b_3\]이다.
a = np.array([1.0, 2.0, 3.0])
b = np.array([4.0, 5.0, 6.0])
dot_product = (a * b).sum()
print(dot_product)
NumPy에서는 다음 두 표현을 사용할 수 있다.
print(np.dot(a, b))
print(a @ b)
결과는 모두 스칼라 32이다. a * b만 계산하면 합을 하지 않으므로 내적이 아니라 원소별 곱 배열이 나온다.
두 벡터가 모두 영벡터가 아니고 내적이 0이면 두 벡터는 서로 수직이다.
x_direction = np.array([1.0, 0.0, 0.0])
y_direction = np.array([0.0, 1.0, 0.0])
print(x_direction @ y_direction)
내적은 두 벡터 사이의 각 $\theta$와 다음 관계를 갖는다.
\[\boldsymbol a\mathbin{\cdot}\boldsymbol b = |\boldsymbol a| |\boldsymbol b| \cos\theta\]따라서
\[\theta = \cos^{-1} \left( \frac{\boldsymbol a\mathbin{\cdot}\boldsymbol b} {|\boldsymbol a||\boldsymbol b|} \right)\]이다.
def angle_between(vector_a, vector_b):
magnitude_a = np.linalg.norm(vector_a)
magnitude_b = np.linalg.norm(vector_b)
if magnitude_a == 0 or magnitude_b == 0:
raise ValueError("zero vector has no direction")
cosine = (
vector_a @ vector_b
/ (magnitude_a * magnitude_b)
)
cosine = np.clip(cosine, -1.0, 1.0)
angle_rad = np.arccos(cosine)
return np.degrees(angle_rad)
a = np.array([1.0, 0.0, 0.0])
b = np.array([0.0, 1.0, 0.0])
print(angle_between(a, b))
결과는 90도이다. np.arccos()의 결과는 radian이고, np.degrees()가 degree로 변환한다. 부동소수점 계산 때문에 cosine이 아주 조금 1보다 커지거나 -1보다 작아지는 것을 막기 위해 np.clip()을 사용했다.
3차원 벡터의 외적(cross product)은 두 벡터에 모두 수직인 새로운 벡터이다.
\[[\boldsymbol a]\times[\boldsymbol b] = \begin{bmatrix} a_yb_z-a_zb_y\\ a_zb_x-a_xb_z\\ a_xb_y-a_yb_x \end{bmatrix}\]a = np.array([1.0, 0.0, 0.0])
b = np.array([0.0, 1.0, 0.0])
cross_product = np.cross(a, b)
print(cross_product)
결과는 $[0,0,1]$이다. 방향은 오른손 법칙을 따른다.
외적은 순서를 바꾸면 부호가 바뀐다.
\[\boldsymbol b\times\boldsymbol a = -\boldsymbol a\times\boldsymbol b\]print(np.cross(b, a))
외적의 크기는 두 벡터가 만드는 평행사변형의 넓이이다.
\[|\boldsymbol a\times\boldsymbol b| = |\boldsymbol a| |\boldsymbol b| \sin\theta\]한 점에 두 힘이 작용한다고 하자.
force_1 = np.array([100.0, 0.0, 0.0])
force_2 = np.array([0.0, 50.0, 0.0])
resultant = force_1 + force_2
resultant_magnitude = np.linalg.norm(resultant)
print(resultant)
print(resultant_magnitude)
합력은 $[100,50,0]$ N이고 크기는 약 111.8 N이다.
입방정(cubic crystal)에서는 결정방향 $[uvw]$를 성분이 $(u,v,w)$인 벡터로 다룰 수 있다. $[100]$과 $[110]$ 사이의 각을 계산해 보자.
direction_1 = np.array([1.0, 0.0, 0.0])
direction_2 = np.array([1.0, 1.0, 0.0])
angle_deg = angle_between(
direction_1,
direction_2,
)
print(angle_deg)
결과는 45도이다. 비입방정(non-cubic)에서는 격자상수를 고려해야 하므로 같은 방법을 그대로 적용할 수 없다.
두 격자벡터가 만드는 평행사변형의 넓이는 외적의 크기로 구할 수 있다.
lattice_a = np.array([2.0, 0.0, 0.0])
lattice_b = np.array([0.0, 3.0, 0.0])
area = np.linalg.norm(
np.cross(lattice_a, lattice_b)
)
print(area)
넓이는 6이다. 격자벡터의 단위가 nm라면 넓이 단위는 nm²이다.
a * b를 내적이라고 생각한다.ndim)과 물리학적 벡터 차원을 혼동한다.np.linalg.norm()은 벡터의 크기를 계산한다.두 벡터 $\boldsymbol a=(1,2,3)$과 $\boldsymbol b=(2,0,1)$의 합을 구하라.
벡터 $(3,4,0)$의 크기를 구하라.
벡터 a의 크기를 NumPy로 계산하는 표현을 쓰시오.
$\boldsymbol a=(1,0,0)$과 $\boldsymbol b=(0,2,0)$의 내적을 구하라.
다음 코드에서 a * b와 a @ b의 결과를 각각 구하라.
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
$\boldsymbol a=(1,0,0)$과 $\boldsymbol b=(0,1,0)$의 외적을 구하라.
np.arccos()가 반환한 각도의 기본 단위는 무엇인가?