Qual è l'algoritmo più veloce per trovare i numeri primi?


183

Qual è l'algoritmo più veloce per scoprire i numeri primi usando C ++? Ho usato l'algoritmo di Sieve ma voglio ancora che sia più veloce!


Ho trovato un vecchio articolo, ma sembra interessante: Fun With Prime Numbers
Mvcoile,

29
@Jaider questo fallisce per numeri fino a 7 (111). Inoltre fallisce per 1001 = 9. E chiaramente fallisce per quasi tutti i numeri primi in generale (non copre il caso 2 ^ p - 1, che sono numeri primi di Mersenne - esempi generati genericamente - che sarà sempre della forma 111 ... 1)
Daniel Kats

1
@Kasperasky - Non hai detto quale setaccio? Probabilmente intendi Setaccio di Eranthoses!
user2618142

Algoritmo Sieve of Eratosthenes
Emad Aghayi

Risposte:


79

Un'implementazione molto rapida del setaccio di Atkin è il primegen di Dan Bernstein . Questo setaccio è più efficiente del setaccio di Eratostene . La sua pagina contiene alcune informazioni di riferimento.


10
In realtà non penso che primegen sia il più veloce, o anche il secondo più veloce; yafu e primesieve sono entrambi più veloci in generale, credo, e sicuramente oltre 2 ^ 32. Entrambi sono setacci (modificati) di Eratosthenes piuttosto che il setaccio Atkin-Bernstein.
Charles,

5
Primesieve Sieve of Eratosthenes (SoE) è l'algoritmo più veloce possibile e sarà sempre più veloce di qualsiasi implementazione del SoA Sieve di Atkin, incluso quello di Bernstein come collegato in questa risposta perché primesieve riduce il numero di operazioni rispetto a SoA: Per il 32- intervallo di numeri di bit (2 ^ 32 - 1), primesieve esegue circa 1,2 miliardi di abbattimenti mentre SoA esegue un totale di circa 1,4 miliardi di operazioni combinate senza interruzioni e quadrate, entrambe le operazioni hanno circa la stessa complessità e possono essere ottimizzate all'incirca nella stessa modo.
GordonBood

7
Continua: Bernstein ha confrontato la SoE solo usando la stessa fattorizzazione delle ruote effettiva della SoA, che è una ruota 2; 3; 5, il cui utilizzo provoca circa 1,83 miliardi di abbattimenti nell'intervallo di numeri a 32 bit; questo rende il SoA circa il 30% più veloce quando si confronta questa versione limitata di SoE per altre ottimizzazioni equivalenti. Tuttavia, l'algoritmo primesieve utilizza una ruota 2; 3; 5; 7 in combinazione con una pre-cullatura del segmento 2; 3; 5; 7; 11; 13; 17 per ridurre il numero di operazioni a circa 1,2 miliardi per eseguire 16,7% più veloce di SoA con ottimizzazioni del circuito operativo equivalenti.
GordonBood

6
Continua2: i SoA non hanno una fattorizzazione a fattore di ruota più elevata utilizzata per fare molta differenza in quanto la ruota di fattorizzazione a 2; 3; 5 è una parte "incorporata" dell'algoritmo.
GordonBood

4
@Eamon Nerbonne, WP è corretto; tuttavia, avere una complessità computazionale leggermente migliore non rende algoritmi più veloci per l'uso generale. In questi commenti, mi riferisco a quella massima fattorizzazione della ruota del setaccio di Eratostene (SoE) (che non è possibile per il setaccio di Atkin-SoA) rende le operazioni leggermente inferiori per la SoE fino a un intervallo di circa un miliardo. Molto al di sopra di quel punto, in genere è necessario utilizzare la segmentazione delle pagine per superare i limiti di memoria, ed è qui che la SoA fallisce, prendendo rapidamente crescenti quantità di sovraccarico costante con portata crescente.
GordonBood

29

Se deve essere molto veloce, puoi includere un elenco di numeri primi:
http://www.bigprimes.net/archive/prime/

