Как оптимизировать приведенный ниже код для определения общего числа простых чисел ниже 1 000 000 000, если сумма их цифр равна 14 в C?

#c

Вопрос:

/Написать программу для определения общего числа простых чисел ниже 1000 000 000, сумма цифр которых равна 14? Убедитесь, что время выполнения составляет несколько секунд./

 #include<stdio.h>
#include<math.h>

int main() {
  int i, j, count = 0, temp = 0, n, ans = 0, tot = 0;
  for (i = 1; i <= 1000000000; i  ) {
    for (j = 2; j <= i / 2; j  ) {
      if (i % j == 0) {
        count  ;
      }
    }

    if (count == 0) {
      n = i;
      while (n != 0) {
        temp = n % 10;
        n = n / 10;
        ans = ans   temp;
      }
      if (ans == 14) {
        tot  ;
        printf("%d,", i);
      }
      ans = 0;
      temp = 0;
    }
    count = 0;
  }
  // printf("%d:n",tot);
  return 0;
}
 

Комментарии:

1. Поместите самые сложные (большинство итераций) for циклы внутрь, если это возможно.

Ответ №1:

Два простых улучшения (среди прочего):

1: Вместо того , чтобы повторять i/2 , повторяйте до квадратного корня из i — то есть j*j <= i .** Это огромное ускорение.

2: Завершите цикл, как только фактор найден.

 // for(j=2;j<=i/2;j  ) {
//  if(i%j==0) {
//    count  ;
//  }
//}
for(j=2;j<=i/j;j  ) { // _much_ lower limit
  if(i%j==0) {
    count  ;
    break; // No need to find more factors: `i` is not a prime.
  }
}
 

Функциональность: Внутри if(count==0) , я бы ожидал ans == 0 раньше while(n!=0) .


** Используйте j<=i/j для предотвращения переполнения. Хороший компилятор увидит поблизости i%j и часто выполнит и i/j, i%j то, и другое за счет временных затрат.

Ответ №2:

Функция суммирования цифр также может использовать раннее возвращение, например:

    int dsum14(int n) {
        int sum = 0;
        for (; n; n /= 10)
            if ((sum  = n % 10) > 14)
                return 0;
        return sum == 14 ? 1 : 0;
    }
 

Но как совместить (эффективный) простой поиск и это условие суммы?

 int n, cnt = 0;
for (n = 3; n < 1000*1000*1000; n  = 2)
    if (n%3 amp;amp; n%5 amp;amp; dsum14(n) amp;amp; n%7 amp;amp; n amp;amp; n)
        cnt  ;
 

Это дает 77469 результат за 1,5 секунды. При dsum() этом на обоих концах логической цепочки она почти удваивается.

amp;amp; n%7 amp;amp; n amp;amp; n Часть будет заменена функцией, использующей список простых чисел примерно до 32000 (квадратный корень из макс.).


…или вы можете оптимизировать его до 0,1 секунды, настроив функцию digsum.

Существует «всего» 575 трехзначных чисел 000-999 с суммой 14 или менее. Поэтому я готовлю их и объединяю три из них, чтобы получить 9-значное число. Генерировать их вместо фильтрации.

Хвост выглядит как:

 920000021
920000201
920001011
920010011
920100011
920100101
920101001
921001001
931000001
total count: 22588

real    0m0.098s
user    0m0.100s
sys     0m0.002s
 

И начало:

 59
149
167
239
257
293
347
383
419
 

Не на 100% уверен, что это правильно, но общее количество также кажется разумным.

Все это зависит от заданного максимума в 1000 миллионов. digsum_prime() использует его для построения номера кандидата из трех (почти) равных частей.

Код:

 #include <stdio.h>
#include <stdlib.h>

int parr[5000] = {3};

struct {
    int tri, sum;
} ts[999]; 

void primarr(void) {
    int maxn = 32000;
    int i = 1;
    for (int n = 5; n < maxn; n  = 2)
        for (int div = 3;; div  = 2) {
            if (!(n % div))
                break;
            if (div*div > n) {
                parr[i  ] = n;
                break;
            }
        }    
}

int isprime(int n) {
    for(int i = 0;; i  ) {
        if (!(n % parr[i]))
            return 0;
        if (parr[i]*parr[i] > n)
            return 1;
    }
}

int dsum(int n) {
    int sum = 0;
    for (; n; n /= 10) 
        sum  = n % 10;
    return sum;
}
int tsarr(void) {
    int i = 0;
    for (int n = 0; n < 1000; n  ) {
        int digsum = dsum(n);
        if (digsum <= 14) {
            ts[i].tri = n;
            ts[i].sum = digsum;
            i  ;
        }
    }    
    return i;
}

int digsum_prime() {

    int cnt = 0;
    int tslen = tsarr();
    printf("tslen: %dn", tslen);

    int high, mid, low;
    int sum, num;

    for (high = 0; high < tslen; high  ) { 

        if(ts[high].sum > 13)
            continue;
        for (mid = 0; mid < tslen; mid  ) { 

            if(ts[mid].sum   ts[high].sum > 13)
                continue;
            sum = ts[mid].sum   ts[high].sum;

            for (low = 0; low < tslen; low  )   
                if (ts[low].tri % 2) 
                    if(ts[low].sum   sum == 14) {

                        num = ts[high].tri * 1000*1000 
                              ts[mid] .tri * 1000 
                              ts[low] .tri;

                        if (isprime(num)) {
                            cnt  ;        
                            printf("%dn", num);
                        }
                    }
        }
    }
    
    return cnt; 
}

int main(void) {
    primarr();
    printf("total count: %dn", digsum_prime());

}
 

Изменение 13-13-14 на 3-3-4 (но та же часть подготовки) дает обзор — за 0,005 с!

 tslen: 575
13
31
103
211
1021
1201
2011
3001
10111
20011
20101
21001
100003
102001
1000003
1011001
1020001
1100101
2100001
10010101
10100011
20001001
30000001
101001001
200001001
total count: 25

real    0m0.005s
user    0m0.005s
sys     0m0.000s
 

Make sure the execution time is few seconds.

oops

But the limits of OP are well chosen: a naive approach takes several seconds.