Quel est l'algorithme le plus rapide pour trouver des nombres premiers?

184

Quel est l'algorithme le plus rapide pour trouver des nombres premiers en utilisant C ++? J'ai utilisé l'algorithme de tamis mais je veux toujours qu'il soit plus rapide!

Kasperasky
la source
Un vieil article que j'ai trouvé, mais qui a l'air intéressant: Fun With Prime Numbers
Mvcoile
29
@Jaider cela échoue pour des nombres aussi bas que 7 (111). Il échoue également pour 1001 = 9. Et clairement cela échoue pour presque tous les nombres premiers en général (ne couvre pas le cas 2 ^ p - 1, qui sont des nombres premiers de Mersenne - exemples générés classiquement - qui seront toujours de la forme 111 ... 1)
Daniel Kats
1
@Kasperasky - Vous n'avez pas mentionné quel tamis? Vous voulez probablement dire Tamis d'Eranthoses!
user2618142
Algorithme Sieve of Eratosthenes
Emad Aghayi

Réponses:

79

Une mise en œuvre très rapide de la Crible d'Atkin est de Dan Bernstein de primegen . Ce tamis est plus efficace que le tamis d'Eratosthène . Sa page contient des informations de référence.

Greg Hewgill
la source
10
En fait, je ne pense pas que primegen soit le plus rapide, ni même le deuxième plus rapide; yafu et primesieve sont tous les deux plus rapides en général, je pense, et certainement plus de 2 ^ 32. Les deux sont des tamis (modifiés) d'Eratosthène plutôt que du tamis Atkin-Bernstein.
Charles
6
Primesieve Sieve of Eratosthenes (SoE) est l'algorithme le plus rapide possible et sera toujours plus rapide que toute implémentation du Sieve d'Atkin SoA, y compris celui de Bernstein comme lié dans cette réponse car primesieve réduit le nombre d'opérations par rapport à SoA: Pour le 32- plage de nombres de bits (2 ^ 32 - 1), primesieve effectue environ 1,2 milliard d'abattages tandis que SoA effectue un total d'environ 1,4 milliard d'opérations combinées bascule et carré libre, les deux opérations étant à peu près de la même complexité et pouvant être optimisées à peu près de la même manière façon.
GordonBGood
7
Suite: Bernstein a seulement comparé le SoE en utilisant la même factorisation de roue effective que pour le SoA, qui est une roue 2; 3; 5, dont l'utilisation entraîne environ 1,83 milliard d'abattages sur la plage de nombres de 32 bits; cela rend le SoA environ 30% plus rapide lorsque l'on compare cette version restreinte de SoE pour d'autres optimisations équivalentes. Cependant, l'algorithme primesieve utilise une roue 2; 3; 5; 7 en combinaison avec un segment de roue 2; 3; 5; 7; 11; 13; 17 pré-abattage pour réduire le nombre d'opérations à environ 1,2 milliard pour exécuter environ 16,7% plus rapide que SoA avec des optimisations de boucle d'opération équivalentes.
GordonBGood
6
Suite2: Le SoA con n'a pas une factorisation de roue de facteur plus élevée utilisée pour faire une grande différence car la roue de factorisation 2, 3, 5 est une partie "intégrée" de l'algorithme.
GordonBGood
4
@Eamon Nerbonne, WP a raison; cependant, le simple fait d'avoir une complexité de calcul légèrement meilleure ne rend pas un algorithme plus rapide pour une utilisation générale. Dans ces commentaires, je fais référence au fait que la factorisation maximale de la roue du tamis d'Eratosthène (SoE) (ce qui n'est pas possible pour le tamis d'Atkin-SoA) permet un peu moins d'opérations pour le SoE jusqu'à une plage d'environ un milliard. Bien au-dessus de ce point, il faut généralement utiliser la segmentation des pages pour surmonter les limitations de la mémoire, et c'est là que le SoA échoue, prenant des quantités de surcharge constantes rapidement croissantes avec une plage croissante.
GordonBGood
29

Si cela doit être très rapide, vous pouvez inclure une liste de nombres premiers:
http://www.bigprimes.net/archive/prime/

Si vous avez juste à savoir si un certain nombre est un nombre premier, il existe différents tests premiers répertoriés sur wikipedia . C'est probablement la méthode la plus rapide pour déterminer si les grands nombres sont des nombres premiers, surtout parce qu'ils peuvent vous dire si un nombre n'est pas un nombre premier.

