Подтвердить что ты не робот

Алгоритм поиска Lucky Numbers

Я столкнулся с этим вопросом. Число называется счастливым, если сумма его цифр, а также сумма квадратов его цифр - простое число. Сколько чисел между A и B повезло? 1 <= A <= B <= 10 18. Я попробовал это.

  • Сначала я породил все возможные простые числа между 1 и числом, которое может быть получено путем суммирования квадратов (81 * 18 = 1458).

  • Я прочитал в и B, чтобы узнать максимальное число, которое может быть сгенерировано путем суммирования цифр. Если B является 2-значным номером (максимальное число равно 18, генерируемому на 99).

  • Для каждого простого числа от 1 до максимального числа. Я применил целочисленный алгоритм разделения.

  • Для каждого возможного раздела я проверил, является ли их сумма квадратов их цифр простой. Если это так, то возможны возможные перестановки этого раздела, и если они лежат в диапазоне, это удачные числа.

Это реализация:

#include<stdio.h>
#include<malloc.h>
#include<math.h>
#include <stdlib.h>
#include<string.h>
long long luckynumbers;
int primelist[1500];

int checklucky(long long possible,long long a,long long b){
    int prime =0;
    while(possible>0){
            prime+=pow((possible%10),(float)2);
            possible/=10;
    }
        if(primelist[prime]) return 1;
        else return 0;
}
long long getmax(int numdigits){
        if(numdigits == 0) return 1; 
        long long maxnum =10;
             while(numdigits>1){
                        maxnum = maxnum *10;
                        numdigits-=1;
             }
         return maxnum; 

}
void permuteandcheck(char *topermute,int d,long long a,long long b,int digits){
    if(d == strlen(topermute)){
            long long possible=atoll(topermute);
            if(possible >= getmax(strlen(topermute)-1)){  // to skip the case of getting already read numbers like 21 and 021(permuted-210

                if(possible >= a && possible <= b){

                    luckynumbers++;
                }
            }
    }
    else{
        char lastswap ='\0';
        int i;
        char temp;
        for(i=d;i<strlen(topermute);i++){
            if(lastswap == topermute[i])
                continue;
            else
                lastswap = topermute[i];
            temp = topermute[d];
            topermute[d] = topermute[i];
            topermute[i] = temp;

            permuteandcheck(topermute,d+1,a,b,digits);

            temp = topermute[d];
            topermute[d] = topermute[i];
            topermute[i] = temp;
        }

    }

}


void findlucky(long long possible,long long a,long long b,int digits){
    int i =0;
    if(checklucky(possible,a,b)){
        char topermute[18];
        sprintf(topermute,"%lld",possible);
        permuteandcheck(topermute,0,a,b,digits);
    }
}


void  partitiongenerator(int k,int n,int numdigits,long long  possible,long long a,long long b,int digits){
    if(k > n || numdigits > digits-1 || k > 9) return;
    if(k == n){

        possible+=(k*getmax(numdigits));

        findlucky(possible,a,b,digits);
        return;
    }
    partitiongenerator(k,n-k,numdigits+1,(possible + k*getmax(numdigits)),a,b,digits);
    partitiongenerator(k+1,n,numdigits,possible,a,b,digits);

}


void calcluckynumbers(long long a,long long b){
    int i;
    int numdigits = 0;
    long long temp = b;
    while(temp > 0){
        numdigits++;
        temp/=10;
    }

    long long maxnum =getmax(numdigits)-1;
    int maxprime=0,minprime =0;
    temp = maxnum;
    while(temp>0){
        maxprime+=(temp%10);
        temp/=10;
    }
    int start = 2;
    for(;start <= maxprime ;start++){
            if(primelist[start]) {
                partitiongenerator(0,start,0,0,a,b,numdigits);
            }
    }   

}   
void generateprime(){
    int i = 0;
    for(i=0;i<1500;i++)
        primelist[i] = 1;
    primelist[0] = 0;
    primelist[1] = 0;
    int candidate = 2;
    int topCandidate = 1499;
    int thisFactor = 2;
    while(thisFactor * thisFactor <= topCandidate){
        int  mark = thisFactor + thisFactor;
        while(mark <= topCandidate){
            *(primelist + mark) = 0;
            mark += thisFactor;
        }
        thisFactor++;
        while(thisFactor <= topCandidate && *(primelist+thisFactor) == 0) thisFactor++;
    }

}
int main(){
        char input[100];
        int cases=0,casedone=0;
    long long a,b;
    generateprime();
        fscanf(stdin,"%d",&cases);
        while(casedone < cases){
        luckynumbers = 0;
                fscanf(stdin,"%lld %lld",&a,&b);
        int i =0;
               calcluckynumbers(a,b);
                casedone++;
        }

}

