![](/img/trans.png)
[英]Initialize a constexpr std::array with the size of an n-dimensional std::array
[英]Constexpr determinant (2 dimensional std::array)
我需要编写一个在编译时计算行列式的 constexpr 函数。 最明显的解决方案是使用拉普拉斯展开式。 支持 C++14。
#include <array>
#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];
}
问题是我完全不知道如何编写GET_SUBMATRIX_OF_A
。
我知道我需要:
std::integer_sequence
); 我的 constexpr 技能几乎不存在。 尝试将a
传递给另一个函数会导致奇怪的错误,例如 error: '* & a' is not a constant expression。
非常感谢所有帮助!
问题是非const
std::array<T, N>::operator[]
(返回T&
)直到 C++17 才不是 constexpr,这使得设置次要元素变得困难。
但是,有一个逃生舱口,即std::get<I>(std::array&)
是constexpr,对结果执行指针运算是完全合法的,因此我们可以重写
a[i] // constexpr since C++17
作为
(&std::get<0>(a))[i] // constexpr in C++14!!
也就是说,我们使用std::get
获取对数组第一个成员的 constexpr 引用,获取指向它的指针,并在指针和索引上使用内置[]
运算符。
然后一个两级数组成员访问a[i][j]
变得非常丑陋但仍然是 constexpr (&std::get<0>((&std::get<0>(a))[i]))[j]
,这意味着我们可以将get_submatrix_of_a
写成一个普通的constexpr
函数:
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;
}
记住const
std::array<T, N>::operator[]
在 C++14 中已经是constexpr
,所以我们不需要重写次要构造的 RHS。
这是一个示例实现。 这样做可能更短或更优雅,但这是一个起点。 实际上,我刚刚意识到您的矩阵是方形的,因此绝对可以在下面的代码中删除一些模板参数。
正如我在评论中提到的,对于 C++17 及更高版本,很可能根本不需要这些。
首先,让我们定义一些样板,让我们创建和索引序列,并保留一个值(即您要跳过的行):
#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>>;
现在让我们用它来提取子矩阵:
#include <array>
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看到它的实际效果:
#include <iostream>
#include <string>
template <class T>
void print_seq (std::integer_sequence<T> const & /* seq */) {
std::cout << '\n';
}
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 << '\n';
}
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 << '\n';
std::array<int, 3> arr = { 1, 7, 9 };
print_array(arr); std::cout << '\n';
std::array<std::array<int, 3>, 3> matrix = {{
{ 0, 1, 2 }
, { 3, 4, 5 }
, { 6, 7, 8 }
}};
print_matrix(matrix); std::cout << '\n';
print_matrix(create_sub_matrix<0>(matrix)); std::cout << '\n';
print_matrix(create_sub_matrix<1>(matrix)); std::cout << '\n';
}
希望这足以帮助您完全实现determinant
函数。 (PS:在调用它时不需要显式提供 size_t 模板参数给 determinant,它会自动从它的 std::array 参数的大小推导出来)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.