Georg Schölly
la source
2
Une liste de tous les nombres premiers? Je pense que vous voulez dire une liste des premiers nombres premiers ... :)
j_random_hacker
9
Si vous appelez quelques 100000000, alors oui. :)
Georg Schölly
58
sûrement 100000000 est "quelques" comparé à l'infini;)
Timofey
9
Pourquoi pensez-vous que le tamis d'Atkin (SoA) est plus rapide que le tamis d'Eratosthène (SoE)? Ce n'est certainement pas quand on implémente simplement un programme en utilisant le pseudo code comme dans l'article de Wikipédia que vous avez lié. Si le SoE est implémenté avec un niveau d'optimisation possible similaire à celui utilisé avec le SoA, alors il y a un peu moins d'opérations pour de très grandes plages de tamisage pour SoA que pour SoE, mais ce gain est généralement plus que compensé par la complexité accrue et la une surcharge de facteur supplémentaire constante de cette complexité de calcul telle que pour les applications pratiques, le SoE est meilleur.
GordonBGood
26

Lui, il je sais que je suis un nécromancien de question répondant à de vieilles questions, mais je viens de trouver cette question en cherchant sur le net des moyens d'implémenter des tests de nombres premiers efficaces.

Jusqu'à présent, je pense que l'algorithme de test des nombres premiers le plus rapide est Strong Probable Prime (SPRP). Je cite des forums Nvidia CUDA:

L'un des problèmes de niche les plus pratiques de la théorie des nombres concerne l'identification des nombres premiers. Étant donné N, comment déterminer efficacement s'il est premier ou non? Ce n'est pas seulement un problème théorique, il peut s'agir d'un problème réel nécessaire dans le code, peut-être lorsque vous avez besoin de trouver dynamiquement une taille de table de hachage principale dans certaines plages. Si N est quelque chose de l'ordre de 2 ^ 30, voulez-vous vraiment faire 30000 tests de division pour rechercher des facteurs? Évidemment pas.

La solution pratique courante à ce problème est un test simple appelé test d'Euler probable premier et une généralisation plus puissante appelée Strong Probable Prime (SPRP). Il s'agit d'un test qui, pour un entier N, peut le classer de manière probabiliste comme premier ou non, et des tests répétés peuvent augmenter la probabilité d'exactitude. La partie lente du test lui-même implique principalement le calcul d'une valeur similaire à A ^ (N-1) modulo N. Quiconque implémente des variantes de chiffrement à clé publique RSA a utilisé cet algorithme. C'est utile à la fois pour les entiers énormes (comme 512 bits) ainsi que pour les entiers normaux 32 ou 64 bits.

Le test peut être changé d'un rejet probabiliste en une preuve définitive de primalité en précalculant certains paramètres d'entrée de test qui sont connus pour toujours réussir pour des plages de N. Malheureusement, la découverte de ces "tests les plus connus" est effectivement une recherche d'un énorme ( en fait infini) domaine. En 1980, une première liste de tests utiles a été créée par Carl Pomerance (célèbre pour être celui qui factorise RSA-129 avec son algorithme Quadratic Seive.) Plus tard, Jaeschke a amélioré les résultats de manière significative en 1993. En 2004, Zhang et Tang ont amélioré la théorie. et les limites du domaine de recherche. Greathouse et Livingstone ont publié les résultats les plus modernes jusqu'à présent sur le Web, à l' adresse http://math.crg4.com/primes.html , les meilleurs résultats d'un vaste domaine de recherche.

Voir ici pour plus d'informations: http://primes.utm.edu/prove/prove2_3.html et http://forums.nvidia.com/index.php?showtopic=70483

Si vous avez juste besoin d'un moyen de générer de très grands nombres premiers et que vous ne vous souciez pas de générer tous les nombres premiers <un entier n, vous pouvez utiliser le test de Lucas-Lehmer pour vérifier les nombres premiers de Mersenne. Un nombre premier de Mersenne se présente sous la forme 2 ^ p -1. Je pense que le test de Lucas-Lehmer est l'algorithme le plus rapide découvert pour les nombres premiers de Mersenne.

Et si vous voulez non seulement utiliser l'algorithme le plus rapide, mais aussi le matériel le plus rapide, essayez de l'implémenter à l'aide de Nvidia CUDA, écrivez un noyau pour CUDA et exécutez-le sur GPU.

Vous pouvez même gagner de l'argent si vous découvrez des nombres premiers suffisamment grands, l'EFF donne des prix allant de 50K $ à 250K $: https://www.eff.org/awards/coop