Алгоритм слишком медленный. Я думаю, что ответ можно найти на основе свойства чисел. Поделитесь своими мыслями. Спасибо.

4b9b3361

Ответ 1

Отличное решение OleGG, но ваш код не оптимизирован. Я внесла следующие изменения в ваш код,

  • Не нужно проходить через 9 * 9 * я для k в функции count_lucky, потому что для 10000 случаев это будет работать много раз, вместо этого я уменьшил это значение до начала и конца.

  • Я использовал массив ans для хранения промежуточных результатов. Это может выглядеть не так много, но более 10000 случаев это основной фактор, который сокращает время.

Я проверил этот код и передал все тестовые примеры. Вот модифицированный код:

    #include <stdio.h>

    const int MAX_LENGTH = 18;
    const int MAX_SUM = 162;
    const int MAX_SQUARE_SUM = 1458;
    int primes[1460];
    unsigned long long dyn_table[20][164][1460];
    //changed here.......1
    unsigned long long ans[19][10][164][1460];  //about 45 MB

    int start[19][163];
    int end[19][163];
    //upto here.........1
    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];
                    }
                }
            }
        }
    }

    unsigned long long count_lucky (unsigned long long maxp) {
        unsigned long long result = 0;
        int len = 0;
        int split_max[MAX_LENGTH];
        while (maxp) {
            split_max[len] = maxp % 10;
            maxp /= 10;
            ++len;
        }
        int sum = 0;
        int sq_sum = 0;
        unsigned long long step_result;
        unsigned long long step_;
        for (int i = len-1; i >= 0; --i) {
            step_result = 0;
            int x1 = 9*i;
            for (int l = 0; l < split_max[i]; ++l) {
    //changed here........2
                step_ = 0;
                if(ans[i][l][sum][sq_sum]!=0)
                    {
                        step_result +=ans[i][l][sum][sq_sum];
                        continue;
                    }
                int y = l + sum;
                int x = l*l + sq_sum;
                for (int j = 0; j <= x1; ++j) {
                    if(primes[j + y])
                        for (int k=start[i][j]; k<=end[i][j]; ++k) {
                            if (primes[k + x]) {
                                step_result += dyn_table[i][j][k];
                                step_+=dyn_table[i][j][k];
                            }
                    }

                }
                 ans[i][l][sum][sq_sum] = step_;
    //upto here...............2
            }
            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();

    //changed here..........3
        for(int i=0;i<=18;i++)
            for(int j=0;j<=163;j++)
                {
                    for(int k=0;k<=1458;k++)
                            if(dyn_table[i][j][k]!=0ll)
                                {
                                    start[i][j] = k;
                                    break;                               
                                }

                    for(int k=1460;k>=0;k--)
                            if(dyn_table[i][j][k]!=0ll)
                                {
                                    end[i][j]=k;
                                    break;                               
                                }
                }
    //upto here..........3
        int cases = 0;
        scanf("%d",&cases);
        for (int i = 0; i < cases; ++i) {
            unsigned long long a, b;

            scanf("%lld %lld", &a, &b);
    //changed here......4
            if(b == 1000000000000000000ll)
                b--;
    //upto here.........4
            printf("%lld\n", count_lucky(b) - count_lucky(a-1));
        }
        return 0;

}

Пояснение:

gen_primes() и gen_table() довольно понятны себе.

count_lucky() работает следующим образом:

разделите число в split_max [], просто сохранив однозначное число для одной, десятки, сотен и т.д. позиций. Идея такова: предположим split_map [2] = 7, поэтому нам нужно вычислить результат для

1 в сотнях позиций и все от 00 до 99.

2 в сотне позиции и все от 00 до 99.

. .

7 в сотне позиции и все от 00 до 99.

это фактически выполняется (в l-цикле) в терминах суммы цифр и суммы квадрата цифр, которые были предварительно скошены. для этого примера: сумма будет варьироваться от 0 до 9 * i, а сумма квадрата будет варьироваться от 0 до 9 * 9 * i... это делается в j и k петлях. Это повторяется для всех длин в я loop

Это была идея OleGG.

Для оптимизации следует следующее:

  • бесполезно запускать сумму квадратов от 0 до 9 * 9 * i, так как для частных сумм цифр она не будет соответствовать всему диапазону. Например, если я = 3 и сумма равна 5, то сумма квадрата не будет меняться от 0 до 9 * 9 * 3. Эта часть хранится в массивах start [] и end [] с использованием предварительно вычисленных значений.

  • значение для определенного количества цифр и отдельных цифр в самом значительном положении числа и до определенной суммы и до определенной суммы квадрата хранится для запоминания. Слишком долго, но все же около 45 МБ. Я считаю, что это можно было бы оптимизировать.

Ответ 2

Вы должны использовать DP для этой задачи. Вот мое решение:

#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;
}

