最後の今回は、転置行列、逆行列、要素型との除算を実装する。また、いくつかのis関数を実装する。
今回のプログラムの実行にはBoostライブラリを必要とする。
// matrix.cpp #include <iostream> #include <initializer_list> #include <stdexcept> #include <boost/rational.hpp> template <typename T, int N> class Matrix { static_assert(N > 0, ""); public: //------------------------------------------ // // 挿入演算子 // //------------------------------------------ friend std::ostream& operator <<(std::ostream& os, const Matrix& x) { os << "["; for (int iRow = 0; iRow < N; iRow++) { os << "["; for (int iColumn = 0; iColumn < N; iColumn++) { os << x.elements_[iRow][iColumn]; if (iColumn != N - 1) os << ","; } os << "]"; if (iRow != N - 1) os << ","; } os << "]"; return os; } public: //------------------------------------------ // // コンストラクタ、デストラクタ、コピー、ムーブ // //------------------------------------------ Matrix() noexcept(noexcept(T())) {} Matrix(const T& a) { for (int i = 0; i < N; i++) elements_[i][i] = a; } Matrix(std::initializer_list<std::initializer_list<T>> ll) { auto itRow = ll.begin(); for (int iRow = 0; iRow < N && itRow != ll.end(); iRow++, ++itRow) { auto l = *itRow; auto itColumn = l.begin(); for (int iColumn = 0; iColumn < N && itColumn != l.end(); iColumn++, ++itColumn) { elements_[iRow][iColumn] = *itColumn; } } } Matrix(const Matrix&) noexcept = default; Matrix(Matrix&&) noexcept = default; ~Matrix() = default; Matrix& operator =(const Matrix&) & noexcept = default; Matrix& operator =(Matrix&&) & noexcept = default; Matrix&& operator =(const Matrix&) && noexcept = delete; Matrix&& operator =(Matrix&&) && noexcept = delete; public: //------------------------------------------ // // 要素の参照 // //------------------------------------------ T& operator ()(int row, int column) { return elements_[row][column]; } const T& operator ()(int row, int column) const { return elements_[row][column]; } public: //------------------------------------------ // // 算術単項演算子 // //------------------------------------------ Matrix operator +() const { return Matrix(*this); } Matrix operator -() const { Matrix ret; for (int iRow = 0; iRow < N; iRow++) { for (int iColumn = 0; iColumn < N; iColumn++) { ret.elements_[iRow][iColumn] = -elements_[iRow][iColumn]; } } return ret; } public: //------------------------------------------ // // 算術代入演算子 // //------------------------------------------ Matrix& operator +=(const Matrix& other) & { for (int iRow = 0; iRow < N; iRow++) { for (int iColumn = 0; iColumn < N; iColumn++) { elements_[iRow][iColumn] += other.elements_[iRow][iColumn]; } } return *this; } Matrix& operator -=(const Matrix& other) & { for (int iRow = 0; iRow < N; iRow++) { for (int iColumn = 0; iColumn < N; iColumn++) { elements_[iRow][iColumn] -= other.elements_[iRow][iColumn]; } } return *this; } Matrix& operator *=(const T& a) & { for (int iRow = 0; iRow < N; iRow++) { for (int iColumn = 0; iColumn < N; iColumn++) { elements_[iRow][iColumn] *= a; } } return *this; } Matrix& operator *=(const Matrix& other) & { Matrix tmp = *this * other; using std::swap; swap(*this, tmp); return *this; } Matrix& operator /=(const T& a) & { for (int iRow = 0; iRow < N; iRow++) { for (int iColumn = 0; iColumn < N; iColumn++) { elements_[iRow][iColumn] /= a; } } return *this; } Matrix&& operator +=(const Matrix& other) && = delete; Matrix&& operator -=(const Matrix& other) && = delete; Matrix&& operator *=(const T& a) && = delete; Matrix&& operator *=(const Matrix& other) && = delete; Matrix&& operator /=(const T& a) && = delete; public: //------------------------------------------ // // 算術二項演算子 // //------------------------------------------ friend Matrix operator +(const Matrix& lhs, const Matrix& rhs) { Matrix ret(lhs); return ret += rhs; } friend Matrix operator -(const Matrix& lhs, const Matrix& rhs) { Matrix ret(lhs); return ret -= rhs; } friend Matrix operator *(const Matrix& lhs, const T& rhs) { Matrix ret(lhs); return ret *= rhs; } friend Matrix operator *(const T& lhs, const Matrix& rhs) { return rhs *= lhs; } friend Matrix operator *(const Matrix& lhs, const Matrix& rhs) { Matrix ret; for (int iRow = 0; iRow < N; iRow++) { for (int iColumn = 0; iColumn < N; iColumn++) { for (int i = 0; i < N; i++) { ret.elements_[iRow][iColumn] += lhs.elements_[iRow][i] * rhs.elements_[i][iColumn]; } } } return ret; } friend Matrix operator /(const Matrix& lhs, const T& rhs) { Matrix ret(lhs); return ret /= rhs; } public: //------------------------------------------ // // その他のメンバ関数 // //------------------------------------------ // iRow行とiCokumn列を除いた成分を行列にして返す。 Matrix<T, N - 1> minor_matrix(int iRow, int iColumn) const { static_assert(N > 1, ""); Matrix<T, N - 1> ret; for (int iRR = 0, i = -1; iRR < N; iRR++) { if (iRR == iRow) continue; i++; for (int iCC = 0, j = -1; iCC < N; iCC++) { if (iCC == iColumn) continue; j++; ret(i, j) = elements_[iRR][iCC]; } } return ret; } // 零行列ならtrue bool is_zero() const { for (int iRow = 0; iRow < N; iRow++) for(int iColumn = 0; iColumn < N; iColumn++) { if (elements_[iRow][iColumn] != 0) return false; } return true; } // 対角行列ならtrue bool is_diag() const { for (int iRow = 0; iRow < N; iRow++) for(int iColumn = 0; iColumn < N; iColumn++) { if (iRow == iColumn) continue; if (elements_[iRow][iColumn] != 0) return false; } return true; } // 零因子ならtrue bool is_zerodevisor() const { return det(*this) == static_cast(0); } public: //------------------------------------------ // // 論理演算子 // //------------------------------------------ friend bool operator ==(const Matrix& lhs, const Matrix& rhs) { for (int iRow = 0; iRow < N; iRow++) { for (int iColumn = 0; iColumn < N; iColumn++) { if (lhs.elements_[iRow][iColumn] != rhs.elements_[iRow][iColumn]) return false; } } return true; } friend bool operator !=(const Matrix& lhs, const Matrix& rhs) { return !(lhs == rhs); } //------------------------------------------ // // iRow行とiCokumn列を除いた成分を行列にして返す。 // //------------------------------------------ Matrix<T, N - 1> minor_matrix(int iRow, int iColumn) const { static_assert(N > 1, ""); Matrix<T, N - 1> ret; for (int iRR = 0, i = -1; iRR < N; iRR++) { if (iRR == iRow) continue; i++; for (int iCC = 0, j = -1; iCC < N; iCC++) { if (iCC == iColumn) continue; j++; ret(i, j) = elements_[iRR][iCC]; } } return ret; } protected: T elements_[N][N] = {}; }; // class Matrix //------------------------------------------ // // (i, j)成分の余因子を求める。 // //------------------------------------------ template <typename T, int N> inline T cofactor(const Matrix<T, N>& m, int iRow, int iColumn) { T sign = (iRow + iColumn) % 2 == 0 ? 1 : -1; return sign * det(m.minor_matrix(iRow, iColumn)); } //------------------------------------------ // // 行列式を求めるときのヘルパー。 // 余因子展開で再帰的(detとcofactorが相互に呼ぶ)に計算する。 // 部分的特殊化により、 N == 1 を特別な動作にしたい。これはそのために必要。 // (関数テンプレートは部分的特殊化ができないが、クラステンプレートならできる) // //------------------------------------------ template <typename T, int N> struct Det { inline static T det(const Matrix<T, N>& matrix) { T ret = 0; for (int iColumn = 0; iColumn < N; iColumn++) { ret += matrix(0, iColumn) * cofactor(matrix, 0, iColumn); } return ret; } }; template<typename T> struct Det<T, 1> { inline static T det(const Matrix<T, 1>& matrix) { return matrix(0, 0); } }; //------------------------------------------ // // 行列式を求める // //------------------------------------------ template <typename T, int N> inline T det(const Matrix<T, N>& matrix) { return Det<T, N>::det(matrix); } //------------------------------------------ // // 余因子行列を求める // //------------------------------------------ template <typename T, int N> inline Matrix<T, N> cofactor_matrix(const Matrix<T, N>& m) { Matrix<T, N> ret; for (int iRow = 0; iRow < N; iRow++) { for(int iColumn = 0; iColumn < N; iColumn++) { //ここで行と列を入れ替えているため、転置したことになっている。 ret(iRow, iColumn) = cofactor(m, iColumn, iRow); } } return ret; } //------------------------------------------ // // 転置行列を求める // //------------------------------------------ template <typename T, int N> inline Matrix<T, N> t(const Matrix<T, N>& m) { Matrix<T, N> ret; for (int iRow = 0; iRow < N; iRow++) { for(int iColumn = 0; iColumn < N; iColumn++) { ret(iRow, iColumn) = m(iColumn, iRow); } } return ret; } //------------------------------------------ // // 逆行列が存在しない場合の例外 // //------------------------------------------ class ZeroDivisorException : public std::exception { public: const char* what() const noexcept { return "Zero divisor cannot create inverse matrix."; } }; //------------------------------------------ // // 逆行列を求める // //------------------------------------------ template <typename T, int N> inline Matrix<T, N> inverse(const Matrix<T, N>& m) { if (det(m) == 0) { throw ZeroDivisorException(); } T d = det(m); Matrix<T, N> co_mat = cofactor_matrix(m); return co_mat / d; } int main() { Matrix<int, 3> x{{1,2,3},{4,5,6},{7,8,9}}; try { Matrix<int, 3> z = inverse(x); } catch (ZeroDivisorException& e) { std::cout << e.what() << std::endl; } std::cout << x / 9 << std::endl << std::endl; using boost::rational; using M = Matrix<rational<int>, 3>; M a{{1,0,4},{3,-1,0},{-2,1,-1}}; try { M b = inverse(a); std::cout << b << std::endl; std::cout << a * b << std::endl; } catch (ZeroDivisorException& e) { std::cout << e.what() << std::endl; } return 0; }
実行結果は
Zero divisor cannot create inverse matrix. //零因子の逆行列は存在しない。
[[0,0,0],[0,0,0],[0,0,1]] //要素型がintの場合、intとintの除算が反映される。商が整数になり余りが切り捨てられる実装のため注意が必要。
[[1/5,4/5,4/5],[3/5,7/5,12/5],[1/5,-1/5,-1/5]] //要素型を分数にすることもできる。
[[1/1,0/1,0/1],[0/1,1/1,0/1],[0/1,0/1,1/1]]
のようになる。
気が向いたら、constexprや、pImplで実装をしてみたい。
0 件のコメント:
コメントを投稿