2008-10-23 18 views
2

Est-ce considéré comme un générateur de nombre premier efficace. Il me semble que c'est assez efficace. Est-ce l'utilisation du flux qui rend le programme plus lent?Est-ce que ce générateur primaire est inefficace C++?

Je suis en train de le soumettre à SPOJ et il me dit que ma limite de temps dépassé ...

#include <iostream> 
#include <sstream> 

using namespace std; 

int main() { 
    int testCases, first, second, counter = 0; 
    bool isPrime = true; 
    stringstream out; 

    cin >> testCases; 

    for (int i = 0; i < testCases; i++) { 
     // get the next two numbers 
     cin >> first >> second; 

     if (first%2 == 0) 
      first++; 

     // find the prime numbers between the two given numbers 
     for (int j = first; j <= second; j+=2) { 
      // go through and check if j is prime 
      for (int k = 2; k < j; k++) { 
       if (j%k == 0) { 
        isPrime = false; 
        break; 
       } 
      } 
      if (isPrime) { 
       out << j << "\n"; 
      } 
      isPrime = true; 
     } 
     out << "\n"; 
    } 

    cout << out.str(); 

    return 0; 
} 

EDIT: Le programme est censé générer des nombres premiers entre les chiffres spécifiés dans l'entrée. (Voir ici pour plus de détails: Prime Generator Problem)

-Tomek

Répondre

14

C'est un pas (sauter les nombres pairs) au-dessus de l'algorithme naïf. Je suggère le Sieve Of Eratosthenes comme un algorithme plus efficace. À partir du lien ci-dessus:

La complexité de l'algorithme est O ((nlogn) (loglogn)) avec une exigence mémoire de O (n). La version segmentée du tamis de Eratosthenes, avec des optimisations de base telles que la roue factorisation, utilise O (n) opérations et O (n1/2loglogn/logn) bits de mémoire.

L'algorithme que vous donnez est quelque part près de O (n^2). L'accélération que vous obtenez en sautant Evens n'est pas très bonne parce que vous trouverez un nombre pair ne pas être premier sur le premier test. Le tamis a une mémoire beaucoup plus grande, mais la complexité d'exécution est de loin supérieure pour les grands N.

+0

qui peuvent ne pas être possible dans la mémoire disponible, cependant; mettre en place un tableau de la taille INTMAX puis annulant les éléments inutilisés prennent beaucoup de RAM; cela peut certainement être fait avec une liste chaînée, mais c'est trop lent aussi. – warren

+0

Vous n'avez vraiment besoin que d'un bit par nombre pour implémenter cet algorithme. – Ferruccio

+0

Aux fins de SPOJ, je doute qu'ils fassent en sorte que le second soit si important que la mémoire soit un problème, et s'ils le faisaient, ils seraient probablement assez indulgents avec le temps d'exécution. En règle générale, SPOJ vous obligera à choisir un algorithme efficace, sinon efficace pour la mémoire, à exécuter à temps. –

7

Vous cherchez un lot plus de numéros que vous avez - tout au plus il vous suffit d'aller à <= (sqrt(num)).

+0

Est-ce vrai? Si j'ai besoin de trouver les nombres premiers entre 1 et 9, comment pourrais-je trouver 5 et 7 si je n'ai testé que jusqu'à 3? Pensez-vous à la factorisation? –

+2

Il signifie dans la boucle où 2 <= k

+1

J'ai compris. C'est embarrassant ;-) –

0

Il peut être rendu légèrement plus efficace. Vous n'avez pas besoin de démarrer k à 2, vous vous assurez déjà de ne pas tester les nombres pairs. Donc, commencez k à 3.
Ensuite, incrémenter k par 2 à chaque fois parce que vous n'avez pas besoin de tester d'autres nombres pairs. Le moyen le plus efficace que je peux penser est de tester seulement si un nombre est divisible par des nombres premiers connus (alors quand vous en trouvez un autre, ajoutez-le à la liste avec laquelle vous testez).

+0

Dans la même idée, seulement 6k + 1 et 6k + 5 peuvent être des nombres premiers. –

0
for (int k = 2; k < j; k++) { 
    if (j%k == 0) { 
     isPrime = false; 
     break; 
    } 
} 

devrait être:

for(int k = 3; k <= j/2; k+=2) 
{ 
    if(j % k == 0) 
     break; 
} 

j/2 devrait vraiment être RACINE (j), mais il est généralement assez bonne estimation.

+3

sqrt (j) est bien mieux que j/2, même à un modeste j de 100. –

4

Voici un simple tamis d'ératosthènes. Il ne nécessite pas de prédéclarer un grand tableau booléen, mais c'est toujours >> O (n) dans le temps et l'espace. Cependant, tant que vous avez assez de mémoire, il devrait être nettement plus rapide que votre méthode naïve actuelle.

#include <iostream> 
#include <map> 

using namespace std; 

template<typename T = int, typename M = map<T, T> > 
class prime_iterator { 
    public: 
     prime_iterator() : current(2), skips() { skips[4] = 2; } 
     T operator*() { return current; } 
     prime_iterator &operator++() { 
      typename M::iterator i; 
      while ((i = skips.find(++current)) != skips.end()) { 
       T skip = i->second, next = current + skip; 
       skips.erase(i); 
       for (typename M::iterator j = skips.find(next); 
         j != skips.end(); j = skips.find(next += skip)) {} 
       skips[next] = skip; 
      } 
      skips[current * current] = current; 
      return *this; 
     } 
    private: 
     T current; 
     M skips; 
}; 

int main() { 
    prime_iterator<int> primes; 
    for (; *primes < 1000; ++primes) 
     cout << *primes << endl; 
    return 0; 
} 

Si cela est encore trop lent pour vous, vous pouvez poursuivre le Sieve of Atkin, une optimisation de Eratosthène Sieve. En réalité, ceux-ci ne sont relativement efficaces que si la gamme de nombres premiers à générer commence à être faible.Si la limite inférieure est déjà assez grande et la limite supérieure n'est pas beaucoup plus grande que la limite inférieure, alors les méthodes de tamisage sont un travail inutile et vous feriez mieux d'exécuter un primality test.

+0

+1 Très agréable! Mais ... vous ajoutez une valeur de saut pour un carré de prime dans la carte, quand le prime est vu. Jusqu'à ce qu'un candidat soit considéré comme égal à ce carré, cette information restera inutile. IOW vous avez seulement besoin d'avoir à l'intérieur de la carte les sauts pour les nombres premiers qui sont en dessous de sqrt du candidat actuel. Cela fera ~ O (sqrt (N)) espace. Une autre chose est, current_current conduit à des erreurs pour tout premier au-dessus de 46340; comme conséquence votre code [chiffres 664246 nombres premiers] (http://ideone.com/0fgMu) ci-dessous 10 mil, au lieu de [le correct 664579 nombres premiers] (http://ideone.com/jXoEL). –

3

Et une chose, ne pas utiliser sqrt (n) dans une boucle:

for(int k=1;k<sqrt(n);++k) 

S'il n'y a pas une bonne optimisation, sqrt sera calculée à chaque itération.

Utilisez

for (int k=1;k*k < n;++k) 

Ou tout simplement

int sq = sqrt (n); 
for (int k=1;k<sq;++k)