Constexpr determinant (2 dimensional std::array)
我需要编写一个在编译时计算行列式的constexpr函数。最明显的解决方案是使用Laplace扩展。支持C14。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | #include #include <utility> constexpr int get_cofactor_coef(int i, int j) { return (i + j) % 2 == 0 ? 1 : -1; } template <int N> constexpr int determinant(const std::array<std::array<int, N>, N>& a) { int det = 0; for (size_t i = 0u; i < N; ++i) { det += get_cofactor_coef(i, 1) * a[i][0] * determinant<N-1>(GET_SUBMATRIX_OF_A<N-1, I, J>(a); } return det; } template <> constexpr int determinant<2>(const std::array<std::array<int, 2>, 2>& a) { return a[0][0] * a[1][1] - a[0][1] * a[1][0]; } template <> constexpr int determinant<1>(const std::array<std::array<int, 1>, 1>& a) { return a[0][0]; } |
问题是我完全不知道如何编写
我知道我需要:
我的constexpr技能几乎不存在。尝试将
问题在于非
但是,有一个转义符,即
1 | a[i] // constexpr since C++17 |
as
1 | (&std::get<0>(a))[i] // constexpr in C++14!! |
也就是说,我们使用
然后,二级数组成员访问
1 2 3 4 5 6 7 8 9 | template<std::size_t N> constexpr std::array<std::array<int, N - 1>, N - 1> get_submatrix_of_a(const std::array<std::array<int, N>, N>& a, int i, int j) { std::array<std::array<int, N - 1>, N - 1> r{}; for (int ii = 0; ii != N - 1; ++ii) for (int jj = 0; jj != N - 1; ++jj) (&std::get<0>(((&std::get<0>(r))[ii])))[jj] = a[ii + (ii >= i ? 1 : 0)][jj + (jj >= j ? 1 : 0)]; return r; } |
请记住,
这是一个示例实现。这样做的时间可能更短或更优雅,但这是一个起点。实际上,我刚刚意识到您的矩阵是正方形的,因此绝对可以在下面的代码中删除一些模板参数。
正如我在评论中提到的那样,对于C 17及更高版本,很可能根本不需要这些。
首先,让我们定义一些样板,让我们创建并索引一个遗漏一个值的序列(即您要跳过的行):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | #include <utility> // Based on https://stackoverflow.com/a/32223343. template <size_t Offset, class T1, class T2> struct offset_sequence_merger; template <size_t Offset, size_t... I1, size_t... I2> struct offset_sequence_merger<Offset, std::index_sequence<I1...>, std::index_sequence<I2...>> : std::index_sequence<I1..., (Offset + I2)...> { }; template <std::size_t Excluded, std::size_t End> using make_excluded_index_sequence = offset_sequence_merger<Excluded + 1, std::make_index_sequence<Excluded>, std::make_index_sequence<End - Excluded - 1>>; |
现在让我们使用它来提取子矩阵:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | #include template <class T, std::size_t N, std::size_t... Indices> constexpr std::array<T, sizeof...(Indices)> extract_columns ( std::array<T, N> const & source, std::index_sequence<Indices...>) { return { source.at(Indices)... }; } template <class T, std::size_t N> constexpr std::array<T, N - 1> drop_first_column ( std::array<T, N> const & source) { return extract_columns(source, make_excluded_index_sequence<0, N>()); } template <class T, std::size_t Rows, std::size_t Cols, std::size_t... RowIndices> constexpr auto create_sub_matrix ( std::array<std::array<T, Cols>, Rows> const & source, std::index_sequence<RowIndices...>) -> std::array<std::array<T, Cols - 1>, sizeof...(RowIndices)> { return { drop_first_column(source.at(RowIndices))... }; } template <std::size_t ExcludedRow, class T, std::size_t Rows, std::size_t Cols> constexpr auto create_sub_matrix ( std::array<std::array<T, Cols>, Rows> const & source) -> std::array<std::array<T, Cols - 1>, Rows - 1> { return create_sub_matrix(source, make_excluded_index_sequence<ExcludedRow, Rows>()); } |
最后,这是一些代码,显示上面的代码似乎已经完成了应做的工作。您可以在Wandbox上看到它的运行情况:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | #include <iostream> #include <string> template <class T> void print_seq (std::integer_sequence< T > const & /* seq */) { std::cout << '\ '; } template <class T, T Head, T... Tail> void print_seq (std::integer_sequence<T, Head, Tail...> const & /* seq */) { std::cout << Head << ' '; print_seq(std::integer_sequence<T, Tail...>{}); } template <class T, std::size_t N> void print_array (std::array<T, N> const & src) { std::string sep =""; for (auto const & e : src) { std::cout << sep << e; sep =""; } std::cout << '\ '; } template <class T, std::size_t N, std::size_t M> void print_matrix (std::array<std::array<T, N>, M> const & src) { for (auto const & row : src) { print_array(row); } } int main () { auto indexSeqA = make_excluded_index_sequence<0, 3>(); print_seq(indexSeqA); auto indexSeqB = make_excluded_index_sequence<1, 3>(); print_seq(indexSeqB); auto indexSeqC = make_excluded_index_sequence<2, 3>(); print_seq(indexSeqC); std::cout << '\ '; std::array<int, 3> arr = { 1, 7, 9 }; print_array(arr); std::cout << '\ '; std::array<std::array<int, 3>, 3> matrix = {{ { 0, 1, 2 } , { 3, 4, 5 } , { 6, 7, 8 } }}; print_matrix(matrix); std::cout << '\ '; print_matrix(create_sub_matrix<0>(matrix)); std::cout << '\ '; print_matrix(create_sub_matrix<1>(matrix)); std::cout << '\ '; } |
希望这足以帮助您完全实现