C++:试图通过组合和帕斯卡三角形来理解constexpr

C++: Trying to understand constexpr through combinations and Pascal's Triangle

本文关键字:三角形 帕斯卡 constexpr 组合 C++      更新时间:2023-10-16

我知道constexpr应该允许消除/简化过去使用的许多模板编程技巧,但我对C++11还很陌生,在计算组合函数时,我很难理解为什么:

C(n,r) = n!/(r!(n-r)!) = C(n-1,r-1) + C(n-1,r)

递归方法在运行时执行时速度慢得离谱,但允许计算更大值的C(n,r),它与模板配合使用很好,但我无法使它与constexpr配合使用。这是我正在使用的模板代码:

using factorial_t = unsigned long long;
template<size_t N, size_t R>
struct recursive_combinations {
enum: factorial_t { value = recursive_combinations<N-1, R>::value +
recursive_combinations<N-1, R-1>::value};
};
template<size_t N>
struct recursive_combinations<N,0> {
enum: factorial_t { value = 1 };
};
template<size_t N>
struct recursive_combinations<N,N> {
enum: factorial_t { value = 1 };
};

这是constexpr版本(也许我在这里做错了什么(:

constexpr const factorial_t recursiveCombinations(const size_t N, const size_t R) {
if (R == N || R == 0) return 1;
return recursiveCombinations(N-1, R) + recursiveCombinations(N-1, R-1);
}

当我尝试这个:

constexpr auto comb1_50_30 = recursive_combinations<50,30>::value;

一切都很好,我得到了47129212243960的预期结果,这是通过计算阶乘无法获得的。

然而,当我尝试这个:

constexpr auto comb2_50_30 = recursiveCombinations(50, 30);

编译器(clang v5.0.1,设置为C++17模式(抱怨comb2_50_30必须由常量表达式初始化。

有人能帮我弄清楚我做错了什么吗?或者是否有办法让constexpr发挥作用(如果没有,为什么不呢(?

以将结果的限制降低min(r, n-r)的一个因子为代价,您可以使用以下更有效的实现:

constexpr uint64_t efficientCombinations(uint64_t n, uint64_t r)
{
uint64_t accum = 1U;
if (n - r > r) r = n - r;
for( uint64_t x = 1; x <= n - r; ++x )
{
accum *= (r + x);
accum /= x;
}
return accum;
}

为了与C++11兼容,将此迭代转换为递归非常简单(如果有点混乱的话(。

(注意:根据"让this工作"的意思,这样改变算法可能不是理想的方法……但它太大了,无法发表评论(

抱怨comb2_50_30必须由常量表达式初始化。

错误的下一行告诉原因:

note: constexpr evaluation hit maximum step limit; possible infinite loop?

对于constexpr评估来说,您的扩展在计算上过于昂贵。您可以使用-fconstexpr-steps=X-fconstexpr-depth=X来增加编译器计算constexpr函数的工作量,但我没能找到允许recursiveCombinations(50, 30)在clang上编译的值。

g++编译的CCD_ 14版本很好。