Se devi solo sapere se un certo numero è un numero primo, ci sono vari test primi elencati su Wikipedia . Sono probabilmente il metodo più veloce per determinare se numeri grandi sono numeri primi, soprattutto perché possono dirti se un numero non è un numero primo.


2
Un elenco di tutti i numeri primi? Penso che intendi un elenco dei primi primi ... :)
j_random_hacker

9
Se chiamate alcuni 100000000, quindi sì. :)
Georg Schölly,

58
sicuramente 100000000 è "pochi" rispetto all'infinito;)
Timofey,

9
Perché pensi che il setaccio di Atkin (SoA) sia più veloce del setaccio di Eratostene (SoE)? Certamente non è quando si implementa semplicemente un programma usando lo pseudo codice come nell'articolo di Wikipedia che hai collegato. Se la SoE viene implementata con un livello simile di possibili ottimizzazioni rispetto a quelle utilizzate per la SoA, allora ci sono leggermente meno operazioni per intervalli di setacciatura molto grandi per la SoA che per la SoE, ma quel guadagno è di solito più che compensato dalla maggiore complessità e dal Fattore extra costante di questa complessità computazionale tale che per applicazioni pratiche la SoE è migliore.
GordonBBuon

26

Lui, lui so di essere un negromante che risponde a vecchie domande, ma ho appena trovato questa domanda cercando in rete modi per implementare test efficaci sui numeri primi.

Fino ad ora, credo che l'algoritmo di test dei numeri primi più veloce sia Strong Probable Prime (SPRP). Sto citando dai forum Nvidia CUDA:

Uno dei problemi di nicchia più pratici nella teoria dei numeri ha a che fare con l'identificazione dei numeri primi. Dato N, come si può determinare in modo efficiente se è primo o no? Questo non è solo un problema teorico, può essere un problema reale necessario nel codice, forse quando è necessario trovare dinamicamente una dimensione della tabella hash primaria all'interno di determinati intervalli. Se N è qualcosa nell'ordine di 2 ^ 30, vuoi davvero fare 30000 test di divisione per cercare qualche fattore? Ovviamente no.

La soluzione pratica comune a questo problema è un semplice test chiamato probabile primo test di Eulero e una generalizzazione più potente chiamata Strong Probable Prime (SPRP). Questo è un test che per un numero intero N può classificarlo probabilisticamente come primo o no, e test ripetuti possono aumentare la probabilità di correttezza. La parte lenta del test stesso comporta principalmente il calcolo di un valore simile a A ^ (N-1) modulo N. Chiunque implementasse varianti di crittografia a chiave pubblica RSA ha utilizzato questo algoritmo. È utile sia per numeri interi enormi (come 512 bit) sia per normali 32 o 64 bit.

Il test può essere modificato da un rifiuto probabilistico in una prova definitiva di primalità precompilando alcuni parametri di input del test che sono noti per avere sempre successo per intervalli di N. Sfortunatamente la scoperta di questi "test più noti" è effettivamente una ricerca di un enorme ( in realtà infinito) dominio. Nel 1980, Carl Pomerance ha creato un primo elenco di test utili (famoso per essere il fattore con cui RSA-129 è stato col suo algoritmo Quadratic Seive.) Successivamente Jaeschke ha migliorato significativamente i risultati nel 1993. Nel 2004, Zhang e Tang hanno migliorato la teoria e limiti del dominio di ricerca. Greathouse e Livingstone hanno pubblicato i risultati più moderni fino ad ora sul Web, su http://math.crg4.com/primes.html , i migliori risultati di un enorme dominio di ricerca.

Vedi qui per maggiori informazioni: http://primes.utm.edu/prove/prove2_3.html e http://forums.nvidia.com/index.php?showtopic=70483

Se hai solo bisogno di un modo per generare numeri primi molto grandi e non ti interessa generare tutti i numeri primi <un numero intero n, puoi utilizzare il test Lucas-Lehmer per verificare i numeri primi di Mersenne. Un numero primo di Mersenne ha la forma di 2 ^ p -1. Penso che il test di Lucas-Lehmer sia l'algoritmo più veloce scoperto per i numeri primi di Mersenne.