Mack
la source
17

Il existe un test mathématique à 100% qui vérifie si un nombre Pest premier ou composé, appelé AKS Primality Test .

Le concept est simple: étant donné un nombre P, si tous les coefficients de (x-1)^P - (x^P-1)sont divisibles par P, alors Pest un nombre premier, sinon c'est un nombre composé.

Par exemple, donné P = 3, donnerait le polynôme:

   (x-1)^3 - (x^3 - 1)
 = x^3 + 3x^2 - 3x - 1 - (x^3 - 1)
 = 3x^2 - 3x

Et les coefficients sont tous deux divisibles par 3, donc le nombre est premier.

Et par exemple, où P = 4, ce qui n'est PAS un nombre premier, produirait:

   (x-1)^4 - (x^4-1)
 = x^4 - 4x^3 + 6x^2 - 4x + 1 - (x^4 - 1)
 = -4x^3 + 6x^2 - 4x

Et ici, nous pouvons voir que les coefficients 6ne sont pas divisibles par 4, donc ce n'est PAS premier.

Le polynôme se (x-1)^Pterminera P+1et peut être trouvé en utilisant une combinaison. Donc, ce test fonctionnera au O(n)moment de l' exécution, donc je ne sais pas à quel point cela serait utile puisque vous pouvez simplement parcourir ide 0 à pet tester le reste.

Kousha
la source
5
AKS est une méthode très lente dans la pratique, non compétitive par rapport aux autres méthodes connues. La méthode que vous décrivez n'est pas AKS mais un lemme d'ouverture qui est plus lent que la division d'essai non optimisée (comme vous le faites remarquer).
DanaJ
bonjour @Kousha, à quoi servent les xstands? dans (x-1)^P - (x^P-1). avez-vous un exemple de code pour cela? en C ++ pour déterminer si l'entier est premier ou non?
kiLLua
@kiLLua X n'est qu'une variable. C'est le coefficient de X qui détermine si le nombre est premier ou non. Et non je n'ai pas le code. Je ne recommande pas d'utiliser cette méthode pour déterminer si un nombre est premier ou non. C'est juste un comportement mathématique très cool des nombres premiers, mais sinon c'est incroyablement inefficace.
Kousha
5

Votre problème est-il de décider si un nombre particulier est premier? Ensuite, vous avez besoin d'un test de primalité (facile). Ou avez-vous besoin de tous les nombres premiers jusqu'à un nombre donné? Dans ce cas, les tamis primaires sont bons (faciles, mais nécessitent de la mémoire). Ou avez-vous besoin des facteurs premiers d'un nombre? Cela nécessiterait une factorisation (difficile pour les grands nombres si vous voulez vraiment les méthodes les plus efficaces). Quelle est la taille des chiffres que vous regardez? 16 bits? 32 bits? plus gros?

Un moyen intelligent et efficace est de pré-calculer les tables de nombres premiers et de les conserver dans un fichier en utilisant un encodage au niveau du bit. Le fichier est considéré comme un vecteur de bits longs tandis que le bit n représente l'entier n. Si n est premier, son bit est mis à un et à zéro dans le cas contraire. La recherche est très rapide (vous calculez le décalage d'octet et un masque de bits) et ne nécessite pas le chargement du fichier en mémoire.

Christian Lindig
la source
Un bon test de primalité est compétitif avec la latence de la mémoire principale pour les tables principales qui pourraient raisonnablement s'adapter, donc je n'utiliserais pas cela à moins qu'il ne puisse s'intégrer dans L2.
Charles
3

Rabin-Miller est un test de primalité probabiliste standard. (vous l'exécutez K fois et le nombre d'entrée est soit définitivement composite, soit il est probablement premier avec une probabilité d'erreur 4 -K . (quelques centaines d'itérations et cela vous dit presque certainement la vérité)

Il existe une variante non probabiliste (déterministe) de Rabin Miller .

Le Great Internet Mersenne Prime Search (GIMPS), qui a trouvé le record mondial du plus grand nombre de nombres premiers prouvés (2 74207281 - 1 en juin 2017), utilise plusieurs algorithmes , mais ce sont des nombres premiers sous des formes spéciales. Cependant, la page GIMPS ci-dessus inclut des tests de primalité déterministes généraux. Ils semblent indiquer que quel algorithme est "le plus rapide" dépend de la taille du nombre à tester. Si votre nombre tient en 64 bits, vous ne devriez probablement pas utiliser une méthode destinée à travailler sur des nombres premiers de plusieurs millions de chiffres.

