Algoritmi e Strutture Dati Autunno 02 Algoritmi per il Massimo Comun Divisore Dip. Informatica ed Appl. Prof. G. Persiano Università di Salerno In questi appunti presentiamo un algoritmo per il calcolo del massimo comun divisore di due interi. Il problema del calcolo del massimo comun divisore è antichissimo e risale ad Euclide che propose un algoritmo nelle proposizioni 1-3 del VII libro degli Elementi [1]. Euclide visse ad Alessandria intorno al 300 A.C. e quindi il problema del massimo comun divisore è uno dei primi problemi algoritmici studiati e l’algoritmo di Euclide è uno dei più antichi algoritmi noti. Definizione 1 Siano a e b due interi. Diciamo che a divide b (in simboli a|b) se esiste un intero k tale b = a · k. Definizione 2 Siano a e b due interi non entrambi uguali a zero. Diciamo che un intero d è il massimo comun divisore di a e b (in simboli d = (a, b)) se 1. d|a e d|b; 2. se d1 |a e d1 |b allora d1 |d; 3. d > 0. È facile osservare che il massimo comun divisore di due interi esiste sempre ed è unico. Infatti se a = 0, allora (a, b) = |b|. Se invece a, b 6= 0, consideriamo l’insieme p1 , · · · , ps dei primi che dividono a o b (o entrambi). Allora esistono interi (positivi) α1 , . . . , αs e β1 , . . . , βs tali che β1 αs βs 1 a = ±pα 1 · · · ps e b = ±p1 · · · ps . Quindi l’intero min(α1 ,β1 ) d = p1 · · · psmin(αs ,βs ) è il massimo comun divisore di a e b. La discussione precedente suggerisce un algoritmo per il calcolo del massimo comun divisore: fattorizzare i due interi e moltiplicare i fattori primi comuni con il minimo esponente. L’algoritmo purtroppo richiede la fattorizzazione dei due interi per cui non si conosce nessun algoritmo efficiente. Vogliamo invece un algoritmo efficiente che calcoli (a, b) in un numero di passi polinomiale nella lunghezza della rappresentazione binaria di a e b. Un primo algoritmo. Euclide [1] ci propone il seguente algoritmo. EuclideMCDLento(a, b) (a, b) ← (max(|a|, |b|), min(|a|, |b|)); while b > 0 (a, b) ← (max(b, a − b), min(b, a − b)); end return a; Ad esempio, se abbiamo a = 30 e b = 12, l’algoritmo EuclideMCDLento procede nel modo seguente: (30, 12) = (18, 12) = (12, 6) = (6, 6) = (6, 0) = 6. Correttezza. La correttezza dell’algoritmo EuclideMCDLento può essere derivata dal seguente lemma. Lemma 1 Siano a e b due interi non entrambi uguali a zero con a ≥ b. 1. (a, 0) = |a|; 2. (a, b) = (b, a − b). Dim.: Dimostrazione lasciata allo studente volenteroso. Algoritmi per il Massimo Comun Divisore 2 Analisi. Osserviamo però che l’algoritmo EuclideMCDLento per valori di b molto piú piccoli di a impiega tempo proporzionale ad a. Ad esempio, se b = 1 il ciclo while è eseguito esattamente a volte e quindi l’algoritmo EuclideMCDLento impiega tempo esponenziale nella lunghezza dell’input (a ha lunghezza n = dlog(a + 1)e mentre l’algoritmo impiega tempo proporzioanle ad a = O(2n )). Versione efficiente dell’algoritmo di Euclide. L’algoritmo EuclideMCDLento può essere migliorato osservando che ripetute sottrazioni dello stesso intero possono essere “collassate” in una sola operazione calcolando il resto della divisione di a per b (ovvero a mod b). Ad esempio, il calcolo di (715, 26) richiede successive sottrazioni di 26 finquando non si ottiene (dopo 27 iterazioni) un intero minore di 26. Lo stesso risultato si può ottenere in un solo passo calcolando 715 mod 26. Pertanto consideriamo il seguente algoritmo. EuclideMCD(a, b) (a, b) ← (max(a, b), min(a, b)); while b > 0 (a, b) ← (b, a mod b); end return a; Analisi dell’algoritmo efficiente. nel modo seguente. EuclideMCDRec(a, b) (a, b) ← (min(|a|, |b|), max(|a|, |b|)); if a = 0 then return(b); return EuclideMCDRec(b, a mod b); Per analizzare l’algoritmo EuclideMCD, riscriviamo EuclideMCD EuclideMCDMod(a, b) (r−1 , r0 ) ← (max(a, b), min(a, b)); k = 0; while rk > 0 k = k + 1; rk = rk−2 mod rk−1 ; end return rk−1 ; Lemma 2 Per ogni k tale che rk > 0, abbiamo che rk ≤ 12 rk−2 . Dim.: Consideriamo due casi. 1. rk−1 ≤ 12 rk−2 . Poiché rk = rk−2 mod rk−1 abbiamo che rk < rk−1 e quindi rk < 12 rk−2 . 2. rk−1 > 21 rk−2 . Poiché rk−1 = rk−3 mod rk−2 abbiamo che rk−1 < rk−2 < 2rk−1 e quindi rk = rk−2 mod rk−1 = rk−2 − rk−1 ≤ 12 rk−2 . Abbiamo quindi il seguente teorema. Teorema 1 Il ciclo while dell’algoritmo EuclideMCDMod è eseguito al più 2 log(max a, b) volte, ove log x denota il logaritmo in base 2 di x. Bibliografia [1] Euclide. Elementi. Disponibile all’URL http://aleph0.clarku.edu/ ˜ djoyce/java/elements/elements.html. La versione aggiornata di questo documento si trova all’url http://www.dia.unisa.it/ ˜giuper/ASDI/note/gcd.pdf.