E se non vuoi solo usare l'algoritmo più veloce ma anche l'hardware più veloce, prova a implementarlo usando Nvidia CUDA, scrivi un kernel per CUDA ed eseguilo su GPU.

Puoi anche guadagnare un po 'di soldi se scopri numeri primi abbastanza grandi, EFF offre premi da $ 50K a $ 250K: https://www.eff.org/awards/coop


17

Esiste un test matematico al 100% che verificherà se un numero Pè primo o composito, chiamato AKS Primality Test .

Il concetto è semplice: dato un numero P, se tutti i coefficienti di (x-1)^P - (x^P-1)sono divisibili per P, allora Pè un numero primo, altrimenti è un numero composto.

Ad esempio, dato P = 3, darebbe il polinomio:

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

E i coefficienti sono entrambi divisibili per 3, quindi il numero è primo.

Ed esempio dove P = 4, che NON è un numero primo, si otterrebbe:

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

E qui possiamo vedere che i coefficienti 6non sono divisibili per4 , quindi NON sono primi.

Il polinomio (x-1)^Pvolontà P+1termini e possono essere trovati tramite combinazione. Quindi, questo test verrà eseguito in O(n)fase di esecuzione, quindi non so quanto sarebbe utile dal momento che puoi semplicemente scorrere ida 0 a pe testare il resto.


5
L'AKS è in pratica un metodo molto lento, non competitivo con altri metodi noti. Il metodo che descrivi non è AKS ma un lemma iniziale che è più lento della divisione di prova non ottimizzata (come fai notare).
DanaJ,

ciao @Kousha, cosa xsignifica? in (x-1)^P - (x^P-1). hai un codice di esempio per questo? in C ++ per determinare se il numero intero è primo o no?
kiLLua,

@kiLLua X è solo una variabile. È il coefficiente di X che determina se il numero è primo o no. E no non ho il codice. Non consiglio di utilizzare effettivamente questo metodo per determinare se un numero è primo o no. Questo è solo un ottimo comportamento matematico dei numeri primi, ma per il resto è incredibilmente inefficiente.
Kousha,

5

Il tuo problema è decidere se un determinato numero è primo? Quindi è necessario un test di primalità (facile). O hai bisogno di tutti i numeri primi fino a un determinato numero? In tal caso i setacci primi sono buoni (facili, ma richiedono memoria). O hai bisogno dei fattori primi di un numero? Ciò richiederebbe la fattorizzazione (difficile per grandi numeri se si vogliono davvero i metodi più efficienti). Quanto sono grandi i numeri che stai guardando? 16 bit? 32 bit? più grande?

Un modo intelligente ed efficiente è pre-calcolare tabelle di numeri primi e tenerli in un file usando una codifica a livello di bit. Il file è considerato un vettore di bit lungo mentre il bit n rappresenta l'intero n. Se n è primo, il suo bit è impostato su uno e su zero altrimenti. La ricerca è molto veloce (si calcola l'offset di byte e una maschera di bit) e non richiede il caricamento del file in memoria.


Un buon test di primalità è competitivo con la latenza della memoria principale per le tabelle prime che potrebbero ragionevolmente adattarsi, quindi non userei questo a meno che non si adatti a L2.
Charles,

3

