Skip to main content
Code Review

Return to Answer

Commonmark migration
Source Link

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
Source Link
Toby Speight
  • 87.5k
  • 14
  • 104
  • 322

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";
}
std::fill is not constexpr - thanks Miles Budnek
Source Link
Toby Speight
  • 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;
}
constexpr version
Source Link
Toby Speight
  • 87.5k
  • 14
  • 104
  • 322
Loading
constexpr version
Source Link
Toby Speight
  • 87.5k
  • 14
  • 104
  • 322
Loading
Source Link
Toby Speight
  • 87.5k
  • 14
  • 104
  • 322
Loading
lang-cpp

AltStyle によって変換されたページ (->オリジナル) /