벡터 간 각도 정의

두 벡터 간의 각도를 계산하는 것은 벡터 연산에서 매우 중요한 개념이다. 두 벡터 \mathbf{v}\mathbf{w} 간의 각도 \theta는 다음과 같이 정의된다. 우선 벡터 간 각도를 구하기 위해서는 두 벡터 간의 내적(inner product)을 이용할 수 있다.

벡터 \mathbf{v}\mathbf{w}의 내적은 다음과 같이 정의된다.

\mathbf{v} \cdot \mathbf{w} = \|\mathbf{v}\| \|\mathbf{w}\| \cos{\theta}

위 식에서 \|\mathbf{v}\|\|\mathbf{w}\|는 각각 벡터 \mathbf{v}\mathbf{w}의 크기이며, \theta는 두 벡터 사이의 각도이다.

이를 통해 \theta는 다음과 같이 구할 수 있다.

\theta = \cos^{-1}\left( \frac{\mathbf{v} \cdot \mathbf{w}}{\|\mathbf{v}\| \|\mathbf{w}\|} \right)

따라서 두 벡터의 크기와 내적을 통해 각도를 구할 수 있다.

벡터의 크기

벡터 \mathbf{v}의 크기는 다음과 같이 정의된다.

\|\mathbf{v}\| = \sqrt{\mathbf{v}_1^2 + \mathbf{v}_2^2 + \cdots + \mathbf{v}_n^2}

여기서 \mathbf{v}_i는 벡터 \mathbf{v}의 각 성분이다.

마찬가지로, 벡터 \mathbf{w}의 크기 역시

\|\mathbf{w}\| = \sqrt{\mathbf{w}_1^2 + \mathbf{w}_2^2 + \cdots + \mathbf{w}_n^2}

로 정의된다.

벡터의 내적 계산

두 벡터 \mathbf{v}\mathbf{w}의 내적은 다음과 같다.

\mathbf{v} \cdot \mathbf{w} = \mathbf{v}_1 \mathbf{w}_1 + \mathbf{v}_2 \mathbf{w}_2 + \cdots + \mathbf{v}_n \mathbf{w}_n

따라서, 두 벡터의 성분을 곱한 후 이를 모두 더하면 내적을 구할 수 있다.

Eigen 라이브러리를 이용한 벡터 각도 계산

Eigen 라이브러리를 사용하여 두 벡터 간의 각도를 계산하는 방법은 다음과 같다. 우선, Eigen 라이브러리에서는 벡터 연산을 매우 간단하게 처리할 수 있다. 두 벡터 간 내적과 크기를 구한 후 각도를 계산할 수 있다.

#include <iostream>
#include <Eigen/Dense>
#include <cmath>

int main() {
    // 두 벡터 정의
    Eigen::Vector3d v(1.0, 2.0, 3.0);
    Eigen::Vector3d w(4.0, 5.0, 6.0);

    // 벡터의 크기 계산
    double v_norm = v.norm();
    double w_norm = w.norm();

    // 벡터 내적 계산
    double dot_product = v.dot(w);

    // 각도 계산 (라디안 값으로 반환)
    double angle = std::acos(dot_product / (v_norm * w_norm));

    // 결과 출력
    std::cout << "두 벡터 간의 각도 (라디안): " << angle << std::endl;

    return 0;
}

위 코드에서는 다음과 같은 과정을 따른다.

  1. Eigen::Vector3d를 사용하여 두 벡터 \mathbf{v} = (1, 2, 3)\mathbf{w} = (4, 5, 6)을 정의한다.
  2. 벡터의 크기는 v.norm()w.norm()으로 구할 수 있다. 이 함수는 벡터의 크기를 자동으로 계산해준다.
  3. 벡터의 내적은 v.dot(w)를 사용하여 구할 수 있다.
  4. std::acos 함수는 내적과 크기를 통해 각도를 계산하며, 반환되는 각도는 라디안 값이다.

라디안과 도(degree) 변환

위의 코드에서 계산된 각도는 라디안 값이다. 그러나 많은 경우, 각도를 도(degree)로 표현하는 것이 더 직관적일 수 있다. 라디안과 도 간의 변환은 다음과 같이 이루어진다:

\theta_{\text{degree}} = \theta_{\text{radian}} \times \left(\frac{180}{\pi}\right)

이를 코드에 반영하면, 라디안 값을 도 값으로 변환할 수 있다.

#include <iostream>
#include <Eigen/Dense>
#include <cmath>

int main() {
    // 두 벡터 정의
    Eigen::Vector3d v(1.0, 2.0, 3.0);
    Eigen::Vector3d w(4.0, 5.0, 6.0);

    // 벡터의 크기 계산
    double v_norm = v.norm();
    double w_norm = w.norm();

    // 벡터 내적 계산
    double dot_product = v.dot(w);

    // 각도 계산 (라디안 값으로 반환)
    double angle_radian = std::acos(dot_product / (v_norm * w_norm));

    // 라디안 값을 도 값으로 변환
    double angle_degree = angle_radian * (180.0 / M_PI);

    // 결과 출력
    std::cout << "두 벡터 간의 각도 (라디안): " << angle_radian << std::endl;
    std::cout << "두 벡터 간의 각도 (도): " << angle_degree << std::endl;

    return 0;
}

코드 설명

  1. 벡터 크기 계산 (v.norm()w.norm()): Eigen 라이브러리에서 벡터의 크기를 계산하는 함수로, 벡터 \mathbf{v}\mathbf{w}의 크기를 구한다. 이는 n차원 벡터의 크기를 유클리드 거리로 계산하는 함수로, \|\mathbf{v}\|\|\mathbf{w}\| 값을 반환한다.

  2. 벡터 내적 계산 (v.dot(w)): Eigen의 dot 함수는 두 벡터의 내적을 계산한다. 내적은 각 벡터의 대응되는 성분의 곱의 합으로 정의되며, 이를 통해 두 벡터 간의 관계를 파악할 수 있다.

  3. 각도 계산 (std::acos): 두 벡터의 내적과 크기를 이용해 코사인 법칙을 적용하여 두 벡터 간의 각도를 구한다. 반환되는 값은 라디안 값이다.

  4. 라디안과 도 변환: angle_radian에 저장된 값을 이용해 도 값으로 변환하는데, 이는 \frac{180}{\pi}를 곱하는 것으로 쉽게 변환할 수 있다.

2차원 벡터 예시

위의 예제는 3차원 벡터를 사용한 경우이지만, 2차원 벡터의 경우에도 동일한 방식으로 처리할 수 있다. 예를 들어, 다음과 같은 2차원 벡터에 대해 벡터 간 각도를 계산할 수 있다.

#include <iostream>
#include <Eigen/Dense>
#include <cmath>

int main() {
    // 두 2차원 벡터 정의
    Eigen::Vector2d v(1.0, 2.0);
    Eigen::Vector2d w(3.0, 4.0);

    // 벡터의 크기 계산
    double v_norm = v.norm();
    double w_norm = w.norm();

    // 벡터 내적 계산
    double dot_product = v.dot(w);

    // 각도 계산 (라디안 값으로 반환)
    double angle_radian = std::acos(dot_product / (v_norm * w_norm));

    // 라디안 값을 도 값으로 변환
    double angle_degree = angle_radian * (180.0 / M_PI);

    // 결과 출력
    std::cout << "두 벡터 간의 각도 (라디안): " << angle_radian << std::endl;
    std::cout << "두 벡터 간의 각도 (도): " << angle_degree << std::endl;

    return 0;
}

일반화된 차원에서의 계산

Eigen 라이브러리는 차원에 관계없이 내적과 벡터 크기 계산을 지원한다. 이는 2차원, 3차원뿐만 아니라 n차원의 벡터에서도 동일하게 작동한다. Eigen의 벡터 연산 함수는 템플릿 기반이므로, 임의의 차원에 대해 유연하게 처리할 수 있다.

4차원 이상의 벡터에서 각도 계산

4차원 이상의 고차원 벡터에서도 위와 같은 방법으로 각도를 계산할 수 있다. 예를 들어, 4차원 벡터 \mathbf{v} = (1, 2, 3, 4)\mathbf{w} = (5, 6, 7, 8) 사이의 각도는 아래와 같이 계산할 수 있다.

#include <iostream>
#include <Eigen/Dense>
#include <cmath>

