Skip to content

Latest commit

 

History

History
243 lines (179 loc) · 6.9 KB

linear-algebra.md

File metadata and controls

243 lines (179 loc) · 6.9 KB

線形代数

線形代数には、Boost uBLAS Libraryを使用する。

インデックス

ベクトルには、boost::numeric::ublas名前空間のvectorクラステンプレートを使用する。

#include <iostream>
#include <boost/numeric/ublas/vector.hpp>
#include <boost/numeric/ublas/io.hpp>

namespace ublas = boost::numeric::ublas;

int main()
{
    // double型を要素とする3次元ベクトル
    ublas::vector<double> v(3);

    // 各要素の参照と代入
    v[0] = 3.0; // x
    v[1] = 0.0; // y
    v[2] = 4.0; // z

    // 出力ストリーム
    std::cout << v << std::endl;

    ublas::vector<double> u(3);

    // ベクトルの演算
    ublas::vector<double> v1 = v + u;
    ublas::vector<double> v2 = v - u;
    ublas::vector<double> v3 = v * 2.0;
    ublas::vector<double> v4 = v / 3.0;

    // ベクトルの複合演算
    v += u;
    v -= u;
    v *= 2.0;
    v /= 3.0;
}

出力:

[3](3,0,4)

vectorクラスは、テンプレート引数で要素の型をとり、コンストラクタでは次元数を引数にとる。

各要素は、operator[]()で参照できる。

ベクトルの演算は、四則演算全てをサポートしており、各演算は以下の意味を持つ:

演算 説明
v + u (v[0] + u[0], v[1] + u[1], ..., v[i] + u[i])
v - u (v[0] - u[0], v[1] - u[1], ..., v[i] - u[i])
v * n (v[0] * n, v[1] * n, ..., v[i] * n)
v / n (v[0] / n, v[1] / n, ..., v[i] / n)

ベクトルの長さを取得するには、boost::numeric::ublas::norm_2()関数を使用する。この関数は、ユークリッドノルム(2-ノルム)を計算して返す。

#include <iostream>
#include <boost/numeric/ublas/vector.hpp>

namespace ublas = boost::numeric::ublas;

int main()
{
    ublas::vector<double> v(3);

    v[0] = 2.0;
    v[1] = 0.0;
    v[2] = 1.0;

    // ベクトルの長さを取得する
    const double length = ublas::norm_2(v);
    std::cout << length << std::endl;
}
  • norm_2[color ff0000]

出力:

2.23607

ベクトルの正規化とは、ベクトルの長さを1にする変換のことである。Boost.uBLASでは、正規化のための関数が直接的には用意されていないため、自分で計算する必要がある。

正規化の計算は、以下のnormalize()関数のように、ベクトルを長さで割ることでできる。

#include <iostream>
#include <boost/numeric/ublas/vector.hpp>
#include <boost/numeric/ublas/io.hpp>

namespace ublas = boost::numeric::ublas;

ublas::vector<double> normalize(const ublas::vector<double>& v)
{
    return v / ublas::norm_2(v);
}

int main()
{
    ublas::vector<double> v(3);

    v[0] = 2.0;
    v[1] = 0.0;
    v[2] = 1.0;

    // 正規化
    ublas::vector<double> normalized_vec = normalize(v);

    std::cout << "length : " << ublas::norm_2(normalized_vec) << std::endl;
    std::cout << normalized_vec << std::endl;
}
  • v / ublas::norm_2(v)[color ff0000]

出力:

length : 1
[3](0.894427,0,0.447214)

値の比率をそのままに、長さが1になっていることがわかる。

2つのベクトルの内積を求めるには、boost::ublas::inner_prod()関数を使用する。この関数は、2つのベクトルを引数にとり、ベクトルの要素型で内積値を返す。

#include <iostream>
#include <boost/numeric/ublas/vector.hpp>

namespace ublas = boost::numeric::ublas;

int main()
{
    ublas::vector<double> v(2);
    v[0] = -1;
    v[1] = 1;

    ublas::vector<double> u(2);
    u[0] = 1;
    u[1] = -2;

    const double result = ublas::inner_prod(v, u);
    std::cout << result << std::endl;
}
  • inner_prod[color ff0000]

出力:

-3

内積は、2つのベクトルが同じ方向を向いていれば正の値、逆方向を向いていれば負の値を返すという特徴がある。

これを利用して、「負の値だったら位置ベクトルが目的地に到達した(衝突した)」と判定するのに応用できる。

2つのベクトルが成す角度の計算を、以下のangle()関数の実装で示す。この関数は、2つのベクトルを与えると、ラジアンで角度が返される。

#include <iostream>
#include <cmath>
#include <boost/numeric/ublas/vector.hpp>
#include <boost/algorithm/clamp.hpp>
#include <boost/geometry/util/math.hpp>
#include <boost/math/constants/constants.hpp>

namespace ublas = boost::numeric::ublas;

template <class T>
T angle(const ublas::vector<T>& v, const ublas::vector<T>& u)
{
    const T length = ublas::norm_2(v) * ublas::norm_2(u);
    if (boost::geometry::math::equals(length, static_cast<T>(0.0))) {
        return boost::math::constants::half_pi<T>();
    }

    const T x = ublas::inner_prod(v, u) / length;
    const T rounded = boost::algorithm::clamp(x, static_cast<T>(-1.0), static_cast<T>(1.0));
    return std::acos(rounded);
}

template <class T>
T radian_to_degree(const T& x)
{
    return x * static_cast<T>(180.0) / boost::math::constants::pi<T>();
}

int main()
{
    ublas::vector<double> v(2);
    v[0] = 1;
    v[1] = 0;

    ublas::vector<double> u(2);
    u[0] = 1;
    u[1] = 1;

    const double result = angle(v, u); // 角度がラジアンで返される
    std::cout << result << std::endl;

    const double deg = radian_to_degree(result); // 度に変換してみる
    std::cout << deg << std::endl;
}
  • angle(const ublas::vector& v, const ublas::vector& u)[color ff0000]
  • angle(v, u); // 角度がラジアンで返される[color ff0000]

出力:

0.785398
45

計算の詳細は、Wikipediaを参照:

documented boost version is 1.53.0