Teoria dei numeri e Crittografia: lezione del 19 dicembre 2011 Algoritmi di fattorizzazione. Sono algoritmi che, dato in input un naturale n>1, calcolano tutti i fattori primi di n: a tutt’oggi non è stato trovato un algoritmo di fattorizzazione di complessità polinomiale. Un algoritmo di fattorizzazione in genere cerca ottenere un obiettivo intermedio: decomporre l’input n nel prodotto n=ab, dove a,b sono naturali (non necessariamente primi) tali che 1<a,b<n (se a,b non esistono, si conclude che n è primo). Una volta trovati a,b l’algoritmo viene riapplicato separatamente ad a,b per decomporli ulteriormente (se possibile): dopo un numero finito di ripetute applicazioni dell’algoritmo, si perviene al calcolo dei fattori primi di n. Ovviamente un algoritmo “ingenuo” di fattorizzazione consiste (come nel test ingenuo di primalità) nel testare, per ogni naturale a con 2 a n , se a è divisore di n, e in caso affermativo porre n= ab (dove b=n/a): il numero dei test da effettuare è però di ordine esponenziale O( 2 x) , se x=L(n) è la lunghezza binaria di n (perché x< 2x dunque n < 2 x). Questo algoritmo trova un divisore a di n in tempi di calcolo ragionevoli solo se n ha un fattore relativamente “piccolo”. Illustriamo ora un algoritmo di fattorizzazione (dovuto a Fermat) che invece è efficiente se n è prodotto di 2 divisori relativamente “vicini fra loro” (quindi relativamente “grandi”). Algoritmo di fattorizzazione di Fermat. Supponiamo l’input n dispari: non è una limitazione in un algoritmo di fattorizzazione perché se n è pari, con successive divisioni per 2 si può fattorizzare n nella forma n=2km, con m dispari, e sostituire m al posto di n come input. L’algoritmo si basa sul seguente risultato, il quale dimostra che fattorizzare n nel prodotto di 2 numeri naturali equivale sostanzialmente a rappresentare n come differenza di 2 quadrati: Teorema (Fermat). Sia n un naturale dispari, e siano: S = { (a,b)NxN / a b, n=ab } T = { (r,s)ZxZ / r>s 0, n=r2-s2 } Allora la funzione f : S T definita da f(a,b)=((a+b)/2,(a-b)/2) è biunivoca. Dimostrazione: Si verifica facilmente che f(a,b)T se (a,b)S. Per dimostrare che f è biunivoca, basta costruire la funzione inversa: definiamo f-1 : T S ponendo f-1(r,s)=(r+s,r-s) (si verifica facilmente che f-1(r,s)S se (r,s)T). Si verifica infine che le composizioni ff-1, f-1f sono le funzioni identiche di T ed S. Come conseguenza del teorema precedente, è possibile implementare un algoritmo di fattorizzazione: dato in input un intero dispari n>1, per trovare una fattorizzazione di n nel prodotto di 2 numeri naturali a,b, basta trovare una coppia (r,s)T (quindi r,s interi, r>s0, tali che n=r2-s2) e porre (a,b)=f-1(r,s)=(r+s,r-s). Per trovare una coppia (r,s)T, possiamo fissare un intero r>0, e cercare un intero s 0 tale che si abbia r2-n=s2 : se r2-n =0 allora s=0, n=r2, a=b=r; se invece r2-n >0 si può usare un algoritmo (già esaminato in una delle precedenti lezioni) per verificare se il numero naturale r2-n è un quadrato perfetto di base naturale opportuna s. Dovendo essere r2-n=s2 0, si ha r n , dunque il valore minimo da fissare per r è r= n . (il “tetto” di n , ossia il minimo intero n ). Per quanto riguarda il valore massimo di r, notiamo che al crescere di r, cresce il valore di s (se esiste) perché r2-n=s2 con n costante, dunque cresce il corrispondente valore di a=r+s (se esiste) nella coppia (a,b). Poiché a b, n=ab, in una fattorizzazione non banale di n (dispari) il valore (teorico) minimo di b è b=3, in corrispondenza del quale il valore (teorico) massimo di a è a=n/3: poichè r=(a+b)/2, si ottiene che il valore massimo (teorico) per r è [(n/3)+3]/2=(n+9)/6, dunque si può limitare la ricerca di r a valori (n+9)/6 (parte intera di (n+9)/6). Dunque, l’algoritmo di fattorizzazione di Fermat si può implementare nel modo seguente: - input n intero dispari >1 - per r= n , n +1,……, (n+9)/6 si testa se r2-n è un quadrato perfetto di base s 0 e in caso affermativo si esce con output “n=ab dove a=r+s, b=r-s” - se nel ciclo precedente nessun test ha avuto esito positivo, si esce con output “non esistono naturali a,b<n tali che n=ab, ossia n è primo,” La complessità di tale algoritmo di Fermat è alta: il numero di valori r da testare è di ordine: O((n+9)/6- n )=O(n)=O(2x) se x=L(n) anche se ogni singolo test per verificare se r2-n è un quadrato perfetto si può effettuare con l’algoritmo visto in una lezione precedente, di complessità polinomiale . Notiamo anche, come già premesso, che il test di Fermat trova un fattore non banale di n in tempi ragionevoli se n è prodotto di 2 divisori a,b abbastanza “vicini fra loro” perché in tale caso a,b n , dunque r=(a+b)/2 n , ossia il valore r che nel ciclo permette di trovare la fattorizzazione di n è “vicino” a n , e dunque (partendo dal valore iniziale r = n ) tale valore viene trovato relativamente “presto”. Esempio. Se l’input é n=6077, n =78, (n+9)/6=1014, il ciclo si dovrà eseguire per r=78, 79,…., 1014 e si ha: r=78 r2-n =7 (non quadrato) r=79 r2-n =164 (non quadrato) r=80 r2-n =323 (non quadrato) r=81 r2-n =484 =222 ottenendo la fattorizzazione n=ab dove a=r+s=81+22=103, b=r-s=81-22=59. Algoritmo di fattorizzazione di Pollard. Premettiamo alcune considerazioni su un argomento di Calcolo delle Probabilità. Siano n,m numeri naturali con 1<n<m, S un insieme finito di cardinalità m e sia data una successione finita di n termini scelti in S (anche non distinti): a1, a2, ……, an in modo che gli ai siano scelti random in modo “uniforme” (dal punto di vista della probabilità) fra gli elementi di S (nel senso che per ogni indice i, ogni elemento dell’insieme S ha la stessa probabilità di essere scelto come elemento ai). Vogliamo calcolare la probabilità che almeno 2 degli elementi della successione coincidano. A tale scopo, calcoliamo dapprima la probabilità che tutti gli elementi della successione siano distinti. Fissato a1, la probabilità che a2 sia diverso da a1 è (m-1)/m; fissati a1,a2 distinti, la probabilità che a3 sia diverso da a1 e da a2 è (m-2)/m , quindi la probabilità che a1, a2, a3 siano tutti distinti è il prodotto [(m-1)/m][(m-2)/m]=(1-1/m)(1-2/m) Iterando il ragionamento si ottiene che la probabilità che a1, a2, ….., an siano tutti distinti è il prodotto: (1-1/m)(1-2/m)……(1-(n-1)/m). Utilizzando lo sviluppo in serie ex=1+x+x2/2!+x3/3!+…, possiamo approssimare 1+x con la funzione ex, di modo che ogni fattore del prodotto precedente è approssimato (ponendo x= -i/m con n ( n -1) 2m i=1,….,n-1) dalla funzione e . Dunque il prodotto è approssimato dalla funzione e (tenendo conto che 1+2+….+(n-1)=n(n-1)/2): per n “grande” possiamo approssimare n(n-1) con n2, di modo che la probabilità che tutti gli elementi della successione siano distinti è approssimata dalla funzione -i/m n2 2m e dunque la probabilità che almeno 2 degli elementi a1, a2, ….., an coincidano è (in modo e approssimato) la seguente funzione di n,m: p(n,m) 1 e n2 2m Se allora fissiamo un valore di probabilità p con 0<p<1, il numero n degli elementi di una successione per i quali sia p la probabilità che fra essi almeno 2 coincidano è approssimativamente: 1 )] n 2m[log e ( 1 p In particolare per esempio se fissiamo una probabilità del 50% (p=0.5), si ottiene n 1,77 m , mentre se se fissiamo una probabilità del 90% (p=0.9), si ottiene n 2,14 m . Dunque se scegliamo in successione in modo “random” elementi dell’insieme S: a1, a2, a3, ……………….. il minimo indice n per cui l’elemento an coincide con almeno uno degli elementi che lo precedono è, dal punto di vista probabilistico (con probabilità che si può rendere alta a piacere) di ordine O( m )=O(m1/2). Esempio. Un’applicazione di tale teoria é il cosiddetto “paradosso dei compleanni”: se sono scelte random un numero n di persone con 1<n<365, la probabilità che almeno 2 fra esse compiano gli n2 730 anni nello stesso giorno e mese dell’anno è 1 e ; inoltre, fissato un valore di probabilità p con 0<p<1, il numero n di persone (scelte random) per le quali è p la probabilità che fra esse almeno 2 1 )] compiano gli anni nello stesso giorno e mese dell’anno è 730[log e ( 1 p (tutto questo supponendo che giorno e mese di nascita degli esseri umani siano distribuiti in modo uniforme fra i 365 giorni dell’anno, il che non è vero in pratica). Per esempio se la probabilità fissata è del 50% (p=0,5), n 730[log e 2] 23: scegliendo 23 persone in modo random, la probabilità che fra esse almeno 2 compiano gli anni nello stesso giorno e mese dell’anno è 50% (abbastanza paradossale…..). Scegliendo invece 50 persone in modo random, la probabilità che almeno 2 fra esse compiano gli anni nello stesso giorno e mese dell’anno è addirittura 97%. Introduciamo ora l’ Algoritmo di fattorizzazione di Pollard. Il metodo di Pollard si basa sulle seguenti considerazioni. Supponiamo di volere fattorizzare un numero intero n>1, cercando un divisore non banale di n (se esiste, cioè se n non è primo). Sappiamo che n ha certamente un divisore primo p n , e formalmente (pur non conoscendo p a priori) consideriamo gli insiemi: S={0,1,2,…,p-1} T={0,1,2,…,n-1} S Sia poi F: T T una funzione che è “compatibile” con la congruenza modulo p, cioè soddisfa la condizione: F(xmodp)=F(x)modp per ogni xT (vedremo in seguito scelte opportune per tale funzione F ) . Fissato un elemento sT (seme), costruiamo una successione ai di elementi di T (con i=0,1,2,….) ponendo induttivamente: a0=s; ai=F(ai-1) per ogni i>0 (in pratica a partire dal seme s, si applica successivamente più volte F per ottenere i termini seguenti). In corrispondenza (utilizzando le riduzioni modulo p) possiamo costruire una successione bi di elementi di S ponendo bi=aimodp . Ora supponiamo che gli elementi di S siano distribuiti in modo uniforme (dal punto di vista probabilistico) nella successione bi (cioè che per ogni i la probabilità che un elemento di S coincida con bi sia uguale per tutti gli elementi): affinché tale ipotesi sia verificata, dovremo ovviamente scegliere opportunamente la funzione F . Per le considerazioni probabilistiche svolte in precedenza, il minimo indice k per cui bk coincide con uno dei termini che lo precedono (cioè bj=bk per un opportuno j<k) è dal punto di vista probabilistico di ordine O(S1/2)=O(p1/2). Poichè bj+1=aj+1modp=F(aj)modp=F(ajmodp)=F(bj), e analogamente bk+1=F(bk), da bj=bk segue bj+1=bk+1 . Per induzione si ottiene facilmente: bj+m=bk+m per ogni m 0 (*) Se fissiamo un qualunque indice i j si ha allora, applicando la (*) con m=i-j): bi=bj+(i-j)=bk+(i-j)=bi+(k-j) Per induzione si ottiene facilmente: bi =bi+t(k-j) per ogni t 0 (fissato l’indice i j) Comunque presi allora due indici i,r con i,r j, se ir (mod k-j) si ha bi=br (in pratica la successione bi dall’indice j in poi diventa ciclica con “periodo” k-j, nel senso che dal termine di indice j in poi i termini coincidono ogni k-j posizioni): infatti basta porre (se per esempio r i) (r-i)=t(k-j) con t 0 e da quanto precede segue appunto bi =bi+t(k-j)=br . Esempio: se j=3, k=9, la successione diventa ciclica con periodo 6 dal termine di indice 3 in poi: infatti dall’indice j=3 in poi, due qualunque termini della successione, i cui indici differiscano per un multiplo di k-j=6 (cioè i cui indici siano congrui modulo 6), coincidono fra loro. Rappresentando graficamente la situazione, si ottiene (per j=3, k=9): b5=b11=b17=…. b4=b10=b16=…. b6=b12=b18=…. b3=b9=b15=…. b2 b1 b0 b8=b14=b20=…. b7=b13=b19=…. Notare la forma geometrica che richiama la struttura della lettera greca (da cui il nome dell’algoritmo).