int main() {
    // 두 4차원 벡터 정의
    Eigen::Vector4d v(1.0, 2.0, 3.0, 4.0);
    Eigen::Vector4d w(5.0, 6.0, 7.0, 8.0);

    // 벡터의 크기 계산
    double v_norm = v.norm();
    double w_norm = w.norm();

    // 벡터 내적 계산
    double dot_product = v.dot(w);

    // 각도 계산 (라디안 값으로 반환)
    double angle_radian = std::acos(dot_product / (v_norm * w_norm));

    // 라디안 값을 도 값으로 변환
    double angle_degree = angle_radian * (180.0 / M_PI);

    // 결과 출력
    std::cout << "두 벡터 간의 각도 (라디안): " << angle_radian << std::endl;
    std::cout << "두 벡터 간의 각도 (도): " << angle_degree << std::endl;

    return 0;
}

내적이 0일 때의 특수한 경우

내적의 결과가 0인 경우를 살펴보면, 이는 두 벡터가 서로 수직(perpendicular)임을 의미한다. 수학적으로, 벡터 \mathbf{v}\mathbf{w}의 내적이 0이면 다음과 같이 해석할 수 있다.

\mathbf{v} \cdot \mathbf{w} = 0 \implies \cos{\theta} = 0

위 식에서 \cos{\theta} = 0이므로, \theta90^\circ 또는 \frac{\pi}{2} 라디안이 된다. 이는 두 벡터가 직교(orthogonal)한다는 것을 나타낸다.

코드에서 내적이 0일 때 벡터가 직교함을 확인할 수 있다. 예를 들어, 다음과 같은 코드를 통해 내적이 0인 두 벡터의 각도를 확인할 수 있다.

#include <iostream>
#include <Eigen/Dense>
#include <cmath>

int main() {
    // 직교하는 두 벡터 정의
    Eigen::Vector3d v(1.0, 0.0, 0.0); // x축 방향 벡터
    Eigen::Vector3d w(0.0, 1.0, 0.0); // y축 방향 벡터

    // 벡터의 크기 계산
    double v_norm = v.norm();
    double w_norm = w.norm();

    // 벡터 내적 계산
    double dot_product = v.dot(w);

    // 각도 계산 (라디안 값으로 반환)
    double angle_radian = std::acos(dot_product / (v_norm * w_norm));

    // 라디안 값을 도 값으로 변환
    double angle_degree = angle_radian * (180.0 / M_PI);

    // 결과 출력
    std::cout << "두 벡터 간의 각도 (라디안): " << angle_radian << std::endl;
    std::cout << "두 벡터 간의 각도 (도): " << angle_degree << std::endl;

    return 0;
}

위 코드는 x축과 y축 방향의 두 벡터가 서로 직교함을 보여준다. 내적이 0이기 때문에 두 벡터의 각도는 90^\circ이다.

벡터의 정규화 (Normalization)

때로는 벡터의 크기를 1로 맞추어 단위 벡터로 만드는 과정이 필요하다. 이를 벡터 정규화(normalization)라고 한다. 정규화된 벡터는 크기가 1인 벡터이며, 모든 방향 정보를 유지한다. 이를 위해서는 벡터를 그 벡터의 크기로 나누면 된다.

정규화된 벡터 \mathbf{\hat{v}}는 다음과 같이 계산된다.

\mathbf{\hat{v}} = \frac{\mathbf{v}}{\|\mathbf{v}\|}

정규화된 벡터는 길이가 1이므로, 벡터의 방향을 보존하면서 내적 계산이나 다른 연산에서 더 간단하게 다룰 수 있다.

Eigen 라이브러리에서는 normalized() 함수를 사용하여 쉽게 벡터를 정규화할 수 있다. 예제 코드를 통해 이를 확인해보자.

#include <iostream>
#include <Eigen/Dense>

int main() {
    // 벡터 정의
    Eigen::Vector3d v(3.0, 4.0, 0.0);

    // 벡터의 정규화
    Eigen::Vector3d v_normalized = v.normalized();

    // 결과 출력
    std::cout << "정규화된 벡터: " << v_normalized.transpose() << std::endl;

    return 0;
}

