3

到目前为止,我一直在尝试元编程:

// compiled on Ubuntu 13.04 with:
// clang++ -O3 -ftemplate-depth-8192 -fconstexpr-depth=4096 -std=c++11 -stdlib=libc++ -lcxxrt -ldl compile-time-primes.cpp -o compile-time-primes

// assembly output with:
// clang++ -S -mllvm --x86-asm-syntax=intel -O3 -ftemplate-depth-8192 -fconstexpr-depth=4096 -std=c++11 -stdlib=libc++ -lcxxrt -ldl compile-time-primes.cpp -o compile-time-primes.asm

#include <array>
#include <iostream>

template<typename T>
constexpr bool is_prime(T number, T limit, T counter)
{
    return counter >= limit
        ? number % limit != 0
        : number % counter
            ? is_prime(number, number / counter, counter + 2)
            : false;
}

template<typename T>
constexpr bool is_prime(T n)
{
    return n == 2 || n == 3 || n == 5
        ? true
        : n <= 1 || n % 2 == 0
            ? false
            : is_prime(n, n / 3, T{3});
}

template<size_t n>
struct print_below;

template<> struct print_below<2> { inline static void primes() { std::cout << 2; } };
template<size_t n>
struct print_below
{
    inline static void primes()
    {
        print_below<n - 1>::primes();
        constexpr bool is_n_prime = is_prime(n);
        if(is_n_prime)
            std::cout << ", " << n;
    }
};

template <typename T, T... N>
struct primes { static const std::array<bool, sizeof...(N)> cache; };

template <typename T, typename L, T R>
struct append_param;

template <typename T, T... L, T R>
struct append_param<T, primes<T, L...>, R> { using primes = primes<T, L..., R>; };

template <size_t N>
struct indexer : append_param<size_t, typename indexer<N - 1>::primes, N - 1> {};

template <>
struct indexer<0> { using primes = primes<size_t>; };

template <typename T, T... N>
const std::array<bool, sizeof...(N)> primes<T, N...>::cache = {{ is_prime(N)... }};

int main()
{
    std::cout << "Some primes: \n";
    print_below<8000>::primes();
    std::cout << std::endl;

    const auto &primes_cache = indexer<1000>::primes::cache;

    for(size_t i = 1; i < primes_cache.size(); ++i)
        std::cout << i << (primes_cache[i] ? " is " : " is not ") << "prime" << std::endl;
}

现在我想知道是否有更好的尾递归算法is_prime可以放入constexpr函数中。

还有什么比这更好的吗?

  • 要求:必须是尾递归的。
  • 可取的:适合一个constexpr函数。
4

1 回答 1

2

是的。

首先,您的主要限制之一将是您的递归深度限制。3对于从到的每个奇数,您的递归一次sqrt(N)。递归限制约为 1000 次,这意味着您最多只能处理 100 万个数字。你需要减少你正在做的递归量。

一种方法是分而治之地搜索你的 number 的因数N。通过一些工作,您可以将其扩展到顺序限制2^1000(即,除了递归限制之外的其他事情,将使其首先无法工作)。

其次,不要检查每个奇数,而是检查 6 mod 1 和 5,并在开始时检查 2/3/5 的特殊情况。可以使用更长范围的模式,而不仅仅是半径为 6。

第三,有足够可靠的概率素性检验,使用它们是正确的答案。您可能可以建立一个测试失败的数字的硬编码表,对照该表进行检查,然后进行测试,并使您的上限大大高于您实际上可以做的。

您的设计的另一个问题是它一次测试一个素数:理想情况下,您应该建立一个素数表并使用它们来帮助您进行素数测试。一些编译器会对以前的结果进行记忆,您可以研究利用它。

于 2013-06-03T02:11:48.120 回答