algoritmi_elementari
algoritmi_elementari
Ottobre 2007
Nelle pagine seguenti sono riportati alcuni problemi e le relative soluzioni costitui-
te da semplici algoritmi espressi con una “pseudo-codifica” in linguaggio naturale. Per
ogni algoritmo risolutivo si riporta anche un diagramma di flusso e la complessità com-
putazionale nel caso peggiore. Viene anche proposta una codifica in linguaggio C del-
l’algoritmo, in modo da poter verificare immediatamente il modo con cui le strutture
di controllo algoritmiche vengono tradotte nell’ambito di un programma scritto con un
linguaggio di programmazione procedurale.
Nella prima sezione di questa breve nota, sono riportati in estrema sintesi i concet-
ti fondamentali che devono essere compresi per poter iniziare la progettazione di un
algoritmo e la valutazione della sua complessità computazionale. Per ciascuno di tali
argomenti sono stati scritti capitoli interi di libri di informatica teorica e di programma-
zione, dunque è a tali volumi (alcuni dei quali sono citati in bibliografia) che si rimanda
per il necessario approfondimento di quanto viene qui riportato sotto forma di appunti.
1
se... allora... altrimenti: consente all’esecutore di valutare un’espressione booleana (una
condizione logica) il cui valore può essere solo “vero” o “falso”; in base al risulta-
to della valutazione della condizione, vengono eseguite delle istruzioni oppure, in
caso contrario, delle altre (es.: “se a > 0 allora a = a − 1 altrimenti a = 20”);
vai al passo ...: consente di saltare ad una specifica istruzione dell’algoritmo anziché
eseguire l’istruzione successiva nella sequenza naturale;
3. la struttura iterativa, in cui una sequenza di istruzioni viene ripetuta più volte fin-
tanto che risulta verificata una determinata condizione; la condizione può essere
valutata all’inizio della sequenza di istruzioni da ripetere, oppure al termine della
sequenza, a seconda dei casi e delle necessità; quando la condizione risulta falsa,
il flusso dell’algoritmo prosegue con la prima istruzione esterna alla sequenza da
ripetere.
Le tre strutture possono essere concatenate fra loro oppure possono essere nidificate
una dentro l’altra; le tre strutture non si devono invece mai “accavallare”: in tal caso si
finisce per progettare un algoritmo non elegante e che risulterebbe assai difficile codifi-
care con le istruzioni di un linguaggio di programmazione strutturato, come il linguag-
gio C o il linguaggio Java. D’altra parte il Teorema Fondamentale della Programmazione
Strutturata (Jacopini e Böhm) garantisce che qualunque algoritmo scritto senza rispet-
tare le regole della programmazione strutturata può anche essere ridefinito attenendosi
strettamente a tali regole.
2
Un aspetto particolarmente significativo della programmazione strutturata è costi-
tuito dal divieto di utilizzare i cosiddetti “salti incondizionati”, ossia istruzioni del tipo Salti condizionati ed
“vai al passo ...” non soggette al controllo diretto di una condizione booleana. Nei lin- incondizionati
guaggi di programmazione strutturata non si utilizza l’istruzione di salto “go-to”, ma si
inserisce il salto sempre all’interno di una struttura iterativa (al termine o all’inizio del
ciclo, in base alla valutazione di una condizione si stabilisce se ripetere il blocco di istru-
zioni oppure se “saltare” ad un’altra istruzione uscendo dalla struttura di controllo itera-
tiva) o condizionale (in base alla valutazione di una condizione si stabilisce se eseguire
determinate istruzioni o “saltare” ad altre istruzioni).
Ricordiamo infine che, nel valutare l’efficienza di un algoritmo, si calcola il numero Valutazione
di operazioni elementari eseguite dall’algoritmo stesso a fronte di un insieme di infor- dell’efficienza di un
algoritmo:
mazioni da elaborare di cardinalità n. La valutazione dell’efficienza di un algoritmo è complessità
quindi una funzione f , tipicamente espressa in funzione della variabile n che rappre- computazionale
senta il numero di dati con cui è definita l’istanza del problema da risolvere. Tale fun-
zione viene chiamata funzione di complessità computazionale dell’algoritmo. Se f (n) è
la complessità dell’algoritmo A , allora per risolvere un’istanza del problema caratteriz-
zata da n = 15 informazioni, l’algoritmo esegue un numero di operazioni pari a f (15). È
facile rendersi conto che la funzione con cui si esprime la complessità di un algoritmo è
una funzione positiva (un algoritmo non può eseguire un numero negativo di operazio-
ni!) ed in generale è non decrescente: con l’aumentare delle informazioni da elaborare
le operazioni effettuate dall’algoritmo non possono diminuire.
Generalmente non tutte le istanze di un determinato problema, a parità di infor- Valutazione delle
mazioni da elaborare, sono equivalenti dal punto di vista della complessità computa- “istanze meno
favorevoli” per una
zionale dell’algoritmo risolutivo; esistono istanze “più facili” ed altre “più difficili”. Nel determinata strategia
valutare la complessità degli algoritmi, quindi, si considera in genere la complessità nel risolutiva
caso peggiore, ossia la complessità ottenuta considerando l’istanza del problema me-
no favorevole al nostro procedimento risolutivo, dando per scontato che negli altri casi
l’algoritmo non potrà che comportarsi meglio, in modo più efficiente.
Per poter confrontare la complessità di algoritmi differenti, senza ”corrompere” il Classi di complessità
confronto con dettagli tecnici poco significativi o ottimizzazioni di minor conto, si rag- computazionale; la
notazione “O grande”
gruppano le funzioni di complessità in classi di complessità espresse mediante la nota-
zione “O grande”:
O( f (n)) = {g (n) : ∃ n 0 > 0, c > 0 tali che 0 < g (n) < c f (n) ∀n > n 0 }
L’insieme O( f (n)) è costituito da numerose (infinite) funzioni g (n) tali da rispettare defi-
nitivamente, ossia da un certo valore n 0 in poi, la condizione 0 < g (n) < c f (n). In pratica
l’insieme O( f (n)) contiene tutte le funzioni g (n) il cui andamento asintotico (quindi al
crescere di n) è dello stesso ordine di grandezza o inferiore a quello di f (n).
Ad esempio se f (n) = n 2 allora O( f (n)) conterrà le funzioni g (n) = n 2 , g (n) = n,
g (n) = 10n 2 + 5, g (n) = n log n, g (n) = k, ecc. Viceversa, l’insieme O(n 2 ) non contiene,
ad esempio, la funzione n 3 . È interessante osservare che O(n) ⊂ O(n 2 ) ⊂ O(n 3 ) ⊂ . . . ⊂
O(2n ) ⊂ O(n!) ⊂ . . ..
Nel valutare la complessità di un algoritmo si cercherà di individuare la classe di
complessità più bassa che contenga la funzione con cui si può esprimere il numero di
operazioni elementari eseguite dall’algoritmo nel caso peggiore. È chiaro infatti che se
un algoritmo ha una complessità appartenente alla classe O(n), in generale sarà più
efficiente di un algoritmo che, pur risolvendo correttamente il medesimo problema,
appartenga alla classe di complessità O(n 3 ).
3
Esercizio 1
Dati n, k > 0, calcolare i primi n multipli di k.
Soluzione
La soluzione del problema è piuttosto elementare: letti in input i dati del problema,
si iterano le istruzioni con cui si calcola l’i -esimo multiplo di k (per i = 1, 2, . . . , n) come
prodotto i ×k e si stampa il risultato. È una classica applicazione della struttura iterativa,
il cui diagramma di flusso è rappresentato in Figura 1.
1: leggi n e k
2: i = 1
3: scrivi i × k
4: i = i + 1
5: se i ≤ n vai al passo 3
6: stop
La complessità computazionale dell’algoritmo è O(n), perché viene reiterato n volte il
ciclo rappresentato dalle righe 3–5.
Start
Leggi n
ek
i=1
Scrivi
i*k
i = i+1
Sì
i≤n
No
Stop
4
5 printf (" Inserisci due numeri interi n e k: ");
6 scanf("%d %d", &n, &k);
7 i = 1;
8 do {
9 printf ("%d\n", i*k);
10 i = i+1;
11 } while (i <= n);
12 return (0);
13 }
5
Esercizio 2
Dati n, k > 0 stabilire se n è divisibile per k.
Soluzione
La soluzione si basa sull’applicazione di una definizione assai elementare del concetto
di divisibilità tra due numeri interi: possiamo dire che n è divisibile per k se k “entra” in
n esattamente d volte; in altri termini n è divisibile per k se esiste un intero d tale che
n = kd . La verifica avviene quindi sottraendo ripetutamente k da n fino ad ottenere il
valore 0 nel caso in cui n sia divisibile per k o un numero maggiore di zero e minore di
k, nel caso in cui n non sia divisibile per k.
1: leggi n e k
2: m = n
3: se m > 0 allora prosegui, altrimenti vai al passo 6
4: m = m − k
5: vai al passo 3
6: se m = 0 allora scrivi “Sì, n è divisibile per k”
7: altrimenti scrivi “No, n non è divisibile per k”
8: stop
Anche in questo caso, come evidenziato nel diagramma di flusso in Figura 2, vie-
ne utilizzata una struttura iterativa (righe 2–4); al termine della struttura iterativa viene
usata una struttura condizionale (righe 5–6) per verificare il risultato dell’elaborazione
effettuata mediante il ciclo.
La complessità computazionale dell’algoritmo è O(n/k), perché il ciclo delle righe
2–4 viene reiterato proprio n/k volte.
Start
Leggi n
ek
m=n
No
m>0
Sì
Sì No
m = m-k m=0
Scrivi Scrivi
Sì No
Stop
6
3: fintanto che m ≥ k ripeti
4: m = m −k
5: fine-ciclo
6: se m = 0 allora
7: scrivi “Sì, n è divisibile per k”
8: altrimenti
9: scrivi “No, n non è divisibile per k”
10: fine-condizione
11: stop
In questo caso il ciclo è stato implementato utilizzando l’istruzione while, visto che
la condizione deve essere valutata ogni volta prima di ripetere il ciclo e non al termi-
ne del ciclo stesso; inoltre, poiché sia il blocco di istruzioni del ciclo while che i bloc-
chi di istruzioni della condizione if e dell’alternativa else sono costituite da una sola
istruzione, vengono omesse le parentesi graffe che delimitano i blocchi.
7
Esercizio 3
Dato n > 1 stabilire se è un numero primo.
Soluzione
La soluzione di questo problema è basata sulla soluzione del problema precedente: n
è primo se non è divisibile per nessuno degli interi k compresi tra 1 ed n (esclusi gli
estremi: 1 < k < n).
La soluzione si ottiene quindi iterando per k = 2, 3, . . . , n − 1 l’algoritmo con cui si ri-
solve il problema “stabilire se n è divisibile per k”. A sua volta questo algoritmo, come
abbiamo visto nelle pagine precedenti, è basato su una struttura iterativa (righe 5–7) se-
guita da una struttura condizionale (riga 8); entrambe queste strutture saranno inserite
all’interno di una struttura iterativa (righe 4–9) con cui si fa variare il valore di k da 2 fino
a n −1 o fino a quando non sia stato individuato un valore di k minore di n tale che n sia
divisibile per k. Al termine di questa struttura iterativa con una struttura condizionale
(righe 10–11) si verifica l’esito dell’elaborazione e si stampa il risultato.
1: leggi n
2: r = 0
3: k = 1
4: m = n, k = k + 1
5: se m > 0 allora prosegui, altrimenti vai al passo 8
6: m = m − k
7: vai al passo 5
8: se m = 0 allora r = 1
9: se r = 0 e k < n − 1 allora vai al passo 4
10: se r = 1 allora scrivi “n non è un numero primo (è divisibile per k)”
11: altrimenti scrivi “n è un numero primo”
12: stop
La complessità computazionale dell’algoritmo nel caso peggiore è data dal nume-
ro di operazioni effettuate nel caso in cui n sia un numero primo; in tal caso infatti
il ciclo più esterno, definito alle righe 4–9, viene eseguito il massimo numero di volte
(k = 2, . . . , n − 1); per ciascun ciclo (e per ciascun valore di k = 2, . . . , n − 1) il ciclo definito
alle righe 5–7 viene eseguito n/k volte (vedi l’esercizio 2), per stabilire se n è divisibile
per k. Complessivamente vengono quindi eseguite n/2 + n/3 + . . . n/(n − 1) operazioni.
Lo stesso algoritmo può essere ridefinito evidenziando meglio le strutture di con-
trollo iterative e condizionali con la seguente pseudo-codifica in cui non sono riportate
esplicitamente le istruzioni di “salto” per evidenziare che si tratta sempre di istruzioni di
“salto condizionato”, così come richiesto dalle regole della programmazione strutturata.
1: leggi n
2: r = 0
3: k = 1
4: ripeti
5: m = n, k = k + 1
6: fintanto che m > 0 ripeti
7: m = m −k
8: fine-ciclo
9: se m = 0 allora
10: r =1
11: fine-condizione
12: fintanto che r = 0 e k < n − 1
13: se r = 1 allora
14: scrivi “n non è un numero primo (è divisibile per k)”
8
15: altrimenti
16: scrivi “n è un numero primo”
17: fine-condizione
18: stop
Di seguito riportiamo il diagramma di flusso con cui viene codificato l’algoritmo;
naturalmente il diagramma di flusso è identico sia nel caso della prima versione dell’al-
goritmo che della seconda, dal momento che le due pseudo-codifiche sono due modi
equivalenti di descrivere il medesimo procedimento risolutivo. Nei blocchi punteggiati
sono evidenziate le strutture di controllo nidificate una dentro l’altra o concatenate una
di seguito all’altra nel rispetto delle regole della programmazione strutturata.
Struttura
sequenziale
Start
Leggi n
r=0, k=1
Struttura
iterativa
m=n, k=k+1
Struttura
iterativa
m>k
No
Sì Struttura
condizionale
Sì No
m = m-k m=0
r=1
Sì
r=0 e k<n
No
Struttura condizionale
Sì No
r=1
Scrivi Scrivi
"No" "Sì"
Stop
9
7 r = 0;
8 k = 1;
9 do {
10 m = n;
11 k = k+1;
12 while (m > 0) {
13 m = m-k;
14 }
15 if (m == 0)
16 r = 1;
17 } while (r == 0 && k < n -1);
18 if (r == 1)
19 printf ("%d non e’ primo (e’ divisibile per %d).\n", n, k);
20 else
21 printf ("%d e’ un numero primo .\n", n);
22 return (0);
23 }
15 int main(void) {
16 int n, k, r;
17 printf (" Inserisci un intero maggiore di zero: ");
18 scanf("%d", &n);
19 r = 0;
20 k = 1;
21 do {
22 k = k+1;
23 if ( divisibile (n, k))
24 r = 1;
25 } while (r == 0 && k < n -1);
26 if (r == 1)
27 printf ("%d non e’ primo (e’ divisibile per %d).\n", n, k);
28 else
29 printf ("%d e’ un numero primo .\n", n);
30 return (0);
31 }
10
Esercizio 4
Dati due interi x e y tali che x > y > 0, calcolare la parte intera e il resto della divisione
x/y.
Soluzione
La strategia risolutiva per questo problema sfrutta ancora una volta la procedura per la
verifica della divisibilità vista nell’esercizio 2 e riutilizzata anche nell’esercizio 3. In-
fatti il rapporto x/y è uguale a d con resto r (0 ≤ r < y) se x = y · d + r . In termi-
ni più rudimentali possiamo dire che d , la parte intera del risultato della divisione, è
dato da quante volte y “entra” in x; mentre il resto r è dato dal valore che “avanza”
dopo aver tolto per d volte y da x. Possiamo proporre quindi la seguente pseudo-
codifica:
1: leggi x e y
2: r = n, d = 0
3: se r ≥ y allora prosegui, altrimenti vai al passo 5
4: r = r − y, d = d + 1
5: vai al passo 3
6: scrivi “x/y = d con resto r ”
7: stop
La pseudo-codifica può essere riscritta mettendo in evidenza i salti condizionati e la
struttura iterativa dell’algoritmo:
1: leggi x e y
2: r = n, d = 0
3: fintanto che r ≥ y ripeti
4: r = r − y, d = d + 1
5: fine-ciclo
6: scrivi “x/y = d con resto r ”
7: stop
È abbastanza evidente che il numero di operazioni elementari svolte dall’algoritmo
è pari al numero di volte che viene iterato il ciclo alle righe 3–5; dunque la complessità
dell’algoritmo è O(x/y).
Riportiamo di seguito il diagramma di flusso e la codifica in linguaggio C dell’algo-
ritmo.
Start
Leggi x
ey
r = n, d = 0
No
r≥y
Sì
Scrivi
r=r-y rapporto d,
resto r
d=d+1 Stop
11
1 # include <stdlib .h>
2 # include <stdio.h>
3 int main(void) {
4 int x, y, r, d;
5 printf (" inserisci due interi x e y (x>y): ");
6 scanf("%d %d", &x, &y);
7 d = 0;
8 r = x;
9 while (r >= y) {
10 r = r-y;
11 d = d+1;
12 }
13 printf (" rapporto : %d\ nresto : %d\n", d, r);
14 return (0);
15 }
Per evitare malintesi è bene precisare che questo esercizio tende a mettere in evi-
denza la possibilità di ottenere il risultato richiesto utilizzando soltanto degli operatori
aritmetici di base. Tuttavia in linguaggio C la divisione fra due variabili intere restituisce
sempre un resto intero (a meno di non utilizzare il cast) e il resto della divisione intera fra
due numeri può essere ottenuto mediante l’operatore di modulo indicato con il simbolo
“%”. Possiamo quindi proporre la seguente codifica in C più compatta della precedente:
1 # include <stdlib .h>
2 # include <stdio.h>
3 int main(void) {
4 int x, y;
5 printf (" inserisci due interi x e y (x>y): ");
6 scanf("%d %d", &x, &y);
7 printf (" rapporto : %d\ nresto : %d\n", x/y, x%y);
8 return (0);
9 }
12
Esercizio 5
Dati due numeri interi positivi x e y calcolare il massimo comune divisore tra x e y.
Soluzione
Per costruire un algoritmo elementare per calcolare il MCD (massimo comun divisore)
tra x e y ancora una volta facciamo ricorso ad una divisione piuttosto elementare in
modo da costruire la strategia risolutiva basandoci soltanto su operazioni aritmetiche
di base. In particolare l’idea è quella di applicare una variante del cosiddetto “algoritmo
di Euclide”: si tratta di sottrarre ripetutamente x da y (se y > x) o y da x (se x > y) fino
ad ottenere lo stesso valore per x e y; tale valore è proprio il massimo comun divisore
dei due numeri originali.
La strategia risolutiva appena descritta può essere precisata con il seguente algorit-
mo di cui riportiamo la pseudo-codifica:
1: leggi x e y
2: se x , y prosegui, altrimenti vai al passo 6
3: se x > y allora x = x − y
4: altrimenti y = y − x
5: vai al passo 2
6: scrivi “il MCD è x”
7: stop
Come al solito possiamo riscrivere la pseudo-codifica per evidenziare meglio le strut-
ture algoritmiche iterativa e condizionale.
1: leggi x e y
2: fintanto che x , y ripeti
3: se x > y allora
4: x =x−y
5: altrimenti
6: y = y −x
7: fine-condizione
8: fine-ciclo
9: scrivi “il MCD è x”
10: stop
L’algoritmo può essere codificato in un programma C come segue.
1 # include <stdlib .h>
2 # include <stdio.h>
3
13 int main(void) {
14 int a, b;
15 printf (" Inserisci due numeri interi : ");
16 scanf("%d %d", &a, &b);
17 printf ("Il massimo comune divisore e’ %d\n", MCD(a,b));
18 return (0);
19 }
13
Riportiamo infine la rappresentazione dell’algoritmo con un diagramma di flusso
in cui risulta evidente che la struttura iterativa del ciclo rappresentato con le righe 2–
5 della prima pseudo-codifica o con le righe 2–8 della seconda, contiene una struttura
condizionale (righe 3–4 e righe 3–7 rispettivamente nella prima e nella seconda versione
della pseudo-codifica).
Start
Leggi x
ey
No
x≠y
Sì
Sì No
x>y
Stop
14
Esercizio 6
Dati due numeri interi positivi x e y calcolare il minimo comune multiplo tra x e y.
Soluzione
La strategia risolutiva è la seguente: siano m x = x ed m y = y i primi multipli di x e y
rispettivamente; se m x < m y si somma x ad m x ottenendo il successivo multiplo di x,
altrimenti si somma y ad m y ottenendo il successivo multiplo di y; il procedimento vie-
ne iterato fino a quando non si ottengono due valori uguali per m x e m y . Naturalmente
il procedimento ha termine dopo un numero finito di passi; infatti bisogna trovare due
interi h, k > 0 tali che hx = k y. Il procedimento ha quindi una complessità di O(h +k): si
deve sommare h volte x e k volte y prima di individuare il più piccolo multiplo comune
ad entrambi.
La seguente pseudo-codifica formalizza in un algoritmo la strategia risolutiva sopra
esposta.
1: leggi x e y
2: m x = x, m y = y
3: se m x , m y prosegui, altrimenti vai al passo 7
4: se m x < m y allora m x = m x + x
5: altrimenti m y = m y + y
6: vai al passo 3
7: scrivi “il mcm tra x e y è m x ”
8: stop
Ancora una volta riscriviamo la pseudo codifica in modo da mettere in evidenza le
strutture algoritmiche utilizzate.
1: leggi x e y
2: m x = x, m y = y
3: fintanto che m x , m y ripeti
4: se m x < m y allora
5: mx = mx + x
6: altrimenti
7: my = my + y
8: fine-condizione
9: fine-ciclo
10: scrivi “il mcm tra x e y è m x ”
11: stop
L’algoritmo può essere codificato in un programma C come segue. Come nell’eser-
cizio precedente l’algoritmo per il calcolo del minimo comune multiplo è codificato in
una funzione richiamata dalla funzione principale main. In questo modo, se fosse ne-
cessario, sarebbe possibile riutilizzare la stessa funzione mcm anche nell’ambito di altri
programmi.
1 # include <stdlib .h>
2 # include <stdio.h>
3
15
13
14 int main(void) {
15 int a, b;
16 printf (" Inserisci due numeri interi : ");
17 scanf("%d %d", &a, &b);
18 printf ("Il minimo comune multiplo e’ %d\n", mcm(a,b));
19 return (0);
20 }
Start
Leggi x
ey
mx = x, my = y
No
mx ≠ my
Sì
Sì No
mx < my
Scrivi
mx = mx + x my = my + y
mx
Stop
16
Esercizio 7
Letti n numeri razionali stabilire se sono stati acquisiti in ordine crescente.
Soluzione
Il problema in sé è piuttosto semplice, ma ci permette di evidenziare l’uso di variabi-
li “flag” che servono a segnalare il fatto che durante l’esecuzione di un procedimento
iterativo si è verificata una determinata condizione che non potremo controllare nuo-
vamente al termine del ciclo a meno di non utilizzare questa variabile che ci permetterà
di stabilire se la condizione desiderata si era verificata o meno.
Il problema richiede infatti di iterare un ciclo con il quale vengono acquisiti in in-
put gli n numeri razionali; man mano che questi numeri vengono letti dovremo con-
frontare l’elemento appena letto con il precedente per stabilire se l’ordine crescente è
rispettato o meno. Il procedimento di lettura deve comunque essere portato a termi-
ne per tutti gli n elementi dell’insieme, ma si deve tenere traccia del fatto che tutti gli
elementi letti in input hanno rispettato l’ordine crescente oppure, al contrario, che al-
meno una coppia di elementi non rispettava tale ordine. A tal fine possiamo utilizzare
una variabile che chiameremo flag a cui assegneremo inizialmente il valore 0; durante
il processo di lettura degli n elementi se ne incontreremo uno minore del suo predeces-
sore, allora modificheremo il valore di flag impostandolo uguale a 1, in caso contrario
non modificheremo il suo valore. In questo modo, al termine del procedimento iterati-
vo di lettura, se la variabile flag sarà rimasta uguale a zero, allora vorrà dire che tutti gli
elementi rispettavano l’ordine crescente; in caso contrario, se f l ag = 1, allora almeno
una coppia di elementi non rispettavano l’ordine crescente. Di seguito proponiamo la
pseudo-codifica dell’algoritmo.
1: leggi n
2: f l ag = 0
3: leggi a
4: i = 1
5: se i < n allora prosegui, altrimenti vai al passo 11
6: leggi b
7: se b ≤ a allora f l ag = 1
8: a = b
9: i = i + 1
10: vai al passo 5
11: se f l ag = 0 allora scrivi “Gli elementi sono in ordine crescente”
12: altrimenti scrivi “Gli elementi non sono in ordine crescente”
13: stop
Riscriviamo la pseudo-codifica mettendo in evidenza il ciclo iterativo principale che
a sua volta contiene una struttura condizionale; la struttura iterativa è seguita da una
struttura condizionale. La concatenazione e la nidificazione delle strutture algoritmiche
è messa in maggiore evidenza dalla rappresentazione grafica dell’algoritmo riportata nel
diagramma di flusso di Figura 7.
1: leggi n
2: f l ag = 0
3: leggi a
4: i = 1
5: fintanto che i < n ripeti
6: leggi b
7: se b ≤ a allora
8: f l ag = 1
9: fine-condizione
17
10: a =b
11: i = i +1
12: fine-ciclo
13: se f l ag = 0 allora
14: scrivi “Gli elementi sono in ordine crescente”
15: altrimenti
16: scrivi “Gli elementi non sono in ordine crescente”
17: fine-condizione
18: stop
Riportiamo la codifica in linguaggio C dell’algoritmo. Gli unici due aspetti degni
di rilievo sono l’utilizzo dell’istruzione for per implementare la struttura di controllo
iterativa e l’uso della sequenza “%f” nella stringa di formato della funzione scanf alle
righe 10 e 12 del programma, dal momento che le variabili a e b erano di tipo float e
non int come per le altre variabili utilizzate fino ad ora.
1 # include <stdlib .h>
2 # include <stdio.h>
3 int main(void) {
4 int n, flag , i;
5 float a, b;
6 printf (" Inserisci il numero di elementi : ");
7 scanf("%d", &n);
8 printf (" inserisci %d elementi : ", n);
9 flag = 0;
10 scanf("%f", &a);
11 for (i=1; i<n; i++) {
12 scanf("%f", &b);
13 if (b < a)
14 flag = 1;
15 a = b;
16 }
17 if (flag == 0)
18 printf ("Gli elementi sono in ordine crescente .\n");
19 else
20 printf ("Gli elementi non sono tutti in ordine crescente .\n");
21 return (0);
22 }
18
Start
Leggi n
flag = 0
Leggi a
i=1
No
i<n
Sì
Sì No
Leggi b flag = 0
Sì Scrivi Scrivi
No
b≤a Sì No
flag = 1
Stop
a=b
i = i+1
19
Riferimenti bibliografici
[1] A.V. Aho, J.E. Hopcroft, J.D. Ullman, Data structures and algorithms, Addison-
Wesley, 1987.
20