Rabin-Miller è un test probabilistico standard di primalità. (lo esegui K volte e il numero di input è sicuramente composito, oppure è probabilmente primo con probabilità di errore 4 -K . (poche centinaia di iterazioni e quasi sicuramente ti sta dicendo la verità)

Esiste una variante non probabilistica (deterministica) di Rabin Miller .

La Great Internet Mersenne Prime Search (GIMPS), che ha trovato il record mondiale per il numero primo più grande provato (2 74.207.281 - 1 a giugno 2017), utilizza diversi algoritmi , ma questi sono numeri primi in forme speciali. Tuttavia, la pagina GIMPS sopra include alcuni test di primalità deterministica generale. Sembrano indicare che l'algoritmo "più veloce" dipende dalla dimensione del numero da testare. Se il tuo numero si adatta a 64 bit, probabilmente non dovresti usare un metodo destinato a lavorare con numeri primi di diversi milioni di cifre.


2

Dipende dalla tua applicazione. Vi sono alcune considerazioni:

  • Hai bisogno solo delle informazioni se alcuni numeri sono primi, hai bisogno di tutti i numeri primi fino a un certo limite o hai bisogno (potenzialmente) di tutti i numeri primi?
  • Quanto sono grandi i numeri che devi affrontare?

I test di Miller-Rabin e analogici sono solo più veloci di un setaccio per numeri di una certa dimensione (credo che siano circa qualche milione circa). Al di sotto di ciò, utilizzare una divisione di prova (se hai solo pochi numeri) o un setaccio è più veloce.


0

Ti lascerò decidere se è il più veloce o no.

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

Sono necessari circa 82 secondi per trovare e stampare numeri primi in un intervallo compreso tra 1 e 1.000.000, sul mio laptop Core 2 Duo con un processore da 2,40 GHz. E ha trovato 78.498 numeri primi.


3
questo è troppo lento. il problema è i <= (ToCheck / 3). dovrebbe essere i <= (ToCheck / i). con esso, invece, potrebbe essere eseguito in 0,1 secondi.
Will Ness,

-1

Uso sempre questo metodo per calcolare i numeri dei numeri primi che seguono con l'algoritmo del setaccio.

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

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

r viene utilizzato prima di essere inizializzato
zumalifeguard,

-3

Non conosco alcun algoritmo predefinito ma ne ho creato uno mio che è molto veloce. Può elaborare numeri di 20 cifre in meno di 1 secondo. La capacità massima di questo programma è 18446744073709551615. Il programma è:

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

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

12
questa dovrebbe essere una risposta su "Come scrivere codice non strutturato senza effettivamente utilizzare GOTO". Tutta questa confusione solo per codificare una semplice divisione di prova! (n%2)+1+(3*n)è carino però. :)
Will Ness

1
@Will Ness Avrei votato in negativo questo come una risposta a quella domanda; perché usare un ciclo for quando una macro lo farà? :)
Rob Grant,

-4

So che è un po 'più tardi, ma questo potrebbe essere utile per le persone che arrivano qui dalle ricerche. Comunque, ecco alcuni JavaScript che si basano sul fatto che devono essere testati solo i fattori primi, quindi i primi precedenti generati dal codice vengono riutilizzati come fattori di test per quelli successivi. Naturalmente, tutti i valori pari e mod 5 vengono prima filtrati. Il risultato sarà nell'array P e questo codice può sgranocchiare 10 milioni di numeri primi in meno di 1,5 secondi su un PC i7 (o 100 milioni in circa 20). Riscritto in C dovrebbe essere molto veloce.

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
}

2
Questo ti darà molti problemi se stai generando un gran numero di numeri primi e, per i confronti, usa meglio P [j] * P [j] <= k, perché sqrt è piuttosto lento
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<<", ";

    }
}

60
questo è il più lento che puoi fare al riguardo.
Will Ness,

1
Questo è molto lento, se il limite superiore è 10000000, allora questo codice richiederà molto tempo !!
Dixit Singla,

questo codice è O (N ^ 2 / log N). senza break;sarebbe ancora più lento, O (N ^ 2), ma questo potrebbe già essere visto come un errore di codifica. il salvataggio e il test con numeri primi è O (N ^ 2 / (log N) ^ 2) e il test con numeri primi solo sotto la radice quadrata del numero, è O (N ^ 1.5 / (log N) ^ 2).
Will Ness,

@WillNess Forse un po 'iperbolico. Avrebbe potuto facilmente avviare il ciclo for da 1 invece di 2 e aggiungere un j <= i invece di j <i. :)
Kenny Cason

3
Non penso che questo post debba essere cancellato, serve da prezioso controesempio.
Will Ness,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.