Simple things
#Simple things
You'veYou've misspelt std::size_t
in a couple of places. I find I often do that, particularly when I've been writing C as well.
Conversion of primes table
#Conversion of primes table
II don't think there's a good reason to collapse primesBoolArray
into a vector when it's perfectly usable as it is (and faster to use than a binary search). In fact, I'd go further, and make it live up to its name (since we always call the function with a constant):
#Modified code #include #include #include #include
Modified code
#include <array>
#include <algorithm>
#include <cmath>
#include <cstddef>
// Get an array of bool - containing true at the prime indexes
template<std::size_t N>
std::array<bool,N> primesUpto()
{
// Use the Sieve of Eratosthenes
std::array<bool,N> primes{};
primes[0] = primes[1] = false;
std::fill(primes.begin()+2, primes.end(), true);
constexpr std::size_t sqrtLimit = std::sqrt(N) + 1;
for (std::size_t i = 2; i < sqrtLimit; ++i)
if (primes[i])
for (std::size_t j = i+i; j < N; j += i)
primes[j] = false;
return primes;
}
template<std::size_t N>
constexpr bool isTruncPrime(std::size_t n, const std::array<bool,N>& primes)
{
for (std::size_t x = 10; x < n; x*= 10)
if (!primes[n%x]) return false;
for (; n; n/=10)
if (!primes[n]) return false;
return true;
}
#Simple things
You've misspelt std::size_t
in a couple of places. I find I often do that, particularly when I've been writing C as well.
#Conversion of primes table
I don't think there's a good reason to collapse primesBoolArray
into a vector when it's perfectly usable as it is (and faster to use than a binary search). In fact, I'd go further, and make it live up to its name (since we always call the function with a constant):
#Modified code #include #include #include #include
// Get an array of bool - containing true at the prime indexes
template<std::size_t N>
std::array<bool,N> primesUpto()
{
// Use the Sieve of Eratosthenes
std::array<bool,N> primes{};
primes[0] = primes[1] = false;
std::fill(primes.begin()+2, primes.end(), true);
constexpr std::size_t sqrtLimit = std::sqrt(N) + 1;
for (std::size_t i = 2; i < sqrtLimit; ++i)
if (primes[i])
for (std::size_t j = i+i; j < N; j += i)
primes[j] = false;
return primes;
}
template<std::size_t N>
constexpr bool isTruncPrime(std::size_t n, const std::array<bool,N>& primes)
{
for (std::size_t x = 10; x < n; x*= 10)
if (!primes[n%x]) return false;
for (; n; n/=10)
if (!primes[n]) return false;
return true;
}
Simple things
You've misspelt std::size_t
in a couple of places. I find I often do that, particularly when I've been writing C as well.
Conversion of primes table
I don't think there's a good reason to collapse primesBoolArray
into a vector when it's perfectly usable as it is (and faster to use than a binary search). In fact, I'd go further, and make it live up to its name (since we always call the function with a constant):
Modified code
#include <array>
#include <algorithm>
#include <cmath>
#include <cstddef>
// Get an array of bool - containing true at the prime indexes
template<std::size_t N>
std::array<bool,N> primesUpto()
{
// Use the Sieve of Eratosthenes
std::array<bool,N> primes{};
primes[0] = primes[1] = false;
std::fill(primes.begin()+2, primes.end(), true);
constexpr std::size_t sqrtLimit = std::sqrt(N) + 1;
for (std::size_t i = 2; i < sqrtLimit; ++i)
if (primes[i])
for (std::size_t j = i+i; j < N; j += i)
primes[j] = false;
return primes;
}
template<std::size_t N>
constexpr bool isTruncPrime(std::size_t n, const std::array<bool,N>& primes)
{
for (std::size_t x = 10; x < n; x*= 10)
if (!primes[n%x]) return false;
for (; n; n/=10)
if (!primes[n]) return false;
return true;
}
Reduce the search space
Instead of testing all odd numbers, we can reduce the number of calls to isTruncPrime
. We know that all such numbers end in a prime, but can't end in 2 or 5 (since they are composite), so the last digit can be only 3 or 7. Similarly, the first digit must be prime, and intermediate digits must be odd (but not 5):
static const auto b0 = { 0, 20000, 30000, 50000, 70000 };
static const auto b1 = { 10000, 30000, 70000, 90000 };
static const auto c0 = { 0, 2000, 3000, 5000, 7000 };
static const auto c1 = { 1000, 3000, 7000, 9000 };
static const auto d0 = { 0, 200, 300, 500, 700 };
static const auto d1 = { 100, 300, 700, 900 };
static const auto e0 = { 20, 30, 50, 70 };
static const auto e1 = { 10, 30, 70, 90 };
for (std::size_t a: { 0, 200000, 300000, 500000, 700000 }) {
for (std::size_t b: a ? b1 : b0) {
for (std::size_t c: a+b ? c1 : c0) {
for (std::size_t d: a+b+c ? d1 : d0) {
for (std::size_t e: a+b+c+d ? e1 : e0) {
for (std::size_t f: { 3, 7 }) {
const auto n = a + b + c + d + e + f;
if (isTruncPrime(n, primes)) {
sum += n;
}
}
}
}
}
}
}
This knocks another 70% off the runtime on my system.
#include <iostream>
int main()
{
static const auto primes = primesUpto<1'000'000>();
auto sum = 0ull;
//static Startconst atauto 11b0 = { 0, because20000, single-digit30000, numbers50000, aren't70000 truncatable};
//static Advanceconst byauto 2b1 = { 10000, because30000, even70000, numbers90000 are};
non-prime static const auto c0 = { 0, 2000, 3000, 5000, 7000 };
static const auto c1 = { 1000, 3000, 7000, 9000 };
static const auto d0 = { 0, 200, 300, 500, 700 };
static const auto d1 = { 100, 300, 700, 900 };
static const auto e0 = { 20, 30, 50, 70 };
static const auto e1 = { 10, 30, 70, 90 };
for (exceptstd::size_t 2a: { 0, but200000, 2<11300000, 500000, 700000 }) {
for (std::size_t nb: =a 11;? b1 n: <b0) primes.size{
for (std::size_t c: a+b ? c1 : c0); {
n += 2 for (std::size_t d: a+b+c ? d1 : d0) {
for (std::size_t e: a+b+c+d ? e1 : e0) {
for (std::size_t f: { 3, 7 }) {
const auto n = a + b + c + d + e + f;
if (isTruncPrime(n, primes)) {
sum += n;
}
}
}
}
}
}
}
std::cout << sum << "\n";
}
#include <iostream>
int main()
{
static const auto primes = primesUpto<1'000'000>();
auto sum = 0ull;
// Start at 11, because single-digit numbers aren't truncatable
// Advance by 2, because even numbers are non-prime (except 2, but 2<11)
for (std::size_t n = 11; n < primes.size(); n += 2) {
if (isTruncPrime(n, primes))
sum += n;
}
std::cout << sum << "\n";
}
Reduce the search space
Instead of testing all odd numbers, we can reduce the number of calls to isTruncPrime
. We know that all such numbers end in a prime, but can't end in 2 or 5 (since they are composite), so the last digit can be only 3 or 7. Similarly, the first digit must be prime, and intermediate digits must be odd (but not 5):
static const auto b0 = { 0, 20000, 30000, 50000, 70000 };
static const auto b1 = { 10000, 30000, 70000, 90000 };
static const auto c0 = { 0, 2000, 3000, 5000, 7000 };
static const auto c1 = { 1000, 3000, 7000, 9000 };
static const auto d0 = { 0, 200, 300, 500, 700 };
static const auto d1 = { 100, 300, 700, 900 };
static const auto e0 = { 20, 30, 50, 70 };
static const auto e1 = { 10, 30, 70, 90 };
for (std::size_t a: { 0, 200000, 300000, 500000, 700000 }) {
for (std::size_t b: a ? b1 : b0) {
for (std::size_t c: a+b ? c1 : c0) {
for (std::size_t d: a+b+c ? d1 : d0) {
for (std::size_t e: a+b+c+d ? e1 : e0) {
for (std::size_t f: { 3, 7 }) {
const auto n = a + b + c + d + e + f;
if (isTruncPrime(n, primes)) {
sum += n;
}
}
}
}
}
}
}
This knocks another 70% off the runtime on my system.
#include <iostream>
int main()
{
static const auto primes = primesUpto<1'000'000>();
auto sum = 0ull;
static const auto b0 = { 0, 20000, 30000, 50000, 70000 };
static const auto b1 = { 10000, 30000, 70000, 90000 };
static const auto c0 = { 0, 2000, 3000, 5000, 7000 };
static const auto c1 = { 1000, 3000, 7000, 9000 };
static const auto d0 = { 0, 200, 300, 500, 700 };
static const auto d1 = { 100, 300, 700, 900 };
static const auto e0 = { 20, 30, 50, 70 };
static const auto e1 = { 10, 30, 70, 90 };
for (std::size_t a: { 0, 200000, 300000, 500000, 700000 }) {
for (std::size_t b: a ? b1 : b0) {
for (std::size_t c: a+b ? c1 : c0) {
for (std::size_t d: a+b+c ? d1 : d0) {
for (std::size_t e: a+b+c+d ? e1 : e0) {
for (std::size_t f: { 3, 7 }) {
const auto n = a + b + c + d + e + f;
if (isTruncPrime(n, primes)) {
sum += n;
}
}
}
}
}
}
}
std::cout << sum << "\n";
}
- 87.5k
- 14
- 104
- 322
// Get an array of bool - containing true at the prime indexes
template<std::size_t N>
constexpr std::array<bool,N> primesUpto()
{
// Use the Sieve of Eratosthenes
std::array<bool,N> primes{};
primes[0] = primes[1] = false;
std::fill(primes.begin()+2, primes.end(), true);
constexpr std::size_t sqrtLimit = std::sqrt(N) + 1;
for (std::size_t i = 2; i < sqrtLimit; ++i)
if (primes[i])
for (std::size_t j = i+i; j < N; j += i)
primes[j] = false;
return primes;
}
template<std::size_t N>
constexpr bool isTruncPrime(std::size_t n, const std::array<bool,N>& primes)
{
for (std::size_t x = 10; x < n; x*= 10)
if (!primes[n%x]) return false;
for (; n; n/=10)
if (!primes[n]) return false;
return true;
}
// Get an array of bool - containing true at the prime indexes
template<std::size_t N>
constexpr std::array<bool,N> primesUpto()
{
// Use the Sieve of Eratosthenes
std::array<bool,N> primes{};
primes[0] = primes[1] = false;
std::fill(primes.begin()+2, primes.end(), true);
constexpr std::size_t sqrtLimit = std::sqrt(N) + 1;
for (std::size_t i = 2; i < sqrtLimit; ++i)
if (primes[i])
for (std::size_t j = i+i; j < N; j += i)
primes[j] = false;
return primes;
}
template<std::size_t N>
constexpr bool isTruncPrime(std::size_t n, const std::array<bool,N>& primes)
{
for (std::size_t x = 10; x < n; x*= 10)
if (!primes[n%x]) return false;
for (; n; n/=10)
if (!primes[n]) return false;
return true;
}
// Get an array of bool - containing true at the prime indexes
template<std::size_t N>
std::array<bool,N> primesUpto()
{
// Use the Sieve of Eratosthenes
std::array<bool,N> primes{};
primes[0] = primes[1] = false;
std::fill(primes.begin()+2, primes.end(), true);
constexpr std::size_t sqrtLimit = std::sqrt(N) + 1;
for (std::size_t i = 2; i < sqrtLimit; ++i)
if (primes[i])
for (std::size_t j = i+i; j < N; j += i)
primes[j] = false;
return primes;
}
template<std::size_t N>
constexpr bool isTruncPrime(std::size_t n, const std::array<bool,N>& primes)
{
for (std::size_t x = 10; x < n; x*= 10)
if (!primes[n%x]) return false;
for (; n; n/=10)
if (!primes[n]) return false;
return true;
}