I came across this question.A number is called lucky if the sum of its digits, as well as the sum of the squares of its digits is a prime number. How many numbers between A
I was trying to come up with a solution using Pierre's enumeration method, but never came up with a sufficiently fast way to count the permutations. OleGG's counting method is very clever, and pirate's optimizations are necessary to make it fast enough. I came up with one minor improvement, and one workaround to a serious problem.
First, the improvement: you don't have to step through all the sums and squaresums one by one checking for primes in pirate's j and k loops. You have (or can easily generate) a list of primes. If you use the other variables to figure out which primes are in range, you can just step through the list of suitable primes for sum and squaresum. An array of primes and a lookup table to quickly determine at which index the prime >= a number is at is helpful. However, this is probably only a fairly minor improvement.
The big issue is with pirate's ans cache array. It is not 45MB as claimed; with 64 bit entries, it is something like 364MB. This is outside the (current) allowed memory limits for C and Java. It can be reduced to 37MB by getting rid of the "l" dimension, which is unnecessary and hurts cache performance anyway. You're really interested in caching counts for l + sum and l*l + squaresum, not l, sum, and squaresum individually.
You should use DP for this task. Here is my solution:
#include <stdio.h>
const int MAX_LENGTH = 18;
const int MAX_SUM = 162;
const int MAX_SQUARE_SUM = 1458;
int primes[1459];
long long dyn_table[19][163][1459];
void gen_primes() {
for (int i = 0; i <= MAX_SQUARE_SUM; ++i) {
primes[i] = 1;
}
primes[0] = primes[1] = 0;
for (int i = 2; i * i <= MAX_SQUARE_SUM; ++i) {
if (!primes[i]) {
continue;
}
for (int j = 2; i * j <= MAX_SQUARE_SUM; ++j) {
primes[i*j] = 0;
}
}
}
void gen_table() {
for (int i = 0; i <= MAX_LENGTH; ++i) {
for (int j = 0; j <= MAX_SUM; ++j) {
for (int k = 0; k <= MAX_SQUARE_SUM; ++k) {
dyn_table[i][j][k] = 0;
}
}
}
dyn_table[0][0][0] = 1;
for (int i = 0; i < MAX_LENGTH; ++i) {
for (int j = 0; j <= 9 * i; ++j) {
for (int k = 0; k <= 9 * 9 * i; ++k) {
for (int l = 0; l < 10; ++l) {
dyn_table[i + 1][j + l][k + l*l] += dyn_table[i][j][k];
}
}
}
}
}
long long count_lucky (long long max) {
long long result = 0;
int len = 0;
int split_max[MAX_LENGTH];
while (max) {
split_max[len] = max % 10;
max /= 10;
++len;
}
int sum = 0;
int sq_sum = 0;
for (int i = len-1; i >= 0; --i) {
long long step_result = 0;
for (int l = 0; l < split_max[i]; ++l) {
for (int j = 0; j <= 9 * i; ++j) {
for (int k = 0; k <= 9 * 9 * i; ++k) {
if (primes[j + l + sum] && primes[k + l*l + sq_sum]) {
step_result += dyn_table[i][j][k];
}
}
}
}
result += step_result;
sum += split_max[i];
sq_sum += split_max[i] * split_max[i];
}
if (primes[sum] && primes[sq_sum]) {
++result;
}
return result;
}
int main(int argc, char** argv) {
gen_primes();
gen_table();
int cases = 0;
scanf("%d", &cases);
for (int i = 0; i < cases; ++i) {
long long a, b;
scanf("%lld %lld", &a, &b);
printf("%lld\n", count_lucky(b) - count_lucky(a-1));
}
return 0;
}
Brief explanation:
That's all. Precalculation work for O(log(MAX_NUMBER)^3), each step have also this complexity.
I've tested my solution against linear straightforward one and results were equal
I have just solved this problem.DP[n](sum-square_sum)
as the DP function, and DP[n](sum-square_sum)
is the count of all of the numbers whose digits is less than or equal to n, with the sum and square_sum of digits of the number is respectively represented by sum and square_sum. For example:
DP[1](1-1) = 1 # only 1 satisfies the condition
DP[2](1-1) = 2 # both 1 and 10 satisfies the condition
DP[3](1-1) = 3 # 1 10 100
DP[3](2-4) = 3 # 11 110 101
Since we can easily figure out the first DP state DP[1][..][..], it is:
(0-0) => 1 (1-1) => 1 (2-4) => 1 (3-9) => 1 (4-16) => 1
(5-25) => 1 (6-36) => 1 (7-49) => 1 (8-64) => 1 (9-81) => 1
then we can deduce DP[1] from DP[1], and then DP[3] ... DP[18] the deduce above is made by the fact that every time when n increase by 1, for example from DP[1] to DP[2], we got a new digit (0..9), and the set of (sum, square_sum) pair (i.e. DP[n]) must be updated.
Finally, we can traverse the DP[18] set and count of the numbers that are lucky.
Well, how about the time and space complexity of the algorithm above? As we know sum <= 18*9=162, square_sum <= 18*9*9 = 1458, so the set of (sum, square_sum) pair (i.e. DP[n]) is very small, less than 162*1458=236196, in fact it's much smaller than 236196; The fact is: my ruby program counting all the lucky numbers between 0 and 10^18 finishes in less than 1s.
ruby lucky_numbers.rb 0.55s user 0.00s system 99% cpu 0.556 total
and I test my program by writing a test function using brute force algorithm, and it's right for numbers less than 10^7 .
First I would like to add that a lucky number can be calculated by a sieve, the explanation of the sieve can be found here: http://en.wikipedia.org/wiki/Lucky_number
so you can improve your solution speed using a sieve to determine the numbers,