Lezione del 13 marzo 2009 Osservazione: Se la funzione TimeA(n), che calcola il numero di operazioni elementari eseguite dall’algoritmo A per un input di dimensione n (nel caso peggiore), viene calcolata in funzione dell’input x (e non della sua dimensione n) e se essa risulta di ordine polinomiale O(xk), possiamo notare che l’algoritmo A ha allora complessità esponenziale in n: infatti esisterà una costante C tale che il numero di operazioni elementari eseguite dall’algoritmo A per un input x sia Cxk, e tenendo conto che x < 2n (essendo n la lunghezza binaria di x) si ricava: TimeA(n) C(2k)n dunque TimeA(n) è di ordine esponenziale O(an) dove a=2k . Complessità di alcuni algoritmi aritmetici. a) Costruiamo un algoritmo A=Somma(x,y) per calcolare la somma x+y di due numeri naturali x,y dati in input: supponiamo che, se n=L(x),m=L(y) sono le lunghezze binarie degli addendi, si abbia nm. L’algoritmo di somma si può eseguire con gli stessi metodi che si usano per sommare numeri naturali rappresentati in base 10, utilizzando le operazioni elementari di somma sui singoli bits: - si incolonna la rappresentazione binaria di y sotto quella di x, aggiungendo (se n>m) n-m bits=0 alla sinistra dei bits di y - si inizializza il valore del riporto c=0 - procedendo da destra verso si sinistra si somma ogni bit di x con il bit dello stesso posto di y (con l’operazione elementare BitSum di somma di bits), ottenendo ad ogni passo una delle cifre binarie di x+y e un nuovo valore del riporto c - se l’ultimo riporto è c=1 si aggiunge a sinistra un ulteriore bit=1 nel risultato x+y - si esce con output x+y Per esempio se x=(110011)2, y=(1101) allora: 110011+ 001101 1000000 dunque il risultato della somma è x+y=(1000000)2. Schematizzando l’algoritmo: 1) input x=(an-1an-2…..a1a0)2 , y=(bm-1bm-2…..b1b0)2 con n=L(x) m=L(y) 2) se n>m si pone bm=bm+1=…=bn-1=0 3) c=0 4) per i=0,1…,n-1 si calcola BitSum(ai,bi,c)=(t,c1) e si pone zi=t, c=c1 5) se c=1 si pone zn=1 6) si esce con output x+y=(znzn-1…..z1z0)2 In tale algoritmo si eseguono n operazioni elementari, dunque Time A(n)=O(n), e l’algoritmo ha complessità polinomiale (lineare). b) Costruiamo un algoritmo A=Diff(x,y) per calcolare la differenza x-y di due numeri naturali x,y (con x>y) dati in input: se n=L(x),m=L(y) sono rispettivamente le lunghezze binarie, si avrà certamente nm. Si può ricondurre il caso a quello dell’algoritmo Somma(x,y) con il seguente ragionamento: consideriamo il numero binario y1 ottenuto da y sostituendo ogni bit 0 con 1 e viceversa e poi aggiungendo a sinistra (n-m) bits =1 (quindi y+y1 è un numero binario con n bits tutti=1 cioè y+y1=2n-1). Si ha dunque y+(y1+1)=2n (y1+1 è il cosiddetto “complemento a 2 di y”) da cui x-y=x+(y1+1)-2n. Quindi per ottenere x-y basta sommare x con il complemento a 2 di y e poi sottrarre 2n al risultato (il che equivale ad elidere l’ultimo bit =1 a sinistra): poiché nel calcolo di x+(y1+1) (addendi lunghezza ≤n ) si esegue (vedere algoritmo Somma(x,y)) un numero n di operazioni elementari, si conclude che TimeA(n)=O(n), e anche questo algoritmo ha complessità polinomiale (lineare). c) Costruiamo un algoritmo A=Prod(x,y) per calcolare il prodotto x+y di due numeri naturali x,y dati in input: supponiamo che, se n=L(x),m=L(y) sono le lunghezze binarie dei fattori, si abbia nm. L’algoritmo di prodotto si può eseguire con gli stessi metodi che si usano per moltiplicare numeri naturali rappresentati in base 10, utilizzando le operazioni elementari di somma sui singoli bits. Si costruiscono (al più) m righe (una riga in meno per ogni bit =0 in y) ognuna consistente nella copia del numero binario y shiftato opportunamente verso sinistra di un certo numero di posti (il che equivale ad aggiungere a destra dei bits =0) e si sommano tutte le righe (pensate come numeri binari): per contare il numero di operazioni elementari, supponiamo di sommare le righe 2 alla volta (la prima con la seconda, la terza con il risultato della somma della prima con la seconda etc.) eseguendo in totale un numero ≤m-1 di somme fra numeri di lunghezza ≤n+m, e poiché ognuna di tali somme corrisponde ad un numero ≤n+m di operazioni elementari di somma di bits, in totale il numero di operazioni elementari nell’algoritmo è ≤(n+m)(m-1)<n2 ossia TimeA(n)=O(n2), e anche questo algoritmo ha complessità polinomiale (quadratica). Per esempio se x=(11011)2, y=(1011) allora: 11011x 1011 11011 110110 11011000 100101001 (si suppone di sommare la prima riga 11011 con la seconda 110110 e poi sommare il risultato con la terza riga 11011000) dunque il risultato del prodotto è xy=(100101001)2. Tale algoritmo di prodotto non è il più efficiente: esiste un algoritmo più sofisticato che ha complessità O(nlog(n)log(log(n))) “minore” di O(n2) (perché limn[nlog(n)log(log(n))]/n2=0). Spesso, per i nostri scopi, ci limiteremo a costruire un algoritmo che risolva un problema senza indagare se sia il più efficiente in termini di complessità.