Matematica Discreta II Lezione del giorno 31 ottobre 2007 Numeri primi e test di primalità Nell’implementazione del sistema crittografico RSA vi è la necessità di trovare numeri primi abbastanza “grandi”, per aumentare la sicurezza del sistema. Sappiamo che i numeri primi sono infiniti (Teorema di Euclide), ma nella successione dei numeri naturali essi sono distribuiti in modo irregolare. Per valutare il numero di primi in un certo intervallo [0,x] della semiretta positiva dell’asse reale, definiamo, per ogni reale x0 : (x)= numero dei primi x Per esempio (22,3)= {2,3,5,7,11,13,17,19}=8 Non esistono formule algebriche per il calcolo esatto di (x). Per esempio, usando i computers, si è verificato che : (1014) = 3.204.941.750.802 (i valori massimi attualmente calcolati sono intorno a 1022). Nel corso del tempo, i matematici hanno cercato funzioni che “approssimassero” (x). Il risultato più famoso è il: Teorema di Hadamard-De La Vallé Poussin. Considerata la funzione f(x)=x/log(x) (definita per x>0, x1) dove log(x) è il logaritmo neperiano, π(x) si ha: lim 1 x f(x) (il teorema si dimostra con metodi analitici). Quindi, per x abbastanza grande, il rapporto π(x) è abbastanza vicino a 1, e in un certo senso f(x) f(x)=x/log(x) approssima (x). Per esempio f(1014) 3.102.103.442.166, da cui π(1014 ) f(10 14 ) 1.033 Come conseguenza del teorema precedente (dopo alcuni ragionamenti probabilistici) si ottiene che la probabilità che, scelto casualmente un numero naturale x di n cifre (in base 10), esso sia primo è 1/[nlog(10)]. Tenendo conto che log(10) 2,3, la probabilità che un numero x di n cifre, scelto casualmente, sia primo è 1/[2,3log(n)]. Per esempio la probabilità che un numero di 100 cifre sia primo è 1/230: se scegliamo casualmente 230 numeri di 100 cifre, statisticamente dovremmo aspettarci che uno di essi sia primo. Quindi per trovare un numero primo di un fissato numero n di cifre (in base 10), si può scegliere casualmente un numero di n cifre, sottoporlo a un “test di primalità” (vedere sotto) e se il test non è superato cambiare la scelta del numero, e così via: statisticamente dovrebbero bastare circa 2,3n tentativi per trovare un numero primo. Un test di primalità è un algoritmo tale che, dato in input un numero naturale n>1, produca un output “n è primo” oppure “n è composto” (definendo composto un naturale >1 non primo). Si distinguono: - test di primalità deterministici: l’output è “n è primo” se e solo se l’input n è un numero primo - test di primalità probabilistici: nell’algoritmo vi è la scelta casuale di alcuni elementi, e inoltre se l’input n è un numero primo allora l’output è sempre “n è primo” (tutti i numeri primi superano il test), ma se n è composto, l’output può essere sia “n è primo” sia “n è composto”, e la probabilità che l’output sia “n è primo” (pur essendo n composto) è maggiorata da una costante < 1 conosciuta a priori e indipendente dall’input n. Se un naturale n>1 supera un test di primalità probabilistico un numero k di volte (con gli elementi casuali scelti in modo indipendente), la probabilità che il numero n sia composto si può rendere piccola a piacere (se k è abbastanza grande), quindi si può accettare che n sia effettivamente primo, con una bassa percentuale di errore. Il primo test ingenuo di primalità è il test (deterministico) che verifica, per k=2,….,n-1, se esiste un k divisore non banale di n: se k esiste l’output è “n è composto”, se k non esiste l’output è “n è primo”. L’algoritmo consiste di (n-2) divisioni, quindi il numero di operazioni è <n: se t è il numero di cifre di n (per esempio in base 2) si ha 2t-1n<2t, e il numero di operazioni è <n<2t, quindi è un algoritmo di complessità esponenziale. Questo test si può rendere più efficiente osservando che se esiste un divisore non banale del numero composto n, allora esiste certamente un divisore non banale di n che sia n (infatti se per assurdo fosse n=bc con b,c> n , si avrebbe n>( n )2, contraddizione). Quindi nel test precedente si può limitare la ricerca ai valori k con 2k n : il numero di divisioni in questo caso è < n < ( 2 ) t , quindi anche questo algoritmo ha complessità esponenziale. Un altro test di primalità deterministico segue dal: Teorema di Wilson. Sia n> 1 un naturale . Allora: n è primo (n-1)!-1 (mod n). Dimostrazione: (): Consideriamo il gruppo moltiplicativo Zn* degli elementi invertibili di Zn. Se n è primo, n è coprimo con tutti i numeri 1,2,…,n-1 (perché non sono multipli di n), dunque Zn* = {[1], [2], ….., [n-1]}. Calcoliamo il prodotto di tutti gli elementi di tale gruppo: [1][2] …..[n-1] = [12 …..(n-1)] = [(n-1)!]. In tale prodotto, sfruttando la proprietà commutativa, possiamo accoppiare ogni elemento [i] con il suo inverso [i]-1 (nel caso in cui [i][i]-1) ottenendo per ogni tale coppia un prodotto =[1]: al termine di tale semplificazione il prodotto precedente si riduce a quello dei soli fattori [i] tali che [i]=[i] -1, cioè tali che [i]2=[i2]=[1]. Ma per un tale i si ha n(i2-1), n(i+1)(i-1), quindi (essendo n primo) n(i+1) oppure n(i-1), ossia i1 (mod n) oppure i-1 (mod n). Si conclude che: [(n-1)!]=[1][-1]=[-1] , da cui la tesi. (): Sia (n-1)!+1=kn, con k intero. Se per assurdo esistesse un divisore non banale d di n, con 1<d<n, tale d sarebbe divisore di (n-1)! (perché d è uno dei fattori del prodotto (n-1)!) dunque d sarebbe divisore di kn-(n-1)!=1 , contraddizione perché d>1. Nota: la condizione (n-1)!-1 (mod n) del Teorema di Wilson equivale a (n-1)!modn=(n-1) (in quanto -1(n-1) (mod n)). Il Teorema di Wilson permette di costruire un test di primalità deterministico: 1) in input il naturale n>1 2) si calcola la riduzione (n-1)!modn (si può per esempio costruire la successione y1,y2,…,yn-1 ponendo y1=1, e per i>1 yi=(iyi-1)modn, ottenendo alla fine yn-1=(n-1)!modn): se (n-1)!modn=(n-1) si esce con output “n è primo”; altrimenti si esce con output “n è composto”. Nel passo 2), il calcolo di (n-1)!modn comporta l’esecuzioni di un numero di operazioni (prodotti e divisioni per la riduzione modulo n) che è <2n=2t+1 (se t è il numero di cifre di n in base 2), dunque anche questo algoritmo ha complessità esponenziale. L’unico test di primalità deterministico di complessità polinomiale attualmente esistente è il test AKS (2003), la cui trattazione esula dagli scopi del corso. Esponiamo ora un esempio di test di primalità probabilistico: il test di Rabin-Miller. Esso si applica ad un input n>1 naturale dispari (non è una restrizione sostanziale perché un naturale pari è sempre composto, tranne nel caso n=2). I passi dell’algoritmo del test di Rabin-Miller sono: 1) in input il naturale n>1 dispari 2) si calcola la massima potenza 2t di base 2 ed esponente t>0 tale che 2t sia divisore del numero pari n-1, e si scrive: n-1=2tk, con k>0 naturale dispari (in questo passo si effettuano t divisioni per 2 fino ad ottenere un quoziente dispari) 3) si sceglie casualmente un intero a con 2an-1 (detto base) 4) si calcolano (con l’algoritmo dell’esponenziazione modulare) le riduzioni delle t potenze di base j a ed esponente k, 21k, 22k, ….. , 2t-1k: a 2 k modn 5) se è verificata almeno una delle seguenti condizioni: per j=0,1,….,t-1 j ak1 (mod n) oppure a 2 k modn = n-1 per qualche j=0,1,….,t-1 si esce con output ”n è primo”, altrimenti si esce con output “n è composto” Dobbiamo naturalmente verificare che un input n primo superi sempre il test con output “n è primo”, e stabilire qual è la costante che maggiora la probabilità che un input composto n superi egualmente il test.