위 코드에서는 벡터 \mathbf{v} = (3, 4, 0)을 정규화한 결과를 확인할 수 있다. 결과적으로 이 벡터의 크기는 1이 되며, 정규화된 벡터는 \mathbf{\hat{v}} = (0.6, 0.8, 0.0)이다.

특수한 경우: 영벡터

벡터의 내적을 이용하여 각도를 계산할 때, 영벡터(모든 성분이 0인 벡터)와 다른 벡터의 각도는 정의되지 않는다. 영벡터의 크기는 0이기 때문에, 다음과 같은 문제를 일으킨다.

\frac{\mathbf{v} \cdot \mathbf{w}}{\|\mathbf{v}\| \|\mathbf{w}\|}

이때 \|\mathbf{v}\| 또는 \|\mathbf{w}\|가 0이면 분모가 0이 되어 계산이 불가능하다. 따라서, 영벡터와 다른 벡터의 각도는 의미가 없으며, 프로그램을 작성할 때 이러한 특수한 경우를 처리해야 한다.

다음은 영벡터에 대한 예외 처리를 포함한 코드이다.

#include <iostream>
#include <Eigen/Dense>
#include <cmath>

int main() {
    // 영벡터와 일반 벡터 정의
    Eigen::Vector3d v(0.0, 0.0, 0.0); // 영벡터
    Eigen::Vector3d w(1.0, 2.0, 3.0); // 일반 벡터

    // 벡터의 크기 계산
    double v_norm = v.norm();
    double w_norm = w.norm();

    // 영벡터인 경우 예외 처리
    if (v_norm == 0 || w_norm == 0) {
        std::cout << "영벡터와의 각도는 정의되지 않는다." << std::endl;
    } else {
        // 벡터 내적 계산
        double dot_product = v.dot(w);

        // 각도 계산 (라디안 값으로 반환)
        double angle_radian = std::acos(dot_product / (v_norm * w_norm));

        // 라디안 값을 도 값으로 변환
        double angle_degree = angle_radian * (180.0 / M_PI);

        // 결과 출력
        std::cout << "두 벡터 간의 각도 (라디안): " << angle_radian << std::endl;
        std::cout << "두 벡터 간의 각도 (도): " << angle_degree << std::endl;
    }

    return 0;
}

위 코드는 영벡터와의 각도를 계산할 때 예외 처리를 포함하여 "영벡터와의 각도는 정의되지 않는다."라는 메시지를 출력한다.

코드 최적화

각도 계산 코드에서 주의할 점은, 두 벡터의 크기가 0에 가까운 경우(즉, 매우 작은 벡터)의 경우에도 정확하게 각도를 계산해야 한다는 것이다. 이때 분모가 0에 매우 가까워지면 부동소수점 오류가 발생할 수 있다. 이를 방지하기 위해 분모의 값이 너무 작으면(예: 임계값 \epsilon 이하인 경우) 별도의 처리 방안을 마련하는 것이 필요하다.

다음은 작은 벡터 크기를 처리하는 예제 코드이다.

#include <iostream>
#include <Eigen/Dense>
#include <cmath>

int main() {
    // 두 벡터 정의
    Eigen::Vector3d v(1e-10, 2e-10, 3e-10);
    Eigen::Vector3d w(4.0, 5.0, 6.0);

    // 벡터의 크기 계산
    double v_norm = v.norm();
    double w_norm = w.norm();
    double epsilon = 1e-8; // 작은 값을 처리하기 위한 임계값

    // 매우 작은 벡터에 대한 처리
    if (v_norm < epsilon || w_norm < epsilon) {
        std::cout << "벡터의 크기가 너무 작아 각도를 계산할 수 없다." << std::endl;
    } else {
        // 벡터 내적 계산
        double dot_product = v.dot(w);

        // 각도 계산 (라디안 값으로 반환)
        double angle_radian = std::acos(dot_product / (v_norm * w_norm));

        // 라디안 값을 도 값으로 변환
        double angle_degree = angle_radian * (180.0 / M_PI);

        // 결과 출력
        std::cout << "두 벡터 간의 각도 (라디안): " << angle_radian << std::endl;
        std::cout << "두 벡터 간의 각도 (도): " << angle_degree << std::endl;
    }

    return 0;
}

이 코드는 매우 작은 벡터의 크기를 처리하여 계산 오류를 방지한다.