C++ 编译时多个集合的笛卡尔积
我正在为一个笛卡尔积的实现而挣扎 具有给定范围的多个索引C++ 编译时多个集合的笛卡尔积,c++,templates,c++17,cartesian-product,C++,Templates,C++17,Cartesian Product,我正在为一个笛卡尔积的实现而挣扎 具有给定范围的多个索引0,…,n-1 基本理念是要有一个功能: template <template <typename...> class L, typename... Ts, typename F> constexpr auto unpack(L<Ts...>, F f) { return f(Ts{}...); } template <size_t R, size_t N> constexp
0,…,n-1
基本理念是要有一个功能:
template <template <typename...> class L,
typename... Ts, typename F>
constexpr auto unpack(L<Ts...>, F f) {
return f(Ts{}...);
}
template <size_t R, size_t N>
constexpr auto cartesian_product()
{
using P = mp_apply_q<
mp_bind_front<mp_product_q, mp_quote<mp_list>>,
mp_repeat_c<mp_list<mp_iota_c<R>>, N>>;
return unpack(P{},
[](auto... lists){
return std::array{
unpack(lists, [](auto... values){
return std::make_tuple(int(values)...);
})...
};
});
}
cartesian_乘积()
使用包含保存不同产品的元组的输出数组
[(0,..,0), (0,...,1), (0,...,n-1),...., (n-1, ..., n-1)]
下面是一个简单的例子:
auto result = cartesian_product<3, 2>();
我的主要问题是,我的笛卡尔积版本很慢,如果您选择有5个以上的集合,就会产生堆栈溢出。我认为我的代码有太多的递归和临时变量
我的实现(C++17)可以在这里找到:
#包括
#包括
#包括
模板
constexpr自动展平\u元组\u i(T元组,std::index\u序列){
返回std::tuple_cat(std::get(tuple)…);
}
模板
constexpr自动展平元组(T元组){
返回flatten_tuple_i(tuple,std::make_index_sequence{});
}
模板
constexpr自动递归\u展平\u元组(T元组){
如果constexpr(深度0),“最低输入必须是笛卡尔的”);
静态断言((集合>0),“最低输入必须是笛卡尔的”);
返回ct_i(std::make_index_sequence{});
}
int main()
{
constexpr auto crt=cartesian_乘积();
用于(自动和电气:crt){
std::cout您可以轻松完成此操作,而无需递归。请注意,每个元组都是从0
到range**set
的数字,在baserange
中,因此您可以递增一个计数器(或应用于std::index\u序列
)并逐个计算每个值
下面是一个实现(它返回std::array
s的std::array
,其工作原理与std::tuple
s基本相同,因为您可以get
,tuple\u size
和std::array
上的tuple\u元素
,但如果确实需要,可以将它们转换为std::tuple
s):
#包括
#包括
请注意,(empty_set)^0
在这里被定义为包含一个空集的集合,但这可以通过使ipow(0,0)==0
而不是1
来改变,因为我也在研究一个解决方案,我认为我也发布了它(尽管与Artyer的答案非常相似).同样的前提,我们用数组替换元组,只需迭代元素,逐个递增
请注意,power
功能已断开,因此如果需要功率值0;)
{
--idx;
如果(++计数器[idx]>=最大值)
{
setAll(counter.begin()+idx,counter.end(),0);//因为std::fill还不是constexpr
}
其他的
{
打破
}
}
}
//std::pow不是constexpr
constexpr自动电源=[](自动基础,自动电源)
{
自动结果=基准;
而(--功率)
结果*=基数;
返回结果;
};
模板
constexpr自动笛卡尔乘积()
{
std::数组产品{};
std::数组currentSet{};
用于(汽车和产品:产品)
{
产品=电流集;
增量(当前设置、静态_转换(范围));
}
退货产品;
}
int main()
{
constexpr auto crt=cartesian_乘积();
用于(自动和电气:crt)
{
用于(自动val:ele)
std::cout我只是为了好玩而尝试,结果我的想法和@Timo差不多,只是格式/风格不同
#include <iostream>
#include <array>
using namespace std;
template<size_t range, size_t sets>
constexpr auto cartesian_product() {
// how many elements = range^sets
constexpr auto size = []() {
size_t x = range;
size_t n = sets;
while(--n != 0) x *= range;
return x;
}();
auto products = array<array<size_t, sets>, size>();
auto counter = array<size_t, sets>{}; // array of zeroes
for (auto &product : products) {
product = counter;
// counter increment and wrapping/carry over
counter.back()++;
for (size_t i = counter.size()-1; i != 0; i--) {
if (counter[i] == range) {
counter[i] = 0;
counter[i-1]++;
}
else break;
}
}
return products;
}
int main() {
auto prods = cartesian_product<3, 6>();
}
#包括
#包括,这是……好吧,它不是一行,但仍然不是那么糟糕:
template <typename... Lists>
using list_product = mp_product<mp_list, Lists...>;
template <typename... Ts>
constexpr auto list_to_tuple(mp_list<Ts...>) {
return std::make_tuple(int(Ts::value)...);
}
template <typename... Ls>
constexpr auto list_to_array(mp_list<Ls...>) {
return std::array{list_to_tuple(Ls{})...};
}
template <size_t R, size_t N>
constexpr auto cartesian_product()
{
using L = mp_repeat_c<mp_list<mp_iota_c<R>>, N>;
return list_to_array(mp_apply<list_product, L>{});
}
虽然这种方法很难理解,但它是相同的算法。如果你想在编译时使用它,你应该只在编译时数据结构上使用编译时求值。正如上面@Barry所指出的,使用Boost.Mp11可以极大地方便它。当然,你可以在你的计算机上用普通的C++17重新实现相关的基本函数拥有:
#包括
模板结构框{
使用类型=T;
};
模板结构列表{};
模板结构;
使用ConsT=typename Cons::type的模板;
模板结构Cons:Box{};
使用Nil=列表;
模板结构n;
使用NthT=typename Nth::type的模板;
模板结构n:std::tuple_元素{};
模板结构头;
使用HeadT=typename Head::type的模板;
模板结构头:框{};
模板结构尾部;
使用TailT=typename Tail::type的模板;
模板结构尾部:框{};
模板结构Concat;
使用ConcatT=typename Concat::type的模板;
模板结构Concat:Cons{};
模板结构Concat:Cons{};
模板结构Concat:Concat{};
模板结构Concat:Box{};
模板结构前置;
使用PrependT=typename Prepend::type的模板;
模板结构前置:框{};
模板结构前置:框{};
模板结构产品;
使用ProductT=typename Product::type的模板;
模板结构产品:Concat{};
模板结构产品:框{};
使用IntValue=std::integral_常量的模板;
模板结构整数序列;
使用IntegerSequenceT=typename IntegerSequence::type的模板;
模板结构整型序列:框{};
模板使用范围=整数序列;
模板结构笛卡尔立方;
使用CartesianCube=typename CartesianCube::type的模板;
模板结构CartesianCube:Product{};
模板结构CartesianCube:Box{};
模板std::ostream&operatorI我只是好奇:为什么要在编译时得到结果?@BlueTune笛卡尔乘积可以用来生成矩阵中的位置。例如a the cartesian()product生成了行和列大小为n-1的2D矩阵中的所有位置。我正在编写一个张量乘法库,我的算法就是基于此。@BlueTune,由于这些位置总是相同的,所以您可以计算所有索引(此处:元组)在编译时,以节省运行时间。您可以使用C++20吗?如果可以,我建议使用关键字consteval
来简化代码。感谢您的提示,但gcc10尚未发布。我选择了您的答案,因为它看起来很干净
#include <iostream>
#include <array>
using namespace std;
template<size_t range, size_t sets>
constexpr auto cartesian_product() {
// how many elements = range^sets
constexpr auto size = []() {
size_t x = range;
size_t n = sets;
while(--n != 0) x *= range;
return x;
}();
auto products = array<array<size_t, sets>, size>();
auto counter = array<size_t, sets>{}; // array of zeroes
for (auto &product : products) {
product = counter;
// counter increment and wrapping/carry over
counter.back()++;
for (size_t i = counter.size()-1; i != 0; i--) {
if (counter[i] == range) {
counter[i] = 0;
counter[i-1]++;
}
else break;
}
}
return products;
}
int main() {
auto prods = cartesian_product<3, 6>();
}
// given cartesian_product<3, 4>
[0, 0, 0, 0]
[0, 0, 0, 1]
[0, 0, 0, 2]
[0, 0, 1, 0] // carry over
...
...
[2, 2, 2, 2]
template <typename... Lists>
using list_product = mp_product<mp_list, Lists...>;
template <typename... Ts>
constexpr auto list_to_tuple(mp_list<Ts...>) {
return std::make_tuple(int(Ts::value)...);
}
template <typename... Ls>
constexpr auto list_to_array(mp_list<Ls...>) {
return std::array{list_to_tuple(Ls{})...};
}
template <size_t R, size_t N>
constexpr auto cartesian_product()
{
using L = mp_repeat_c<mp_list<mp_iota_c<R>>, N>;
return list_to_array(mp_apply<list_product, L>{});
}
template <template <typename...> class L,
typename... Ts, typename F>
constexpr auto unpack(L<Ts...>, F f) {
return f(Ts{}...);
}
template <size_t R, size_t N>
constexpr auto cartesian_product()
{
using P = mp_apply_q<
mp_bind_front<mp_product_q, mp_quote<mp_list>>,
mp_repeat_c<mp_list<mp_iota_c<R>>, N>>;
return unpack(P{},
[](auto... lists){
return std::array{
unpack(lists, [](auto... values){
return std::make_tuple(int(values)...);
})...
};
});
}