Nel mio secondo post di questa piccola serie ho detto che esistono 3 differenti strategie per verificare la primalità di un numero: una dedicata a numeri piccoli, una per numeri grossi e, l’ultima, per numeri particolari. Nel seguito mi sono dedicato solo alla seconda, e questo post vuole iniziare a sistemare il problema: parlerò di algoritmi per la prima delle due classi di numeri.
Siamo nel caso in cui i numeri sono piuttosto piccoli e quindi non è particolarmente utile creare tutto l’impianto necessario per il test di Miller-Rabin, ci possiamo accontentare di qualcosa di più semplice.
Il primo metodo che può venire in mente sono le divisioni successive: proviamo a dividere un numero per ogni numero minore della sua radice quadrata. È un metodo molto naïve, con complessità esponenziale, e non è particolarmente interessante.
I Crivelli
I crivelli forniscono un metodo per filtrare insiemi di numeri attraverso un criterio. In particolare, noi vorremmo che l’insieme filtrato contenga solo numeri primi. Prendiamo quindi l’insieme dei numeri naturali da 2 a N. Nel nostro esempio scegliamo N = 30.
{2, 3, 4, 5, 6, 7, 8, 9, … 30}
Partiamo dal primo numero, cioè 2. Lo segnamo come primo ed eliminiamo tutti i suoi multipli: 4, 6, 8, …
{2, 3, 5, 7, 9, 11, … 30}
Andiamo al numero successivo, 3, segnamo anch’esso come primo e eliminiamo i suoi multipli, che sono 6, 9, 12…
{2, 3, 5, 7, 11, 13, 17, 19, 23, 25, 29}
A questo punto passiamo a 5 (il 4 è già stato eliminato), e ripetiamo il processo di eliminazione dei multipli. Ci fermiamo quando arriviamo al numero appena minore di . Nell’esempio ci siamo fermati appena dopo il 5, perché ; alla fine abbiamo ottenuto l’insieme: {2, 3, 5, 7, 11, 13, 17, 19, 23, 29}, che sono proprio tutti i numeri primi minori di 30.
Questo appena descritto è il crivello di Erastotene: il metodo più antico e semplice per generare numeri primi. È un algoritmo piuttosto efficiente, ma occupa abbastanza spazio; si nota che fino ai 10 milioni funziona abbastanza bene. L’algoritmo può essere migliorato in vari modi, per esempio utilizzando le ruote o segmentando il crivello: ovvero dividere l’insieme in pezzi di dimensione minore, da sottoporre al crivello separatamente.
Pochi anni fa è stato ideato il crivello di Atkin: è un metodo che conta le soluzioni a certe equazioni per verificare se un numero è primo. Si scopre che non è particolarmente veloce rispetto al semplice crivello di Erastotene: anche se veramente ottimizzato le prestazioni rimangono piuttosto allineate.
Il funzionamento di questi metodi di generazione dei numeri primi è stato spiegato molto bene da zar nel suo blog (crivello di Erastotene, crivello di Atkin 1 e 2).
Ora abbiamo generato un milione di numeri primi: possiamo verificare se un numero minore di 1012 è primo semplicemente provando a dividerlo per questi numeri. Chiaramente, una volta che si ha una lista di primi, diventa piuttosto efficiente verificare se un numero è primo.
Purtroppo, come abbiamo visto, non possiamo generare liste arbitrariamente lunghe di numeri primi in maniera efficiente. L’unico modo che abbiamo oggi per verificare se un numero generico (senza proprietà particolari) è primo in modo veloce è usare il test di Miller-Rabin.