我需要检查编译时是否有一些整数素数(将 bool 值作为模板参数)。
我写的代码做得很好:
#include <type_traits>
namespace impl {
template <int n, long long i>
struct PrimeChecker {
typedef typename std::conditional<
(i * i > n),
std::true_type,
typename std::conditional<
n % i == 0,
std::false_type,
typename PrimeChecker<n, (i * i > n ) ? -1 : i + 1>::type
>::type
>::type type;
};
template <int n>
struct PrimeChecker<n, -1> {
typedef void type;
};
} // namespace impl
template<int n>
struct IsPrime {
typedef typename impl::PrimeChecker<n, 2>::type type;
};
template<>
struct IsPrime<1> : public std::false_type {
};
它适用于 ~1000000 的数字,但失败并出现错误 109
prog.cpp:15:23: error: template instantiation depth exceeds maximum of 900 (use -ftemplate-depth= to increase the maximum) instantiating ‘struct impl::PrimeChecker<1000000000, 901ll>’
>::type type;
^
prog.cpp:15:23: recursively required from ‘struct impl::PrimeChecker<1000000000, 3ll>’
prog.cpp:15:23: required from ‘struct impl::PrimeChecker<1000000000, 2ll>’
prog.cpp:24:54: required from ‘struct IsPrime<1000000000>’
prog.cpp:32:41: required from here
我无法增加深度限制。有没有可能减少我使用的深度?
我想要实现的目标:我需要检查编译时是否为常量无需更改编译字符串,模板深度限制为 900 和 constexpr
深度限制 512。(我的 g++ 的默认值)。它应该适用于所有正 int32 或至少适用于高达 109+9
最佳答案
您可以使用分治算法将范围分成两半,从而将空间要求从线性更改为对数。该方法采用分治法,只测试奇数因子(Live at Coliru):
namespace detail {
using std::size_t;
constexpr size_t mid(size_t low, size_t high) {
return (low + high) / 2;
}
// precondition: low*low <= n, high*high > n.
constexpr size_t ceilsqrt(size_t n, size_t low, size_t high) {
return low + 1 >= high
? high
: (mid(low, high) * mid(low, high) == n)
? mid(low, high)
: (mid(low, high) * mid(low, high) < n)
? ceilsqrt(n, mid(low, high), high)
: ceilsqrt(n, low, mid(low, high));
}
// returns ceiling(sqrt(n))
constexpr size_t ceilsqrt(size_t n) {
return n < 3
? n
: ceilsqrt(n, 1, size_t(1) << (std::numeric_limits<size_t>::digits / 2));
}
// returns true if n is divisible by an odd integer in
// [2 * low + 1, 2 * high + 1).
constexpr bool find_factor(size_t n, size_t low, size_t high)
{
return low + 1 >= high
? (n % (2 * low + 1)) == 0
: (find_factor(n, low, mid(low, high))
|| find_factor(n, mid(low, high), high));
}
}
constexpr bool is_prime(std::size_t n)
{
using detail::find_factor;
using detail::ceilsqrt;
return n > 1
&& (n == 2
|| (n % 2 == 1
&& (n == 3
|| !find_factor(n, 1, (ceilsqrt(n) + 1) / 2))));
}
编辑:使用编译时 sqrt 将搜索空间绑定(bind)到上限 (sqrt(n)),而不是 n/2。现在可以根据需要计算 is_prime(100000007)
(和 is_prime(1000000000039ULL)
在 Coliru 上没有爆炸。
对于糟糕的格式表示歉意,我仍然没有为 C++11 折磨的 constexpr
子语言找到一种舒适的风格。
编辑:清理代码:用另一个函数替换宏,将实现细节移动到细节命名空间,从 Pablo 的答案中窃取缩进样式。
关于c++ - 编译时素数检查,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18303632/