为了求解行列式的值,需要数组的全排列,上一篇文章已经实现了一个元编程的容器了,本章基于容器,定义了二维数组容器用以盛放全排列的结果。那么就看一下具体的实现吧。
首先对原来的容器做了一些修改,主要是因为当容器为空时,一些操作的定义得作一些短路操作,否则就无法编译通过(这里就不得不再次吐槽一下C艹元编程了,一个简单的异常判断都要写一大堆代码),这里在一维数组中增加了两元素的互换操作swap。
#include <type_traits>
template<int i, int...num_ts>
constexpr bool contain_number_b = contain_number<i, num_ts...>::b;
template<int...is>
struct container;
template<int...is>
struct protect_cal;
template<int N, int top, int...remains>
struct n_cal
{
using t_front = typename protect_cal<remains...>::template t_front<N - 1>::template push_front<top>;
using t_after = typename protect_cal<remains...>::template t_after< N - 1>;
};
template<int...remains>
struct n_cal<0, remains...>
{
using t_front = container<>;
using t_after = container<remains...>;
};
/* 屏蔽空参数情况时的类型定义 */
template<int...is>
struct protect_cal
{
template<int N>
using t_front = typename n_cal<N, is...>::t_front;
template<int N>
using t_after = typename n_cal<N, is...>::t_after;
};
template<>
struct protect_cal<>
{
template<int N>
using t_front = container<>;
template<int N>
using t_after = container<>;
};
template<int...is>
struct container
{
template<int N, int itr, int topop, int... remains>
static constexpr int get_cal = get_cal<N, itr + 1, remains...>;
template<int N, int ret, int...remains>
static constexpr int get_cal<N, N, ret, remains...> = ret;
template<int N>
static constexpr int get = get_cal<N, 0, is...>;
static constexpr int size = sizeof...(is);
template<int i> using push_back = container<is..., i>;
template<int i> using push_front = container<i, is...>;
template<int total_N, int cur_N, typename other>
struct st_push_other_back
{
using t_next = push_back < other::template get<cur_N>>;
using ret = typename t_next::template st_push_other_back<total_N, cur_N + 1, other>::ret;
};
template<int N, typename other>
struct st_push_other_back<N, N, other>
{
using ret = std::conditional_t<N!=0, container<is...>, container<is...> >;
};
template<typename other>
using push_other_back = typename st_push_other_back<other::size, 0, other>::ret;
template<int N>
using front_n = typename protect_cal<is...>::template t_front<N>;
template<int N>
using after_n = typename protect_cal<is...>::template t_after<N>;;
template<int N, int M>
using mid_nm = typename front_n<M>::template after_n<N>;
template<int N, int M>
struct swap_cal
{
using swap_front = front_n<N>;
using swap_mid = mid_nm<N + 1, M>;
using swap_after = after_n<M + 1>;
using ret = typename swap_front::template push_back<get<M>>::template push_other_back<swap_mid>::template push_back<get<N>>::template push_other_back<swap_after>;
};
template<int N, int M>
using swap = typename swap_cal<N, M>::ret;
template<int N, int v>
struct set_cal
{
using swap_front = front_n<N>;
using swap_after = after_n<N + 1>;
using ret = typename swap_front::template push_back<v>::template push_other_back<swap_after>;
};
template<int N, int v>
using set = typename set_cal<N, v>::ret;
};
接下来实现一下二维的数组:
template<typename...ts>
struct container2d
{
template<int N, int itr, typename topop = std::enable_if_t<0==sizeof...(ts), container<> >, typename... remains>
struct get_cal
{
using t = std::conditional_t<0!= sizeof...(ts), typename get_cal<N, itr + 1, remains...>::t, container<> >;
};
template<int N, typename ret, typename...remains>
struct get_cal<N, N, ret, remains...>
{
using t = ret;
};
template<int N>
using get = std::conditional_t<0!=sizeof...(ts), typename get_cal<N, 0, ts...>::t, void>;
static constexpr int size = sizeof...(ts);
template<typename t> using push_back = container2d<ts..., t>;
template<typename test_t, typename cur_t, typename...remains>
static constexpr bool contain_cal = std::is_same_v< test_t, cur_t> || contain_cal<test_t, remains...>;
template<typename test_t, typename cur_t>
static constexpr bool contain_cal<test_t, cur_t> = std::is_same_v< test_t, cur_t>;
template<typename test_t, typename...params>
static constexpr bool contain_p = contain_cal<test_t, params...>;
template<typename test_t>
static constexpr bool contain_p<test_t> = false;
template<typename test_t>
static constexpr bool contain = contain_p<test_t, ts...>;
};
然后是求全排列的函数,处理的逻辑是:1、交换容器中的sub_idx和main_idx;2、如果交换后的在二维数组cont2d_t中不存在就加入形成cont2d_t_with_new_swap_added类型;3、继续计算子全排列,排列对象为交换后的容器的main_idx以下的数据(这里理解起来有些困难,说起来也困难,直接看代码理解吧);4、循环减少sub_idx,递归执行all_perm,最终得到的结果要特化一下,在sub_idx和main_idx都是0的情况下得到的就是全排列的结果啦。代码如下:
template<typename cont2d_t, typename cont_t, int sub_idx, int main_idx>
struct all_perm
{
using swaped_cont_t = std::conditional_t<sub_idx!=main_idx, typename cont_t::template swap<sub_idx, main_idx>, cont_t>;
using cont2d_t_with_new_swap_added = std::conditional_t<cont2d_t::template contain<swaped_cont_t>
, cont2d_t
, typename cont2d_t::template push_back<swaped_cont_t> >; // 将交换后的两个加进去
using sub_perm = all_perm< cont2d_t_with_new_swap_added, swaped_cont_t, main_idx - 1, main_idx - 1 >;
using cont2d_with_sub_all_perm = typename sub_perm::ret; // 将交换过sub_idx和main_idx后的子串全排列再加进队列
using ret = typename all_perm< cont2d_with_sub_all_perm, cont_t, sub_idx - 1, main_idx>::ret;
};
template<typename cont2d_t, typename cont_t, int main_idx>
struct all_perm<cont2d_t, cont_t, 0, main_idx>
{
using swaped_cont_t = typename cont_t::template swap<0, main_idx>;
using cont2d_t_with_new_swap_added = std::conditional_t<cont2d_t::template contain<swaped_cont_t>
, cont2d_t
, typename cont2d_t::template push_back<swaped_cont_t> >; // 将交换后的两个加进去
using cont2d_with_sub_all_perm = typename all_perm<cont2d_t_with_new_swap_added, swaped_cont_t, main_idx - 1, main_idx - 1>::ret; // 将交换过sub_idx和main_idx后的子串全排列再加进队列
using ret = cont2d_with_sub_all_perm; // 自己的循环已经执行完成
};
template<typename cont2d_t, typename cont_t>
struct all_perm<cont2d_t, cont_t, 0, 0>
{
/* 该swap的都已经swap完成了 */
using ret = std::conditional_t<cont2d_t::template contain<cont_t>
, cont2d_t
, typename cont2d_t::template push_back<cont_t> >; // 将交换后的两个加进去
};
template<typename cont_t>
using all_perm_t = typename all_perm<container2d<>, cont_t, cont_t::size - 1, cont_t::size - 1>::ret;
之前也写过形成一个顺序一维元编程数组的例子,这里贴在下面:
template<int N, int cur_i, int... is>
struct gen_seq
{
using t = typename gen_seq<N, 1 + cur_i, is..., cur_i>::t;
};
template<int N, int... is>
struct gen_seq<N, N, is...>
{
using t = container<is...>;
};
template<int N>
using gen_seq_t = typename gen_seq<N, 0>::t;
至此,所有的试验条件都已经具备,就进行一下试验吧。试验代码如下:
#include <conio.h>
int main(int argc, char** argv)
{
all_perm_t<gen_seq_t<5>> k1;
int size_k = decltype(k1)::size;
_getch();
return 0;
}
可以在_getch调用处打断点,可以看到k1的类型是0-5数组的全排列。接下来结合之前的元编程矩阵运算就可以形成元编程的行列式求和啦。然后就是矩阵求逆以及元编程的BP神经网络实现。