Jason S
la source
2

Cela dépend de votre application. Il y a quelques considérations:

  • Avez-vous simplement besoin d'informations pour savoir si quelques nombres sont premiers, avez-vous besoin de tous les nombres premiers jusqu'à une certaine limite, ou avez-vous besoin (potentiellement) de tous les nombres premiers?
  • Quelle est la taille des chiffres avec lesquels vous devez faire face?

Les tests Miller-Rabin et analogiques ne sont plus rapides qu'un tamis que pour des nombres supérieurs à une certaine taille (quelque part autour de quelques millions, je crois). En dessous, utiliser une division d'essai (si vous n'avez que quelques chiffres) ou un tamis est plus rapide.

Svante
la source
0

Je vous laisse décider si c'est le plus rapide ou non.

using System;
namespace PrimeNumbers
{

public static class Program
{
    static int primesCount = 0;


    public static void Main()
    {
        DateTime startingTime = DateTime.Now;

        RangePrime(1,1000000);   

        DateTime endingTime = DateTime.Now;

        TimeSpan span = endingTime - startingTime;

        Console.WriteLine("span = {0}", span.TotalSeconds);

    }


    public static void RangePrime(int start, int end)
    {
        for (int i = start; i != end+1; i++)
        {
            bool isPrime = IsPrime(i);
            if(isPrime)
            {
                primesCount++;
                Console.WriteLine("number = {0}", i);
            }
        }
        Console.WriteLine("primes count = {0}",primesCount);
    }



    public static bool IsPrime(int ToCheck)
    {

        if (ToCheck == 2) return true;
        if (ToCheck < 2) return false;


        if (IsOdd(ToCheck))
        {
            for (int i = 3; i <= (ToCheck / 3); i += 2)
            {
                if (ToCheck % i == 0) return false;
            }
            return true;
        }
        else return false; // even numbers(excluding 2) are composite
    }

    public static bool IsOdd(int ToCheck)
    {
        return ((ToCheck % 2 != 0) ? true : false);
    }
}
}

Il faut environ 82 secondes pour trouver et imprimer des nombres premiers dans une plage de 1 à 1 000 000, sur mon ordinateur portable Core 2 Duo avec un processeur 2,40 GHz. Et il a trouvé 78 498 nombres premiers.

Tayyab Mazhar
la source
3
c'est bien trop lent. le problème est i <= (ToCheck / 3). ça devrait être i <= (ToCheck / i). avec lui, il pourrait fonctionner en 0,1 seconde à la place.
Will Ness
-1

J'utilise toujours cette méthode pour calculer les nombres premiers suivants avec l'algorithme de tamisage.

void primelist()
 {
   for(int i = 4; i < pr; i += 2) mark[ i ] = false;
   for(int i = 3; i < pr; i += 2) mark[ i ] = true; mark[ 2 ] = true;
   for(int i = 3, sq = sqrt( pr ); i < sq; i += 2)
       if(mark[ i ])
          for(int j = i << 1; j < pr; j += i) mark[ j ] = false;
  prime[ 0 ] = 2; ind = 1;
  for(int i = 3; i < pr; i += 2)
    if(mark[ i ]) ind++; printf("%d\n", ind);
 }
Osman Goni Nahid
la source
-3
#include<stdio.h>
main()
{
    long long unsigned x,y,b,z,e,r,c;
    scanf("%llu",&x);
    if(x<2)return 0;
    scanf("%llu",&y);
    if(y<x)return 0;
    if(x==2)printf("|2");
    if(x%2==0)x+=1;
    if(y%2==0)y-=1;
    for(b=x;b<=y;b+=2)
    {
        z=b;e=0;
        for(c=2;c*c<=z;c++)
        {
            if(z%c==0)e++;
            if(e>0)z=3;
        }
        if(e==0)
        {
            printf("|%llu",z);
            r+=1;
        }
    }
    printf("|\n%llu outputs...\n",r);
    scanf("%llu",&r);
}    
Tjandra
la source
r est utilisé avant d'être initialisé
zumalifeguard
-3

Je ne connais aucun algorithme prédéfini mais j'ai créé le mien qui est très rapide. Il peut traiter des nombres de 20 chiffres en moins de 1 seconde. La capacité maximale de ce programme est 18446744073709551615. Le programme est:

#include <iostream>
#include <cmath>
#include <stdlib.h>

using namespace std;

unsigned long long int num = 0;

bool prime() {
    if (num % 2 == 0 || num == 1) {
        return false;
    }

    unsigned long int square_root = sqrt(num);
    for (unsigned long int i = 3; i <= square_root; i += 2) {
        if (num % i == 0) {
            return false;
        }
    }

    return true;
}

int main() {
    do {
        system("cls");
        cout << "Enter number : ";
        cin >> num;

        if (prime()) {
            cout << "The number is a prime number" << endl << endl << endl << endl;
        } else {
            cout << "The number is not a prime number" << endl << endl << endl << endl;
        }
        system("pause");
    } while (1);

    return 0;
}
Sushant
la source
-4
#include <iostream>

using namespace std;

int set [1000000];

int main (){

    for (int i=0; i<1000000; i++){
        set [i] = 0;
    }
    int set_size= 1000;
    set [set_size];
    set [0] = 2;
    set [1] = 3;
    int Ps = 0;
    int last = 2;

    cout << 2 << " " << 3 << " ";

    for (int n=1; n<10000; n++){
        int t = 0;
        Ps = (n%2)+1+(3*n);
        for (int i=0; i==i; i++){
            if (set [i] == 0) break;
            if (Ps%set[i]==0){
                t=1;
                break;
            }
        }
        if (t==0){
            cout << Ps << " ";
            set [last] = Ps;
            last++;
        }
    }
    //cout << last << endl;


    cout << endl;

    system ("pause");
    return 0;
}
vidura
la source
12
cela devrait être une réponse sur "Comment écrire du code non structuré sans utiliser réellement GOTO". Toute cette confusion juste pour coder une simple division d'essai! (n%2)+1+(3*n)c'est plutôt sympa. :)
Will Ness
1
@Will Ness J'aurais voté contre cela comme réponse à cette question; pourquoi utiliser une boucle for quand une macro fera l'affaire? :)
Rob Grant
-4