Краткое объяснение:

  • Я вычисляю все простые числа до 9 * 9 * MAX_LENGTH с использованием метода Eratosthenes;
  • Позже, используя DP, я строю таблицу dyn_table, где значение X в dyn_table [i] [j] [k] означает, что мы имеем ровно X: длина строки i с суммой цифр, равной j, а сумма ее квадратов равна k
  • Тогда мы можем легко подсчитать количество счастливых чисел от 1 до 999..999 ( len раз 9). Для этого мы просто суммируем все dyn_table [len] [j] [k], где оба j и k являются штрихами.
  • Чтобы вычислить количество счастливых чисел от 1 до случайного X, мы разделим интервал от 1 до X на интервалы с длиной, равной 10 ^ K (см. функцию * count_lucky *).
  • И наш последний шаг - вычесть count_lucky (a-1) (потому что мы включаем a в нашем интервале) из count_lucky (b).

Это все. Предварительная калькуляция для O (log (MAX_NUMBER) ^ 3), каждый шаг также имеет такую ​​сложность.

Я тестировал свое решение против линейного прямого результата, и результаты были равны

Ответ 3

Вместо перечисления пространства чисел перечислите разные "подписи" чисел, которым повезло. и затем распечатать все их комбинации differnet.

Это можно сделать с помощью тривиального возврата:

#define _GNU_SOURCE
#include <assert.h>
#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>

#define bitsizeof(e)   (CHAR_BIT * sizeof(e))
#define countof(e)     (sizeof(e) / sizeof((e)[0]))
#define BITMASK_NTH(type_t, n) ( ((type_t)1) << ((n) & (bitsizeof(type_t) - 1)))
#define OP_BIT(bits, n, shift, op) \
    ((bits)[(unsigned)(n) / (shift)] op BITMASK_NTH(typeof(*(bits)), n))
#define TST_BIT(bits, n)    OP_BIT(bits, n, bitsizeof(*(bits)), &  )
#define SET_BIT(bits, n)    (void)OP_BIT(bits, n, bitsizeof(*(bits)), |= )

/* fast is_prime {{{ */

static uint32_t primes_below_1M[(1U << 20) / bitsizeof(uint32_t)];

static void compute_primes_below_1M(void)
{
    SET_BIT(primes_below_1M, 0);
    SET_BIT(primes_below_1M, 1);
    for (uint32_t i = 2; i < bitsizeof(primes_below_1M); i++) {
        if (TST_BIT(primes_below_1M, i))
            continue;
        for (uint32_t j = i * 2; j < bitsizeof(primes_below_1M); j += i) {
            SET_BIT(primes_below_1M, j);
        }
    }
}

static bool is_prime(uint64_t n)
{
    assert (n < bitsizeof(primes_below_1M));
    return !TST_BIT(primes_below_1M, n);
}

/* }}} */

static uint32_t prime_checks, found;

static char     sig[10];
static uint32_t sum, square_sum;

static void backtrack(int startdigit, int ndigits, int maxdigit)
{
    ndigits++;

    for (int i = startdigit; i <= maxdigit; i++) {
        sig[i]++;
        sum        += i;
        square_sum += i * i;
        prime_checks++;
        if (is_prime(sum) && is_prime(square_sum)) {
                found++;
        }
        if (ndigits < 18)
            backtrack(0, ndigits, i);
        sig[i]--;
        sum        -= i;
        square_sum -= i * i;
    }
}

