The choice of algorithm seems good, and std::next_permutation() was exactly how I would implement the search.
Style review
We could separate the loop from the test for the divisibility property:
static bool has_euler43_property(const std::string& digits) { static constexpr unsigned int primes[] {2, 3, 5, 7, 11, 13, 17}; for (int i = 0; i < 7; ++i) { long substring_num = std::stol(digits.substr(i+1, 3)); if (substring_num % primes[i] != 0) { // failed a test return false; } } // all tests passed return true; } int main() { std::string digits = "0123456789"; unsigned long long sum = 0; do { if (has_euler43_property(digits)) { sum += std::stoul(digits); } } while ( std::next_permutation(digits.begin(), digits.end()) ); std::cout << sum << "\n"; }
That's a little easier to follow than using a has_property variable.
Note that I changed PRIMES to primes - we normally use upper-case only for preprocessor macros, which need particular care. I also changed the second std::stol to std::stoul, since we are accumulating unsigned values.
Performance review
I got a substantial speed-up by avoiding the substring creation and the call of std::stoul inside the loop:
long substring_num = (digits[i+1] - '0') * 100 + (digits[i+2] - '0') * 10 + digits[i+3] - '0';
And a little more by changing the type and consolidating the subtraction:
unsigned int substring_num = digits[i+1] * 100 + digits[i+2] * 10 + digits[i+3] - '0' * 111;
I shaved another 10% by prepending quick tests for divisibility by 2 and by 5:
// quick tests if (digits[3] % 2 != '0' % 2) { return 0; } if (digits[5] != '0' && digits[5] != '5') { return 0; }
Adding a quick test for multiples of 3 produced only a very small improvement, but allows us to skip the slow path for primes less than 7.
Reversing the order of testing (as there are fewer multiples of 17 than of 7) produced a tiny improvement too.
Improved code
On my system, this takes around 0.01 seconds, compared to 0.21 seconds for the original code:
#include <algorithm> #include <array> #include <iostream> #include <string> // returns the number itself for digit strings that satisfy the type // or zero for digit strings that don't // * digits must be a ten-digit string (no bounds checking here) static unsigned long long euler43_property(const std::string& digits) { static constexpr std::array primes = {2, 3, 5, 7, 11, 13, 17}; // quick tests if (digits[3] % 2 != '0' % 2) { return 0; } if ((digits[2] + digits[3] + digits[4]) % 3 != ('0' + '0' + '0') % 3) { return 0; } if (digits[5] != '0' && digits[5] != '5') { return 0; } // we skip the cases is covered above (i < 3) for (auto i = primes.size() - 1; primes[i] > 5; --i) { auto substring_num = digits[i+1] * 100 + digits[i+2] * 10 + digits[i+3] - '0' * 111; if (substring_num % primes[i] != 0) { // failed a test return 0; } } // all tests passed return std::stoul(digits); } int main() { std::string digits = "0123456789"; unsigned long long sum = 0; do { sum += euler43_property(digits); } while ( std::next_permutation(digits.begin(), digits.end()) ); std::cout << sum << "\n"; }