Matematica Discreta I - Matematica e Informatica

Matematica Discreta II
Lezione del giorno 18 ottobre 2007
Complessità degli algoritmi
Gli algoritmi di cui ci occuperemo avranno in input uno (o più) numeri naturali. Considereremo
operazioni elementari in un algoritmo quelle di somma, sottrazione, prodotto, divisione con
quoziente e resto.
In un algoritmo definiremo taglia dell’input il numero x di cifre dell’input stesso, rappresentato in
una base fissata b (in generale b=2 o b=10): si dimostra facilmente che le definizioni che daremo
sono indipendenti dalla base scelta per rappresentare l’input.
Distingueremo alcune classi di algoritmi in base alla loro complessità, ossia in base al numero di
operazioni elementari, come funzione della taglia dell’input.
Se n=n(x) è il numero di operazioni elementari eseguite nell’algoritmo (in funzione di un input di
taglia x) diremo che l’algoritmo ha complessità polinomiale se n=n(x) è  di una espressione
polinomiale in x:
n  a0+a1x+a2x2+……+akxk
con ai costanti reali
Si può osservare che la complessità è riferita a una quantità (funzione della taglia x dell’input) che
maggiora il numero di operazioni con input di taglia x, ma ciò non impedisce che per particolari
valori dell’input il numero di operazioni sia molto minore del maggiorante previsto.
L’espressione polinomiale in x che maggiora n può essere sempre ridotta ad un solo monomio di
grado massimo osservando che:
n  a0+a1x+a2x2+……+akxk  a0xk+a1xk+a2xk+…..+akxk = (a0+a1+a2+…..+ak)xk
Dunque un algoritmo è di complessità polinomiale quando esistono c, k costanti reali (con k intero)
tali che n=n(x)cxk (dove n è il numero di operazioni, x la taglia dell’input).
Diremo che un algoritmo ha complessità esponenziale se esistono c, k costanti reali tali che
n=n(x)c k x (dove come sopra n è il numero di operazioni, x la taglia dell’input).
Nota: esistono anche algoritmi di complessità diversa da polinomiale o esponenziale
(subesponenziale etc.). Inoltre in molti testi il concetto di operazione elementare è definito
riferendosi a operazioni sui singoli bits dell’input ma è facile verificare che si ottengono definizioni
equivalenti a quelle precedenti.
Un algoritmo di complessità polinomiale non è in assoluto più efficiente di uno di complessità
esponenziale, ma il maggiorante del numero di operazioni elementari cresce ovviamente molto
meno rapidamente al crescere della dimensione dell’input.
Esempio:
Siano dati l’algoritmo A di complessità polinomiale con numero di operazioni elementari n=n(x) 
x7 (in funzione della taglia x dell’input), e l’algoritmo B di complessità esponenziale con numero di
operazioni elementari s=s(n)  2n. Per un input di dimensione n=32 (cifre binarie) l’algoritmo A
esegue un massimo di 327=235 operazioni, mentre l’algoritmo B ne esegue un massimo di 232
(quindi un numero inferiore): se ogni operazione è eseguita in 1 milionesimo di secondo,
l’algoritmo A impiega un massimo di circa 9 ore e mezza, l’algoritmo B circa 1 ora e 12 minuti.
Ma per un input di dimensione doppia n=64 (cifre binarie), l’algoritmo A esegue un massimo di 647
operazioni (circa 52 giorni), l’algoritmo B un massimo di 264 operazioni (circa 585.00 anni !!!).
Per la soluzione di alcuni problemi matematici sono già stati trovati algoritmi di complessità
polinomiale: per esempio nel campo dei test di primalità (algoritmi che, dato in input un naturale
n>1, escono con output “n è primo” oppure “n non è primo”) è stato trovato nel 2002 un algoritmo
detto AKS (autori Agrawal, Kayal, Saxena) di complessità polinomiale.
Per la soluzione di altri problemi esistono algoritmi di complessità esponenziale (o
subesponenziale) ma, a tutt’oggi, non sono disponibili algoritmi di complessità polinomiale (anche
se in futuro potrebbero essere implementati, sempre che ne esistano): uno di questi casi riguarda gli
algoritmi di fattorizzazione, che, dato in input un naturale n>1, calcolano in output la lista dei
fattori primi di n.
Tornando al gruppo Zm* degli elementi simmetrizzabili del monoide Zm rispetto al prodotto, poiché
esso contiene tutte le classi [a] con a=1,2,….,m-1, tali che a,m sono coprimi, la sua cardinalità
coincide con la ben nota funzione di Eulero (m).
Ricordiamo che, se è nota la fattorizzazione di m in prodotto di potenze di numeri primi distinti:
m = p1k1 p 2 k 2 .......ps k s
allora è possibile calcolare facilmente la funzione di Eulero di m mediante la formula (ottenuta
applicando il principio di inclusione-esclusione):
(m) = m(1-1/p1)(1-1/p2)……(1-1/ps)= p1k1  1p 2 k 2  1.......ps k s  1 (p1-1)(p2-1)…..(ps-1).
Viceversa la conoscenza del valore (m) può portare al calcolo dei fattori primi di m: per esempio
se m=pq è il prodotto di 2 primi distinti p,q, allora (m)=(p-1)(q-1)=pq-(p+q)+1=m-(p+q)+1, da cui
si ottiene il sistema somma-prodotto nelle incognite p,q:
pq = m
p+q=m+1-(m)
e si possono calcolare p,q come soluzioni dell’equazione di 2° grado x2-(p+q)x+pq=0.
Da quanto precede si deduce che il problema della fattorizzazione di m e quello del calcolo di (m)
hanno la stessa complessità: attualmente non esiste un algoritmo di complessità polinomiale per il
calcolo di (m).
Sottogruppi di un gruppo.
Siano A un gruppo (in notazione moltiplicativa con neutro 1A, e con simmetrico x-1=inverso di x,
per ogni elemento x) e B un suo sottoinsieme non vuoto.
Diremo che B è sottogruppo di A se B è chiuso rispetto all’operazione di A e se B anch’esso
gruppo, rispetto alla stessa operazione di A, quindi se valgono le proprietà:
1) chiusura di B: dati x,yB si ha xyB
2) associativa in B
3) esistenza di un neutro in B
4) per ogni elemento xB esistenza dell’inverso di x in B
Notiamo che la 2) è superflua, in quanto la proprietà associativa già vale in A.
Riguardo alla 3) e alla 4) dimostreremo che l’elemento neutro di B (se esiste) coincide con
l’elemento neutro di A, e che , dato l’elemento xB, l’inverso di x in B (se esiste) coincide con
l’inverso di x in A.
Premettiamo una proprietà importante di ogni gruppo A, detta legge di cancellazione:
a) se x,y,zA e se xz=yz, allora x=y (legge di cancellazione a destra)
b) se x,y,zA e se zx=zy, allora x=y (legge di cancellazione a sinistra)
Per dimostrare per esempio la a) si può moltiplicare a destra per z-1 ottenendo, per l’associativa:
(xz)z-1 = (yz)z-1 ; x(zz-1) = y(zz-1) ; x1A = y1A ; x = y
(analogo ragionamento per dimostrare la b)).
Tornando alle condizioni 1),2),3),4) del sottogruppo B, come premesso osserviamo che:
- se B ha elemento neutro 1B, si ha 1B=1A ; infatti dalle eguaglianze 1B1B=1B=1B1A si ottiene la tesi
(per cancellazione a sinistra di 1B)
- se un elemento xB ha inverso x’ in B, allora x’=x-1 (dove x-1 indica il simmetrico di x in A):
infatti dalle eguaglianza xx’=1B=1A=xx-1 si ottiene la tesi (per cancellazione a sinistra di x).
Riassumendo, per verificare se B è sottogruppo del gruppo A, si devono verificare le seguenti
proprietà:
1) chiusura di B: dati x,yB si ha xyB
2) il neutro di A appartiene a B
3) per ogni elemento xB, l’inverso x-1 di x in A appartiene a B
Queste 3 proprietà si possono ridurre ad una sola, come dimostra il seguente:
Teorema.
Se B è un sottoinsieme non vuoto del gruppo A: B è sottogruppo di A  x,yB si ha xy-1B
Dimostrazione:
: Se x,yB, per la proprietà 3) dei sottogruppi si ha y.-1B, e per la proprietà 1) si ottiene xy-1B.
: Dimostriamo le proprietà 1),2),3) dei sottogruppi.
2) : Preso un generico elemento bB, e posto x=b, y=b, dall’ipotesi segue xy-1=bb-1=1AB
3) : Preso un generico elemento bB, e posto x=1AB, y=b, dall’ipotesi segue xy-1=1Ab-1= b-1B
1) : Presi i generici elementi b,cB, e posto x=b, y=c-1B, dall’ipotesi segue xy-1=b(c-1)-1= bcB
Esempio:
Considerato il gruppo dei razionali non nulli Q*=Q-{0} rispetto al prodotto, e il sottoinsieme B
contenente tutte le potenze 2k di base 2 ed esponente k intero relativo, si ha che B è sottogruppo di
Q* in quanto x=2k,y=2hB si ha xy-1=2k-hB .