Matematica Discreta II - Matematica e Informatica

Matematica Discreta II
Lezione del giorno 21 novembre 2007
Esaminiamo la complessità dell’algoritmo della lezione precedente, che calcola la parte intera della
radice n-esima di un input naturale x (fissato il numero naturale n>1).
Il numero k di iterazioni nel passo 2) è maggiorato da un’espressione polinomiale nel numero t di
cifre binarie dell’input x: infatti k coincide con il numero di blocchi di n cifre binarie che è
ovviamente (t/n)+1.
Inoltre nell’istruzione a) si deve calcolare una potenza con esponente n, che si ottiene moltiplicando
la base per sé stessa n volte, ma notiamo che essendo x<2t (perché x ha t cifre binarie) ed essendo
x2n (perché si è escluso il caso banale x<2n in cui la parte intera cercata è 1) si ha n<t.
Dunque il numero di moltiplicazioni nell’algoritmo è <t(t/n)+1) e l’algoritmo ha complessità
polinomiale nel numero di cifre binarie t dell’input x.
Ovviamente tale algoritmo si può trasformare facilmente in un algoritmo che testi se un numero
naturale x>1 sia la potenza n-esima esatta di un opportuna base naturale, dove n è un naturale >1
fissato.
Infatti basta calcolare con l’algoritmo precedente la parte intera yk della radice n-esima di x, ed
verificare se essa coincide esattamente con la radice n-esima di x; basta quindi aggiungere
all’algoritmo una istruzione che esegua il calcolo della potenza ykn e verifichi se il risultato è =x : in
caso affermativo x è una potenza n-esima esatta di base naturale (e la base è proprio yk), in caso
negativo non lo é.
Ovviamente anche tale algoritmo ha complessità polinomiale, perché l’istruzione aggiunta
comporta il calcolo di una potenza con esponente n<t.
Infine possiamo notare che si può anche costruire un algoritmo che, dato in input un naturale x>1,
testi se x è potenza n-esima di un opportuna base naturale, per un opportuno naturale n>1 (notare
che n non è fissato a priori).
Infatti abbiamo notato sopra che in ogni caso si ha n<t, quindi basta ripetere l’algoritmo precedente
per tutti i valori n con 2nt e se per nessuno di tali valori il test dà esito positivo concludere con
certezza che l’input x non è potenza n-esima di un opportuna base naturale, per nessun naturale
n>1.
Osservando che in questo algoritmo si ripete l’algoritmo precedente per un numero di volte che è
maggiorato da un’espressione polinomiale nel numero di cifre binarie t dell’input x, si può
concludere che anche quest’ultimo algoritmo ha complessità polinomiale.
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 di decomporre 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 in [2, n ], se a è divisore di n, e in caso affermativo porre b=n/a: il
numero dei test da effettuare è maggiorato da n =n1/2 < 2t/2 , se t è il numero di cifre binarie di n e
quindi n<2t (complessità esponenziale).
Questo algoritmo trova un fattore a di n in tempi di calcolo ragionevoli se n ha un fattore
relativamente “piccolo”.
Illustriamo ora un algoritmo di fattorizzazione che invece è efficiente se n ha un fattore
relativamente “grande”.
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.
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 esiste una funzione biunivoca f : S  T.
Dimostrazione:
Definiamo f(a,b)=((a+b)/2,(a-b)/2) per ogni (a,b)S (notare che essendo n=ab, anche a,b sono
dispari dunque (a+b)/2, (a-b)/2 sono interi).
Essendo a,b>0 ed ab, si ha (a+b)/2>(a-b)/20, ed inoltre [(a+b)/2]2-[(a-b)/2]2=ab=n, dunque
f(a.b)T.
La funzione f è iniettiva perché da f(a,b)=((a+b)/2,(a-b)/2)=f(c,d)=((c+d)/2,(c-d)/2) segue
(a+b)/2=(c+d)/2, (a-b)/2=(c-d)/2 e si ricava facilmente a=c, b=d.
Dimostriamo la surgettività di f: data una coppia (r,s)T, poniamo a=r+s, b=r-s; essendo r>s0 si
ha ab con a,bN, ed n=r2-s2=ab, dunque (a,b)S, ed infine f(a,b)=((a+b)/2,(a-b)/2)=(r,s).
Come conseguenza del teorema precedente, è possibile implementare il seguente 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>s0, tali che n=r2-s2) e
porre (a,b)=f-1(r,s)=(r+s,r-s) (vedere la dimostrazione della surgettività di f nel teorema).
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 il test già esaminato
in precedenza 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
cosiddetto “tetto” di  n , ossia il minimo intero non inferiore a  n ).
Per quanto riguarda il valore massimo di r, notiamo che al crescere di r, cresce il valore
corrispondente di a (se esiste) nella coppia (a,b): se r1>r, e se n=r2-s2=r12-s12 , allora s1>s, dunque
a=r+s<a1=r1+s1 . Poiché in una fattorizzazione non banale di n (dispari) certamente é an/3 (perché
n=ab con b3), e ricordando che r=(a+b)/2, si ottiene che il valore massimo per r è ≤
[(n/3)+3]/2=(n+9)/6, quindi il valore massimo di r è la 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
opportuna 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 “n è primo”
Notiamo che ogni passo del ciclo ha complessità polinomiale (test del quadrato perfetto), ma il
numero dei passi è maggiorato da funzioni lineari di n, n , e quindi da funzioni esponenziali nel
numero di cifre binarie di n (la complessità è perciò esponenziale).
Notiamo anche, come già premesso, che il test di Fermat trova un fattore non banale di n in tempi
ragionevoli se n ha un fattore abbastanza “ grande” cioè  n , perché in tale caso, se n=ab, anche
b n , dunque r=(a+b)/2 n , ossia il valore r che nel ciclo permette di trovare la fattorizzazione
di n è “vicino” alla radice quadrata di 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.