int main(void)
{
    compute_primes_below_1M();
    backtrack(1, 0, 9);

    printf("did %d signature checks, found %d lucky signatures\n",
           prime_checks, found);
    return 0;
}

Когда я его запускаю, он делает:


$ time ./lucky
did 13123091 signature checks, found 933553 lucky signatures
./lucky  0.20s user 0.00s system 99% cpu 0.201 total

Вместо найденного ++ вы хотите сгенерировать все различные перестановки цифр, которые вы можете построить с этим номером. Я также предкоммутирую первые 1М простых чисел.

Я не проверял правильность кода на 100%, возможно, вам придется немного отладить его. Но здесь есть шероховатая идея, и я могу создать всю удачную перестановку ниже 0,2 с (даже без ошибок она не должна быть более чем в два раза медленнее).

И, конечно, вы хотите сгенерировать перестановки, которые проверяют A <= B. Возможно, вы захотите проигнорировать генерирующие разделы, у которых больше цифр, чем B или меньше A. В любом случае, вы можете улучшить мою общую идею отсюда.

(Примечание: рекламный ролик в начале - это потому, что я вырезал и вставлял код, который я написал для эйлера проекта, поэтому очень быстрый is_prime, который работает для N <= 1M;))

Ответ 4

Для тех, кто уже не знал, это проблема на сайте InterviewStreet.com(и, на мой взгляд, самая сложная из них). Мой подход начался аналогично (и был вдохновлен) OleGG ниже. Однако, создав первую таблицу [19] [163] [1459], которую он сделал (которую я назвал table1), я пошел в несколько другом направлении. Я создал вторую таблицу оборванной длины [19] [x] [3] (таблица2), где x - количество уникальных пар сумм для соответствующего количества цифр. И для третьего измерения таблицы, с длиной 3, 1-й элемент представляет собой количество уникальных "пар сумм" с суммами и значениями squareSum, удерживаемыми 2-м и 3-м элементами.

Например:

//pseudocode

table2[1] = new long[10][3]
table2[1] = {{1, 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}}

table2[2] = new long[55][3]
table2[3] = new long[204][3]
table2[4] = new long[518][3]
    .
    .
    .
    .
table2[17] = new long[15552][3]
table2[18] = new long[17547][3]

Число, которое у меня есть для второй размерной длины массива (10, 55, 204, 518,..., 15552, 17547), может быть проверено путем запроса таблицы1, и аналогичным образом таблица2 может быть заполнена. Теперь, используя таблицу 2, мы можем решить большие "удачливые" запросы намного быстрее, чем метод OleGG, хотя все еще использует подобный "расщепленный" процесс, как и он. Например, если вам нужно найти удачу (00000-54321) (т.е. Удачливые номера от 0 до 54321), он разбивается на сумму следующих 5 строк:

lucky(00000-54321) = {
    lucky(00000-49999) +
    lucky(50000-53999) +
    lucky(54000-54299) +
    lucky(54300-53319) +
    lucky(54320-54321)
}

Что дальше:

lucky(00000-49999) = {
    lucky(00000-09999) +
    lucky(10000-19999) +
    lucky(20000-29999) +
    lucky(30000-39999) +
    lucky(40000-49999)
}
    .
    .
lucky(54000-54299) = {
    lucky(54000-54099) +
    lucky(54100-54199) +
    lucky(54200-54299)
}
    .
    .
    .
    etc

Каждое из этих значений можно легко получить, запросив таблицу2. Например, удачный (40000-49999) найден путем добавления 4 и 16 к 2-му и 3-му элементам таблицы третьего измерения2:

sum = 0
for (i = 0; i < 518; i++)
    if (isPrime[table2[4][i][1] + 4] && isPrime[table2[4][i][2] + 4*4])
        sum += table2[4][i][0]
return sum

Или для удачливого (54200-54299):

sum = 0
for (i = 0; i < 55; i++)
    if (isPrime[table2[2][i][1] + (5+4+2)]
    && isPrime[table2[2][i][2] + (5*5+4*4+2*2)])
        sum += table2[2][i][0]
return sum