Je sais que c'est un peu plus tard, mais cela pourrait être utile aux personnes qui arrivent ici à la suite de recherches. Quoi qu'il en soit, voici un code JavaScript qui repose sur le fait que seuls les facteurs premiers doivent être testés, de sorte que les premiers nombres premiers générés par le code sont réutilisés comme facteurs de test pour les plus récents. Bien sûr, toutes les valeurs paires et mod 5 sont filtrées en premier. Le résultat sera dans le tableau P, et ce code peut écraser 10 millions de nombres premiers en moins de 1,5 seconde sur un PC i7 (ou 100 millions dans environ 20). Réécrit en C ça devrait être très rapide.

var P = [1, 2], j, k, l = 3

for (k = 3 ; k < 10000000 ; k += 2)
{
  loop: if (++l < 5)
  {
    for (j = 2 ; P[j] <= Math.sqrt(k) ; ++j)
      if (k % P[j] == 0) break loop

    P[P.length] = k
  }
  else l = 0
}
Robin Nixon
la source
2
Cela vous posera beaucoup de problèmes si vous générez un grand nombre de nombres premiers, et pour les comparaisons, mieux vaut utiliser P [j] * P [j] <= k, car sqrt est assez lent
Simon
-11
#include<iostream>
using namespace std;

void main()
{
    int num,i,j,prime;
    cout<<"Enter the upper limit :";
    cin>>num;

    cout<<"Prime numbers till "<<num<<" are :2, ";

    for(i=3;i<=num;i++)
    {
        prime=1;
        for(j=2;j<i;j++)
        {
            if(i%j==0)
            {
                prime=0;
                break;
            }
        }

        if(prime==1)
            cout<<i<<", ";

    }
}
Gaurav
la source
60
c'est à peu près le plus lent que vous puissiez y prendre.
Will Ness
1
C'est très lent, si la limite supérieure est disons 10000000 alors ce code prendra beaucoup de temps !!
Dixit Singla
ce code est O (N ^ 2 / log N). sans break;cela, ce serait encore plus lent, O (N ^ 2), mais cela pourrait déjà être vu comme une erreur de codage. sauvegarder et tester par nombres premiers est O (N ^ 2 / (log N) ^ 2), et tester par nombres premiers sous la racine carrée du nombre uniquement, est O (N ^ 1,5 / (log N) ^ 2).
Will Ness
@WillNess Peut-être un peu hyperbolique. Il aurait pu facilement démarrer la boucle for à partir de 1 au lieu de 2, et ajouter un j <= i au lieu de j <i. :)
Kenny Cason
3
Je ne pense pas que cet article devrait être supprimé, il sert de contre-exemple précieux.
Will Ness