Какая самая эффективная функция проверки простых чисел с хвостовой рекурсией известна?

Я экспериментировал с метапрограммированием до этого момента:

// 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 функции.

person pepper_chico    schedule 03.06.2013    source источник
comment
На самом деле это не дубликат - это о хвостовой рекурсии.   -  person Yakk - Adam Nevraumont    schedule 03.06.2013


Ответы (1)


Да.

Прежде всего, одним из ваших основных ограничений будет ограничение глубины рекурсии. Ваш рекурсивно один раз для каждого нечетного числа от 3 до sqrt(N). С пределом рекурсии ~ 1000 это означает, что вы сможете обрабатывать только числа до 1 миллиона. Вам нужно уменьшить количество рекурсий, которые вы делаете.

Один из способов сделать это — выполнить поиск делителей вашего числа N по принципу «разделяй и властвуй». Немного поработав, вы можете расширить его до предела порядка 2^1000 (т. е. другие вещи, помимо ограничения рекурсии, сначала заставят его не работать).

Во-вторых, вместо того, чтобы проверять каждое нечетное число, проверьте 6 по модулю 1 и 5, с особым случаем, чтобы проверить 2/3/5 в начале. Можно использовать шаблоны с большим радиусом действия, а не просто радиус 6.

В-третьих, существуют вероятностные тесты на простоту, которые достаточно надежны, чтобы их использование было правильным ответом. Вероятно, вы могли бы создать жестко закодированную таблицу чисел, для которых тест не пройден, свериться с этой таблицей и выполнить тесты в противном случае, и сделать свой верхний предел намного, намного выше, чем вы могли бы практически сделать в противном случае.

Еще одна проблема с вашим дизайном заключается в том, что он проверяет простые числа по одному: в идеале вы должны создать таблицу простых чисел и использовать ее для помощи в тестировании простых чисел. Некоторые компиляторы будут запоминать предыдущие результаты, вы можете изучить это.

person Yakk - Adam Nevraumont    schedule 03.06.2013
comment
Для хвостовых рекурсивных вариантов использования, которые меня интересуют, применяется ли ограничение рекурсии? поскольку объектный код не содержит рекурсии, только исходный код. Хотя я предоставил пример метапрограммирования, меня интересует не только время компиляции, отсюда и акцент на хвостовой рекурсии. - person pepper_chico; 03.06.2013
comment
@chico хорошая мысль. Я не знаю об исключении в стандарте для хвостовой рекурсии, хотя, возможно, оно должно быть. - person Yakk - Adam Nevraumont; 03.06.2013
comment
@chico Поскольку я упустил важность хвостовой рекурсии в вашем вопросе, вы хотите, чтобы я удалил этот ответ, чтобы оставить ваш вопрос без ответа? - person Yakk - Adam Nevraumont; 03.06.2013
comment
но я был бы рад, если бы возможное дублирование и голосование за закрытие были удалены, поскольку упомянутая цель дублирования нигде не упоминает советы и ограничения хвостовой рекурсии. - person pepper_chico; 03.06.2013
comment
@chico Я не могу отменить голосование, чтобы закрыть, но я могу проголосовать за повторное открытие, если оно превысит 5. - person Yakk - Adam Nevraumont; 03.06.2013