Теперь решение OleGG выполнялось значительно быстрее, чем все, что я пробовал до тех пор, но с моими модификациями, описанными выше, он работает даже лучше, чем раньше (примерно в 100 раз для большого набора тестов). Тем не менее, он все еще не достаточно быстро подходит для слепых тестов, которые даются на интервью. Через какой-то умный взлом я смог определить, что я в настоящее время работает около 20 раз слишком медленно, чтобы завершить свой тестовый набор в назначенное время. Однако я не могу найти дальнейших оптимизаций. Самое большое время здесь - это, очевидно, повторение второго измерения таблицы2, и единственным способом избежать этого было бы суммировать результаты этих сумм. Однако есть слишком много возможностей вычислить их все за указанное время (5 секунд) или сохранить их все в указанном пространстве (256 МБ). Например, удачный (54200-54299) цикл выше может быть предварительно вычислен и сохранен как одно значение, но если бы это было так, нам также нужно было предварительно вычислить удачу (123000200-123000299) и повезло (99999200-99999299) ) и т.д. Я сделал математику, и это слишком много вычислений, чтобы предварительно вычислить.

Ответ 5

Я только что решил эту проблему.

Это просто проблема динамического программирования. Возьмите DP[n](sum-square_sum) как функцию DP, а DP[n](sum-square_sum) - это счет всех чисел, цифры которых меньше или равны n, причем сумма и квадрат_сума цифр числа соответственно представлены суммой и квадратом. Например:
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

Поскольку мы можем легко вычислить первое DP-состояние DP [1] [..] [..], это:

