12 2. Algoritmi e Programmi Dato un problema, per arrivare ad un programma che lo risolva dobbiamo: individuare di cosa dispongo: gli input; definire cosa voglio ottenere: gli output; trovare un metodo di risoluzione che compone di un algoritmo, di strutture dati su cui l algoritmo opera. Codificare algoritmo e strutture dati in un linguaggio comprenbile dalla macchina (in questo Corso useremo il linguaggio C) Un algoritmo è un ineme di regole (n ambigue) che ci permette di calcolare i risultati voluti a partire dai dati di ingresso. Gli algoritmi posso essere descritti in vari modi; tra questi: in un linguaggio pseudonaturale, con un diagramma di flusso, con un linguaggio di programmazione. 2.1. Diagrammi di Flusso I diagrammi di flusso so un linguaggio grafico per descrivere algoritmi. I valori utilizzati per il calcolo so contenuti in variabili. Posamo paragonare una variabile ad un contenitore che contiene un valore che può essere letto e può essere rimpiazzato con un altro valore. Il calcolo è fatto attraverso la esecuzione di istruzioni. L istruzione fondamentale è l assegnamento di un valore ad una variabile. Nel calcolo dei valori, utilizziamo espresoni che so formate con valori numerici (costanti), variabili, e operatori. Un diagramma di flusso rappresenta le istruzioni entro blocchi e la sequenza di esecuzione delle istruzioni mediante l utilizzo di frecce. Noi condereremo i seguenti tipi di blocco: v expr v expr test Input : l input fornito dall utente è assegnato alla variabile v Output : visualizza in output il valore della espresone expr Assegnazione: alla variabile v è assegnato il valore della espresone expr Decione : a questo blocco seguo più istruzioni. Se l espresone test ha valore vero, viene scelta l uscita, mentre se ha valore falso, viene scelta l uscita. Ombreggiatura : Un blocco ombreggiato nasconde un commento o u zoom.
13 Conderiamo alcuni esempi di diagrammi di flusso. Esempio 13 Vogliamo calcolare le radici di una equazione di secondo grado: a x 2 + b x + c = 0, essendo a, b e c tre numeri reali. Supponiamo iltre che a a 0, altrimenti l'equazione n è più di secondo grado, ma di primo. Sappiamo che: x 1,2 = b ± b2 4 a c 2 a Iltre, le due radici so coincidenti, se il delta = (b 2-4 a c) è uguale a zero, e so distinte se 0. In questo secondo caso, le radici so reali se > 0, oppure complesse coniugate, se < 0. Nella Figura 4 è riportato il diagramma di flusso che descrive il programma per il calcolo delle due radici. a, b, c so numeri reali a 0 Start a,b,c = b**2-4 a c = 0 Se > 0 le radici so reali Se < 0 le radici so complesse coniugate x1 = x2 = -b/(2 a) x1 = [-b + Sqrt( )]/(2 a) x2 = [-b - Sqrt( )]/(2 a) x1, x2 x1, x2 Stop Figura 4 Diagramma di flusso che descrive il calcolo delle due radici dell'equazione di secondo grado a x 2 + b x + c = 0.
14 Il programma in Figura 4 ha la caratteristica di essere lineare, e cioè di essere costituito da alcune sequenze alternative di istruzioni, in nessuna delle quali c'è un ritor ad un passo precedente. Esempio 14 Vogliamo calcolare la media di una serie di n numeri x 1, x 2,, x k,, x n mediante la formula: m = 1 n n L'algoritmo può sommare i numeri, leggendoli u alla volta e mantenendo una somma parziale, finché n ha sommato tutti gli n numeri. I risultati parziali so contenuti nella variabile m. Ad ogni iterazione (contata dall'intero k) incrementiamo m dell'ultimo valore x k letto. L algoritmo termina quando k raggiunge il valore n. L'algoritmo è descritto nella Figura 5. k=1 x k Start n m = 0 k = 1 Read x(k) m = m + x(k) k = n k = k+1 m = m/n Stop Print m Figura 5 Calcolo della media di n numeri, con n 1. Osserviamo che m viene posto inizialmente a zero affinchè le operazioni in ogni ciclo a le stesse. Si sarebbe potuto inizializzare il valore di m a x 1, ma, in
15 questo caso, il primo numero sarebbe stato trattato in modo diverso dagli altri, e così pure le operazioni nella prima esecuzione del ciclo sarebbero state diverse. Osserviamo iltre che, affinchè l'algoritmo a corretto, deve essere n 1. Notiamo iltre che l'algoritmo di Figura 5 n è lineare come quello di Figura 4: esso presenta una ripetizione di una serie di istruzioni, controllate dal raggiungimento di un valore specifico (n) di ripetizioni. Questo algoritmo ha una struttura iterativa. Esempio 15 Un caso più semplice del precedente è quello in cui voglio sommare i primi n numeri interi a partire da 1. In questo caso, il contatore k è anche il valore che vuole sommare e n c'è bisog di leggere dati esterni. L'algoritmo è riportato in Figura 6. n acc = 0 k = 0 acc = acc + k k = k + 1 k = n Print acc Figura 6 Calcolo della somma dei primi n numeri interi, a partire da 1. L'algoritmo dovrà sommare i numeri a partire da 1 finché n ha sommato tutti i primi n numeri (il primo da sommare è 1). I risultati parziali di questa somma so contenuti in una variabile, che chiameremo acc ("accumulatore") e nella variabile k registreremo i numeri succesvi da sommare. Ad ogni iterazione incrementiamo k di 1 e sommiamo il suo valore all accumulatore. L algoritmo termina quando k raggiunge il valore n. Esempio 16 Supponiamo di avere n numeri x 1, x 2,, x n, ordinati in modo crescente. Dato un altro numero y, vogliamo sapere se y è uguale a u degli x k oppure in quale intervallo definito dagli x k esso è incluso. Uamo un algoritmo parallelo, descritto nella Figura 7 per il caso n = 3. Conderiamo 2 n comparatori, a ognu dei quali è associati u dei numeri x k, disposti come in Figura 7.
16 Quando un test è terminato, il ramo che è vero attiva un display di luce verde se y è mire del valore associato al comparatore, rosso se è maggiore e giallo se è uguale. y y x1 y x2 y x3 y = x1 y = x2 y = x3 1-1 1+ 2-2 2+ 3-3 3+ Figura 7 Determinazione della pozione di un numero y rispetto a una sequenza crescente di n numeri. Quando i comparatori han terminato di operare, ottengo dei pattern di attivazione che corrispondo ai diver intervalli. Se c'è un display giallo acceso (se ne può avere solo u, essendo i numeri tutti diver), sappiamo che y è uguale al valore mostrato dal display. Se i display so tutti verdi, sappiamo che y è mire di tutti gli x k, mentre display tutti ros indica che y è maggiore di tutti gli x k. Se han display verdi e ros, y è compreso tra il display rosso e quello verde che so adiacenti. La struttura del diagramma di flusso in Figura 7 è parallela. Esempio 17 Data una matrice A n,m = {a j,k 1 j n, 1 k m}, con n righe ed m colonne, vogliamo calcolare la somma di tutti gli elementi di A. Posamo farlo sommando prima tutti gli elementi della stessa riga e poi sommando i risultati ottenuti. Sia T la somma totale che vogliamo ottenere e a R j la somma degli elementi della riga j-ma. Abbiamo: R j = m a j,k T = n R j o anche T = n m a j,k k=1 j=1 j=1 k=1 L'algoritmo è descritto nella Figura 8.
17 A, n, m T = 0 j = 1 Calcola R(j) T = T + R(j) j = j + 1 j = n Print T Figura 8 Calcolo della somma T di tutti gli elementi di una matrice rettangolare A con n righe ed m colonne. La somma è fatta prima per righe e poi per colonne. Nel diagramma di flusso della Figura 8, il calcolo di R j deve essere specificato a parte, come descritto nella Figura 9. R(j) = 0 k = 1 R(j) = R(j) + a(j,k) Figura 9 -Espanone del blocco Calcola R j della Figura 8. k = k + 1 k = m Output R(j)
18 Il diagramma di flusso completo sarà quello della Figura 10. A,n,m T = 0 j = 1 R(j) = 0 k = 1 R(j) = R(j) + a(j,k) k = k + 1 k = m j = j + 1 j = n T = T + R(j) Print T Figura 10 Diagramma completo per il calcolo della somma degli elementi di una matrice. Il diagramma di flusso della Figura 10 ha una struttura annidata.