(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

то мы можем вывести DP [1] из DP [1], а затем DP [3]... DP [18] выведенное выше сделано тем, что каждый раз, когда n увеличивается на 1, например, от DP [1] до DP [2], мы получили новую цифру (0..9), а набор (sum, square_sum ) (т.е. DP [n]) должны быть обновлены.

Наконец, мы можем пересечь набор DP [18] и количество чисел, которым повезло.

Ну, а как насчет сложности времени и пространства алгоритма выше? Как известно, сумма <= 18 * 9 = 162, square_sum <= 18 * 9 * 9 = 1458, поэтому набор (сумма, квадрат_сум) пары (т.е. DP [n]) очень мала, меньше 162 * 1458 = 236196, на самом деле она намного меньше 236196; Дело в том, что моя рубиновая программа, подсчитывающая все удачные номера от 0 до 10 ^ 18, заканчивается менее чем за 1 с.

ruby lucky_numbers.rb  0.55s user 0.00s system 99% cpu 0.556 total

и я тестирую свою программу, написав тестовую функцию с использованием алгоритма грубой силы, и это правильно для чисел меньше 10 ^ 7.

Ответ 6

Исходя из требований, вы можете сделать это по-разному. Если бы я это делал, я бы вычислил простые числа, используя "Сито Eratosthenes" в требуемом диапазоне (от A до (9 * 2) * B.length), кешируйте их (опять же, в зависимости от вашей установки, вы можете использовать в -memory или disk cache) и использовать его для следующего прогона.

Я просто закодировал быстрое решение (Java), как показано ниже ( ПРИМЕЧАНИЕ: переполнение целых чисел не проверено. Просто быстрый пример. Также мой код не оптимизирован.):

import java.util.ArrayList;
import java.util.Arrays;

public class LuckyNumbers {
    public static void main(String[] args) {
        int a = 0, b = 1000;
        LuckyNumbers luckyNums = new LuckyNumbers();
        ArrayList<Integer> luckyList = luckyNums.findLuckyNums(a, b);
        System.out.println(luckyList);
    }

    private ArrayList<Integer> findLuckyNums(int a, int b) {
        ArrayList<Integer> luckyList = new ArrayList<Integer>();
        int size = ("" + b).length();        
        int maxNum = 81 * 4; //9*2*b.length() - 9 is used, coz it the max digit
        System.out.println("Size : " + size + " MaxNum : " + maxNum);
        boolean[] primeArray = sieve(maxNum);

        for(int i=a;i<=b;i++) {
            String num = "" + i;
            int sumDigits = 0;
            int sumSquareDigits = 0;

            for(int j=0;j<num.length();j++) {
                int digit = Integer.valueOf("" + num.charAt(j));
                sumDigits += digit;
                sumSquareDigits += Math.pow(digit, 2);
            }

            if(primeArray[sumDigits] && primeArray[sumSquareDigits]) {
                luckyList.add(i);
            }
        }

        return luckyList;
    }

    private boolean[] sieve(int n) {
        boolean[] prime = new boolean[n + 1];
        Arrays.fill(prime, true);
        prime[0] = false;
        prime[1] = false;
        int m = (int) Math.sqrt(n);

        for (int i = 2; i <= m; i++) {
            if (prime[i]) {
                for (int k = i * i; k <= n; k += i) {
                    prime[k] = false;
                }
            }
        }

        return prime;
    }
}

И результат был:

[11, 12, 14, 16, 21, 23, 25, 32, 38, 41, 49, 52, 56, 58, 61, 65, 83, 85, 94, 101, 102, 104, 106, 110, 111, 113, 119, 120, 131, 133, 137, 140, 146, 160, 164, 166, 173, 179, 191, 197, 199, 201, 203, 205, 210, 223, 232, 250, 289, 292, 298, 302, 308, 311, 313, 317, 320, 322, 331, 335, 337, 344, 346, 353, 355, 364, 368, 371, 373, 377, 380, 386, 388, 397, 401, 409, 410, 416, 434, 436, 443, 449, 461, 463, 467, 476, 490, 494, 502, 506, 508, 520, 533, 535, 553, 559, 560, 566, 580, 595, 601, 605, 610, 614, 616, 634, 638, 641, 643, 647, 650, 656, 661, 665, 674, 683, 689, 698, 713, 731, 733, 737, 739, 746, 764, 773, 779, 791, 793, 797, 803, 805, 829, 830, 836, 838, 850, 863, 869, 883, 892, 917, 919, 922, 928, 937, 940, 944, 955, 968, 971, 973, 977, 982, 986, 991]

Ответ 7

Я не тщательно проанализировал ваше текущее решение, но это может улучшить его:

Так как порядок цифр не имеет значения, вы должны пройти все возможные комбинации цифр 0-9 от 1 до 18, отслеживая сумму цифр и их квадратов и добавляя по одной цифре за раз, используя результат предыдущего расчета.

Итак, если вы знаете, что за 12 сумм цифр - 3, а квадратов - 5, посмотрите на цифры 120, 121, 122... и т.д. И рассчитывать суммы для них тривиально от 3 и 5 для 12.

Ответ 8

Иногда самое быстрое решение невероятно просто:

uint8_t precomputedBitField[] = {
    ...
};

bool is_lucky(int number) {
    return precomputedBitField[number >> 8] & (1 << (number & 7));
}

Просто измените существующий код, чтобы создать "precomputedBitField".

Если вы беспокоитесь о размере, чтобы покрыть все числа от 0 до 999, это будет стоить вам всего 125 байт, поэтому этот метод, вероятно, будет меньше (и намного быстрее), чем любая другая альтернатива.

Ответ 9

Я пытался найти решение с использованием метода перечисления Пьера, но никогда не придумывал достаточно быстрый способ подсчета перестановок. Метод подсчета OleGG очень умный, и оптимизация пиратов необходима, чтобы сделать его достаточно быстрым. Я придумал одно небольшое усовершенствование, и один способ решения серьезной проблемы.

Во-первых, улучшение: вам не нужно проходить через все суммы и квадраты один за другим, проверяя простые числа в пиратских j и k петлях. Вы имеете (или можете легко генерировать) список простых чисел. Если вы используете другие переменные, чтобы выяснить, какие простые числа находятся в диапазоне, вы можете просто перечислить список подходящих простых чисел для суммы и квадратов. Массив простых чисел и таблица поиска, чтобы быстро определить, при каком индексе простое число = = число является полезным. Однако это, вероятно, лишь незначительное улучшение.

Большая проблема связана с массивом кеша pirate ans. Это не 45 МБ, как утверждается; с 64-битными вводами, это что-то вроде 364 МБ. Это за пределами допустимых пределов памяти для C и Java. Его можно уменьшить до 37 МБ, избавившись от измерения "l", что не является необходимым и в любом случае ухудшает производительность кеша. Вы действительно заинтересованы в кешировании count для l + sum и l * l + squaresum, а не l, sum и squaresum индивидуально.

Ответ 10

Сначала я хотел бы добавить, что счастливое число может быть рассчитано ситом, объяснение сита можно найти здесь: http://en.wikipedia.org/wiki/Lucky_number

чтобы вы могли улучшить скорость своего решения, используя сито для определения чисел,