Sei sulla pagina 1di 173

Algoritmi

http://de253.poliba.it/mailman/
listinfo/fondamenti-di-
informatica-cdl-elettronica-
telecomunicazioni

2
Algoritmi
• Definizione
• Caratteristiche
• Pianificazione e progettazione
• Rappresentazione
• Operazioni fondamentali
• Teorema di Jacopini-Böhm
• Diagrammi di flusso

3
Definizione
• Intuitiva
Sequenza ordinata di istruzioni per risolvere un problema.
(Es. ricetta)

• Formale
Insieme ordinato di operazioni non ambigue,
effettivamente eseguibili che risolvono, in un tempo
finito, tutti i problemi di una determinata classe.

4
Caratteristiche degli algoritmi
• Carattere deterministico (.. Insieme ordinato di
istruzioni non ambigue ..)

una istruzione e’ non ambigua se può essere


direttamente eseguita dall’agente di calcolo
(istruzione primitiva) e la sua interpretazione non
dipende dal contesto o dalla preparazione
dell’agente. I risultati dell’algoritmo non
dipendono dalla macchina o persona che lo
esegue.

5
insieme ordinato … non ambigue
Shampoo Torta
• Bagna i capelli •Prepara base
• Insapona
• Risciacqua - setaccia farina
• Ripeti (cosa?, - aggiungi uova, acqua…
quante volte?) •prendi setaccio

Esegui A oppure B •versa la farina


(come stabilire se A o
B?)

6
Caratteristiche degli algoritmi
• Carattere di realizzabilità pratica (..effettivamente
eseguibili ..)
tutte le istruzioni che costituiscono l’algoritmo devono
essere effettivamente eseguibili
(Es. calcolare l’esatto valore decimale di π. Calcola tutti i
numeri primi e stampa il primo maggiore di 100)

7
Caratteristiche degli algoritmi
• Carattere finito (.. risolvono in un tempo finito …)

il numero delle istruzioni che definiscono un algoritmo e’


finito e queste vengono eseguite un numero finito di volte.
(Es. calcolare tutti i numeri primi)

8
Carattere finito
Shampoo
1) Bagna i capelli
2) Contatore = 0
3) Ripeti passi da 4 a 6 finché contatore =2
4) Insapona i capelli
5) Risciacqua
6) Contatore = Contatore + 1
7) stop

9
Algoritmo per sommare due
numeri
c=a+b con a e b numeri formati da m cifre
cm cm-1cm-2.. c0 = am-1 am-2 ..a0 + bm-1 bm-2 ..b0

1. riporto = 0
2. i = 0
3. ripeti passi 4 – 6 finché i diventa > m -1
4. somma ai e bi al riporto per ottenere ci

10
Algoritmo per sommare due
numeri
5. se ci ≥ 10 ci = ci -10 e riporto = 1, altrimenti riporto = 0
6. i = i + 1
7. cm = riporto
8. stampa risultato
9. stop

11
Pianificazione e progettazione
• Analizzare il problema
•comprendere a fondo il problema e darne una
definizione precisa
•quali sono gli input?
•quali sono i dati? I formati?
•quali devono essere gli output?
•come elaborare i dati?

12
Pianificazione e progettazione
• Progettare una soluzione
Definire i passi da applicare per risolvere il
problema
Passi dettagliati, non ambigui e finiti
Verifica della soluzione
• Codificare il programma e documentarlo
• Testare il programma
Singole parti del codice (moduli)

13
Pianificazione e progettazione
• Validare il programma
Serie estesa di prove
Input non validi
Valutazioni sull’interfaccia utente (sono
sufficienti le indicazioni fornite?)

14
Esempio
Determinare in un gruppo di persone la percentuale di
presenze divisa nei seguenti gruppi di età:
0-5
6-12
13-19
20-64
> 65

15
Analizzare il problema
Input età di ogni persona presente

Output percentuale di presenze in ogni gruppo di età

Elaborazione aggiungere 1 al gruppo di età per ogni


persona che rientra in quel gruppo

16
Progettare una soluzione
1) Richiedere l’età della persona
2) Aggiungerla al gruppo corrispondente
3) Ripeti per tutte le persone i passi 1-2
4) Calcola la percentuale per ogni gruppo
4.1) Determina il numero totale di persone
4.2) Dividi i totali parziali per il totale globale
5) Output risultato

17
Esercizi
• Modificare l’algoritmo della somma in modo che non stampi
gli zero non significativi
• Eseguire la moltiplicazione mediante l’addizione ripetuta
• Eseguire la divisione mediante la sottrazione ripetuta

18
Caratteristiche di un
calcolatore elettronico
• È velocissimo
Non è intelligente
Non è in grado di compiere deduzioni o
ragionamenti in modo autonomo.
• Non è in grado di capire un problema.
• Non è in grado di capire se il risultato
raggiunto è la soluzione del problema.
Capacità del calcolatore
• Sa leggere le informazioni dall’esterno (input).
Sa memorizzare le informazioni.
Sa eseguire alcune operazioni elementari: addizione,
sottrazione, prodotto e rapporto fra numeri,
concatenazione di parole.
Sa eseguire il confronto fra informazioni dello stesso
tipo: (es.: a>b, a=b, a≤b e l’uguaglianza fra due parole).

Sa memorizzare sequenze di istruzioni elementari 



(programma) e le sa eseguire secondo un ordine
stabilito dal programma stesso.
Sa scrivere le informazioni all’esterno (output).
Compiti del programmatore

• Analizzare il problema riducendolo in


termini astratti e formulare un modello del
problema.
Individuare una strategia risolutiva
Codificare l’algoritmo - renderlo
comprensibile al calcolatore.
Analizzare il risultato dell’elaborazione
evidenziando eventuali errori nella
formulazione del problema, nella strategia
risolutiva, nella codifica dell’algoritmo.
Analisi e programmazione

• Algoritmo ⎯ elenco finito di istruzioni, che


specificano le operazioni eseguendo le quali
si risolve una classe di problemi
• Un particolare problema della classe viene
risolto utilizzando l’apposito algoritmo sui
dati che lo caratterizzano
• Un algoritmo non può essere eseguito
direttamente dall’elaboratore
Analisi e programmazione
• Programma ⎯ ricetta che traduce
l’algoritmo ed è direttamente comprensibile,
pertanto eseguibile, da parte di un
elaboratore
• Linguaggio di programmazione ⎯
linguaggio rigoroso che permette la
formalizzazione di un algoritmo in un
programma

es. : Effettuare un accredito su un c/c


bancario
Analisi e programmazione
es.: Effettuare un accredito
su un c/c bancario
Soluzione: Utilizzare un
programma che serva per
predisporre il calcolatore
all’accredito di una qualunque
cifra su un qualunque c/c;
cifra da accreditare e numero
di c/c sono i dati caratteristici
del problema
Le fasi del procedimento di
analisi e programmazione
Algoritmo
• Algoritmo deriva dal nome del matematico
arabo Al Khuwarizmi, vissuto nel IX secolo d.C.
• Un algoritmo è una successione di istruzioni o
passi che definiscono le operazioni da eseguire
sui dati per ottenere i risultati; un algoritmo
fornisce la soluzione ad una classe di
problemi
• Lo schema di esecuzione di un algoritmo
specifica che i passi devono essere eseguiti in
sequenza, salvo diversa indicazione
26
Algoritmo
• Ogni algoritmo è concepito per interagire con
l’ambiente esterno per acquisire dati e
comunicare messaggi o risultati;
• i dati su cui opera un’istruzione sono forniti
dall’esterno o sono frutto di istruzioni eseguite
in precedenza

27
Algoritmo
Esempio: Ordinamento di un mazzo di carte
Sia dato un mazzo da 40 carte da ordinare in modo che
i cuori precedano i quadri, che a loro volta precedono
fiori e picche; le carte di uno stesso seme sono ordinate
dall’asso al re
Algoritmo:
• Si suddivida il mazzo in 4 mazzetti, ciascuno costituito
da tutte le carte dello stesso seme
• Si ordinino le carte di ciascun mazzetto dall’asso al re
• Si prendano nell’ordine i mazzetti delle cuori, quadri,
fiori e picche

28
Algoritmo
Si vuole ricercare, all’interno di un mazzo di chiavi,
quella che apre un dato lucchetto
Algoritmo:

1) Si seleziona una chiave dal mazzo e la si marca con un
pennarello
2) Si tenta di aprire il lucchetto con la chiave appena
marcata; se funziona, si va al passo 4)
3) Altrimenti, si controlla la chiave successiva
3.1) Se non è marcata, la si marca e si torna al passo 2)
3.2) Viceversa, si prende atto che nel mazzo non è
presente la chiave che apre il lucchetto
4) Fine della ricerca
29
Algoritmo
Somma di un insieme di numeri
Dati da elaborare: un insieme di numeri (che sarà noto
solo nel momento in cui l’algoritmo verrà eseguito).

Risultato desiderato: la loro somma.

Un possibile algoritmo: scorrere la sequenza dei numeri,


dal primo all’ultimo, e addizionare ogni numero alla
somma di quelli precedenti
(che inizialmente sarà pari a zero).

30
Algoritmo
Esempio: calcolare la somma dei numeri 5, −7, 4, 9, −2. Somma iniziale: 0

primo passo: 5, −7, 4, 9, −2 nuova somma parziale: 0 + 5 = 5 


secondo passo: 5, −7, 4, 9, −2 nuova somma parziale: 5 + (−7) = −2 


terzo passo: 5, −7, 4, 9, −2 nuova somma parziale: −2 + 4 = 2 


quarto passo: 5, −7, 4, 9, −2 nuova somma parziale: 2 + 9 = 11 


quinto passo: 5, −7, 4, 9, −2 nuova somma parziale: 11 + (−2) = 9



Risultato: 9. 


31
Problemi e istanze
Un’istanza di un problema corrisponde a un
particolare valore dei suoi dati d’ingresso.

Nell’esempio precedente, i numeri 5, −7, 4, 9, −2


costituiscono una delle possibili istanze del problema
del calcolo della somma di un insieme di numeri.
Un algoritmo viene formulato per risolvere un
insieme di istanze di un dato problema, non per
una sola istanza.
In teoria un problema (come quello precedente) può
avere infinite istanze.
32
Algoritmo
Problema: Calcolo delle 5) x1=(−b+√∆)/2a,
radici reali di ax2+bx+c=0 x2=(−b−√∆)/2a 

Algoritmo:
1) Input coefficienti a,b,c 
 6) Output dei valori x1, x2 


2) Calcolare ∆ = b2−4ac 
 7) Fine 


3) Se ∆<0 non esistono radici


reali, eseguire l’istruzione 7) 


4) Se ∆=0, x1=x2=−b/2a, poi


eseguire l’istruzione 6) 


33
Algoritmo
es. Calcolare il M.C.D. di due interi a,b, con a>b

Algoritmo: Formalizzato da Euclide nel 300 a.C., si basa sul


fatto che:
ogni divisore comune ad a e b è anche divisore del resto r
della divisione intera di a per b, quando a>b e r≠0;
se r=0, b è il M.C.D.
MCD(a,b) = MCD(b,r), se r≠0
MCD(a,b)=b, se r=0
M.C.D.(a,b) = M.C.D.(b,r) dove r = a mod b
M.C.D. (20, 12)= (=> 20 mod 12 = 8)
M.C.D. (12, 8) = (=> 12 mod 8 = 4)
M.C.D. (8, 4) = (=> 8 mod 4 = 0) 34
Algoritmo
es. Calcolare il M.C.D. di due interi a,b, con a>b

1) Acquisire i valori di a e b
2) Se b>a, scambiare i valori di a e b
3) Calcolare il resto r della divisione intera di a per b
4) Se r=0, MCD(a,b)=b; comunicare il risultato
all’esterno; eseguire l’istruzione 6)
5) Se r≠0, sostituire il valore di a con il valore di b ed
il valore di b con il valore di r; tornare al passo 3)
6) Fine

35
Algoritmo: costanti e variabili
I dati su cui opera un algoritmo sono costanti e
variabili
• Un dato è costante quando il suo valore non può
essere aggiornato durante l’esecuzione
dell’algoritmo o per esecuzioni successive
• Una variabile è una coppia <nome, valore>: può
essere immaginata come una scatola sulla
quale è scritto un nome e che può contenere un
valore

36
Algoritmo: costanti e variabili
Il valore di una variabile deve appartenere
all’insieme di definizione, su cui si opera
mediante regole opportune specifiche dell’insieme
Data una variabile <x,v>, x è il nome della variabile
e v è il suo valore attuale; le variabili sono
indeterminate in fase di definizione dell’algoritmo,
ma corrispondono a valori specifici durante ogni
esecuzione
Esempio: Nell’algoritmo di risoluzione delle
equazioni di 2° grado, a, b, c non corrispondono a
nessun valore finché non si esegue l’algoritmo

37
Assegnazione
L’istruzione di assegnazione definisce il valore attuale di
una variabile, che resta inalterato fino all’assegnazione
successiva
L’assegnazione si rappresenta con i simboli “←” o “=“:
nome di variabile ← espressione
nome di variabile = espressione
che si legge assegna alla variabile “nome di variabile” il
valore di “espressione”; l’espressione a destra di ← è
costituita da variabili, costanti e operatori
L’assegnazione viene così eseguita:
a) si valuta l’espressione a destra di ←, sostituendo ai nomi
di variabile i loro valori attuali; il risultato deve
appartenere all’insieme di definizione della variabile a
sinistra di ← | = 38
Assegnazione
I nomi delle variabili possono essere scelti in modo
arbitrario, ma è opportuno selezionare nomi
significativi del contenuto della variabile

È necessario rispettare la regola dell’ordinamento:



Quando una variabile appare a destra di ← in una
assegnazione deve essere già istanziata

cioè le deve essere già stato assegnato un valore

39
Istruzioni
Istruzioni operative, che producono risultati
Istruzioni di controllo, che controllano il verificarsi di condizioni
specificate e, in base al risultato del controllo, determinano il flusso di
istruzioni da eseguire
Istruzioni di salto, che alterano il normale flusso di esecuzione
sequenziale delle istruzioni di un algoritmo, specificando quale sia la
successiva istruzione da eseguire
• nelle istruzioni di salto condizionato, l’effettiva esecuzione del salto è
legata al verificarsi di una condizione specificata
• l’istruzione di salto incondizionato produce sempre un salto
Istruzioni di Input/Output, che specificano come debba essere effettuata
una trasmissione di dati o messaggi fra l’algoritmo e l’ambiente esterno
Istruzioni di inizio/fine esecuzione, che indicano l’inizio/la fine
dell’algoritmo
40
Algoritmo
Problema: Calcolo delle radici reali di ax2+bx+c=0
Algoritmo:
1) Input coefficienti a,b,c 


2) Calcolare ∆ = b2−4ac 


3) Se ∆<0 non esistono radici reali, eseguire l’istruzione 7) 


4) Se ∆=0, x1=x2=−b/2a, poi eseguire l’istruzione 6) 


5) x1=(−b+√∆)/2a, x2=(−b−√∆)/2a 


6) Output dei valori x1, x2 



41
7) Fine 

Esempio: Calcolo delle radici di
equazioni di 2° grado

a) “acquisire i coefficienti a, b, c” è un’istruzione di lettura
(INPUT)
b) “calcolare ∆=b2−4ac” è un’istruzione operativa
c) “se ∆=0, x1=x2=−b/2a” è un’istruzione di controllo:
l’istruzione di assegnazione x1=x2=−b/2a viene eseguita solo
se ∆=0
d) “comunicare i valori x1, x2” è un’istruzione di scrittura
(OUTPUT)
e) “eseguire l’istruzione 6)” è un’istruzione di salto
incondizionato
f) “se ∆<0 eseguire l’istruzione 7)” è un’istruzione di salto
condizionato, perché l’istruzione 7) è la prossima istruzione
da eseguire solo se ∆<0 42
Proposizioni e predicati
Una proposizione è un costrutto linguistico del quale si può
asserire o negare la veridicità
Esempi
1) “Roma è la capitale della Gran Bretagna” falsa 


2) “3 è un numero intero” vera 


Una proposizione è un predicato se il suo valore di verità


dipende dall’istanziazione di alcune variabili
Esempi
1) “la variabile età è minore di 30” 


2) “la variabile base è maggiore della variabile altezza” 



43
Proposizioni e predicati
La valutazione di un predicato è l’operazione che permette di
determinare se il predicato è vero o falso, sostituendo alle
variabili i loro valori attuali
I valori vero e falso sono detti valori logici o booleani
Proposizioni e predicati possono essere espressi per mezzo
degli operatori relazionali:

= (uguale) ≠ (diverso)

> (maggiore) < (minore)

≥ (maggiore o uguale) ≤ (minore o uguale)

44
Proposizioni e predicati
Dato un predicato p, il predicato not p, detto opposto o
negazione logica di p, ha i valori di verità opposti rispetto a p;
Dati due predicati p e q, la congiunzione logica p and q è un
predicato vero solo quando p e q sono entrambi veri, e falso
in tutti gli altri casi
Dati due predicati p e q, la disgiunzione logica p or q è un
predicato falso solo quando p e q sono entrambi falsi, e vero
in tutti gli altri casi
I predicati nei quali compare almeno un operatore logico,
not, and, or, sono detti composti
La tavola di verità di un predicato composto specifica il valore
del predicato per ognuna delle possibili combinazioni dei suoi
argomenti

45
Predicati composti

not (base > altezza)



è vero solo quando il valore di base è minore o uguale
del valore di altezza

età > 30 and età < 50



è vero solo quando il valore di età è compreso tra 30 e
50 (esclusi)

base > altezza or base > 100


è vero quando il valore di base è maggiore del valore di
altezza, o quando il valore di base è maggiore di 100, o
quando entrambe le condizioni sono verificate
46
Rappresentazione degli algoritmi
Gli algoritmi devono essere descritti in modo chiaro,
preciso e non ambiguo.

1 Linguaggio naturale. Testi non strutturati difficili da


seguire (saggi), l’interpretazione dipende inoltre dal
contesto e dalla esperienza del lettore (poesie).

47
Rappresentazione degli algoritmi
2. Linguaggio di programmazione formale
quando si progetta un algoritmo si parte da livello elevato
di astrazione, senza doversi concentrare sulla
punteggiatura, sulla grammatica e sintassi.
#include <iostream>
#include <cstdlib>
using namespace std;
int main()
{
const int NEM = 10;
int v[NEM];
int ne;
cout << "Numero elementi vettore? (max " << NEM << "): ";
cin >> ne;
for(int i=0; i<ne; i++) v[i] = rand()%101; // max 100
cout << "Il vettore e':" << endl;
for(int i=0; i<ne; i++) cout << v[i] << " ";
cout << endl;
float min = v[0];
for(int i=0; i<ne; i++) if(v[i] < min) min = v[i];
cout << "Il minimo vale: " << min << endl;
}

48
Rappresentazione degli algoritmi
3. Pseudocodice
linguaggio di programmazione senza regole
grammaticali,
facilmente leggibile.

1) Input coefficienti a,b,c


2) Calcolare ∆ = b2−4ac
3) Se ∆<0 non esistono radici reali, eseguire l’istruzione 7)
4) Se ∆=0, x1=x2=−b/2a, poi eseguire l’istruzione 6)
5) x1=(−b+√∆)/2a, x2=(−b−√∆)/2a
6) Output dei valori x1, x2
7) Fine 


49
Rappresentazione degli algoritmi
4. Diagrammi di flusso (flow chart)
Rappresentazione in forma schematica della
logica e della struttura dell’algoritmo.
Ad ogni istruzione corrisponde un simbolo grafico
la cui forma dipende dal tipo di istruzione
considerata.
Efficace mezzo di comunicazione tra i
programmatori e chi e’ coinvolto nell’uso del
programma

50
Operazioni fondamentali
Operazioni sequenziali descrivono un singolo compito ben
definito

Input inserimento dei dati da elaborare


Output visualizzare i risultati
Elaborazione
x=0
aggiungere due uova

51
Operazioni fondamentali
Operazioni condizionali consentono di alterare il normale
flusso sequenziale.
Si pone una domanda e in base alla risposta si determina la
successiva operazione da eseguire
Se condizione (vera/falsa) è vera allora
esegui queste operazioni
altrimenti
esegui queste operazioni

52
Operazioni fondamentali
Algoritmo per sommare due numeri
5. se ci ≥ 10 ci = ci -10 e riporto = 1, altrimenti riporto = 0

5. se ci ≥ 10
ci = ci -10 e riporto = 1
altrimenti
riporto = 0

53
Operazioni fondamentali
Operazioni iterative (ciclo) ripetizione di un blocco di
istruzioni

Ripeti dal passo i al passo j fino a che condizione (vera/falsa)


diventa vera
Algoritmo per sommare due numeri
3. ripeti passi 4 – 6 finché i diventa > m -1

54
Iterazioni per vero
Ciclo while
Il corpo del ciclo viene eseguito 0,1 o più volte (iterazione a
condizione iniziale)

Mentre condizione (vera/falsa) rimane vera esegui


corpo del ciclo
fine del ciclo
3. sum = 0

mentre (n>0) fai
3.1 leggi x
3.2 sum = sum + x
3.3 n = n-1
4. stampa sum
55
Iterazioni per falso
Ciclo repeat
Il corpo del ciclo viene eseguito almeno una volta (iterazione
a condizione finale)

Ripeti dal passo i al passo j fino a che condizione (vera/falsa)


diventa vera
Ripeti
5.1 se Ai > max allora poni max = Ai
5.2 poni i = i + 1
5.3 fine ciclo

fino a che i > N


56
Teorema di Jacopini-Böhm
Eliminare dai procedimenti risolutivi le istruzioni di salto, del
tipo “vai a ...” oppure “ritorna a ...”, che portano alla
costruzione di algoritmi disordinati, difficili da comprendere e
non sequenziali
ES: Esaminare un elenco di persone con il nome e la professione di ciascuna, e
comunicare il nome delle persone che sono avvocati
senza usare le strutture di controllo:

1. leggi il nome di una persona e la sua professione


2. se la professione è diversa da avvocato vai al punto 4
3. scrivi il nome della persona
4. se ci sono altre persone da esaminare torna al punto 1
5. fine
57
Teorema di Jacopini-Böhm
Eliminare dai procedimenti risolutivi le istruzioni di salto, del
tipo “vai a ...” oppure “ritorna a ...”, che portano alla
costruzione di algoritmi disordinati, difficili da comprendere e
non sequenziali
ES: Esaminare un elenco di persone con il nome e la professione di ciascuna, e
comunicare il nome delle persone che sono avvocati
algoritmo equivalente ben strutturato
esegui
immetti nome, professione
se professione = avvocato
allora
scrivi nome
fine se
ripeti finché elenco terminato
58
Teorema di Jacopini-Böhm
Qualunque algoritmo puo’ essere descritto utilizzando
soltanto le tre strutture fondamentali:
Sequenza
Diramazione
Iterazione

59
Esercizi
• Scrivere un algoritmo che riceva in ingresso un dato
X e calcoli X2 e 1/X fino a che X = 0.
• Scrivere un algoritmo che riceva in ingresso N
numeri interi e determini se tra questi e’ presente il
numero X e la sua posizione.
• Scrivere un algoritmo che riceva in ingresso N
numeri interi e determini il più grande.
• Scrivere un algoritmo per il calcolo del fattoriale di
N.

60
Diagrammi di flusso

Ad ogni istruzione corrisponde un simbolo grafico la cui forma


dipende dal tipo di istruzione considerata.

Tali simboli sono collegati tra loro, dall’alto verso il basso, da


segmenti orientati, rappresentanti l’ordine di esecuzione delle
istruzioni corrispondenti

61
Diagrammi di flusso

START
Input/Output

istruzione

STOP

62
Diagrammi di flusso

vero falso
proposizione

vero
proposizione

1 1

falso

63
Diagrammi di flusso

Vero Falso
proposizione

A B

sequenza diramazione
IF THEN ELSE

64
Diagrammi di flusso

falso

proposizione

vero
proposizione
falso

vero

while repeat until

65
Diagrammi di flusso
Un diagramma a blocchi è un insieme di blocchi elementari
composto da:

• un blocco iniziale
• un blocco finale
• un numero finito n (n≥1) di blocchi di azione e/o di blocchi di
lettura/scrittura
• un numero finito m (m≥0) di blocchi di controllo

66
Diagrammi di flusso
L’insieme dei blocchi elementari che descrivono un algoritmo deve
soddisfare le seguenti condizioni:
• ciascun blocco di azione o di lettura/scrittura ha una sola freccia
entrante ed una sola freccia uscente
• ciascun blocco di controllo ha una sola freccia entrante e due
frecce uscenti
• ciascuna freccia entra in un blocco oppure si innesta in un’altra
freccia
• ciascun blocco è raggiungibile dal blocco iniziale
• il blocco finale è raggiungibile da qualsiasi altro blocco

Un blocco B è raggiungibile a partire da un blocco A se esiste una


sequenza di blocchi X1,X2,...,Xn, tali che A=X1, B=Xn, e ∀ Xi,
i=1,...,n−1, Xi è connesso con una freccia a Xi+1
67
Analisi Strutturata
I programmatori inesperti tendono ad “aggrovigliare” il
programma introducendo numerosi salti privi di regole (spaghetti
programming)
L’analisi strutturata favorisce la descrizione di algoritmi
facilmente documentabili e comprensibili
I blocchi di un diagramma a blocchi strutturato sono collegati
secondo i seguenti schemi:
Schema di sequenza – più schemi di flusso sono eseguiti in
sequenza
Schema di selezione – un blocco di controllo subordina
l’esecuzione di due possibili schemi di flusso al verificarsi di una
condizione
Schema di iterazione – si itera l’esecuzione di un dato schema di
flusso 68
analisi strutturata
un diagramma a blocchi strutturato è un diagramma a blocchi
nel quale gli schemi di flusso sono strutturati
Uno schema di flusso è strutturato quando soddisfa una delle
seguenti proprietà:
1) è uno schema elementare o di sequenza

69
analisi strutturata
2) è uno schema di selezione

Nel primo caso, lo schema S viene eseguito solo se la condizione


C è vera; se C è falsa, non viene eseguita alcuna azione
Nel secondo caso, viene eseguito solo uno dei due schemi Sv o
Sf, in dipendenza del valore di verità della condizione
70
analisi strutturata
3) è uno schema di iterazione

Nel primo caso, S può non venire mai eseguito, se la condizione


C è subito falsa; nel secondo caso, S viene eseguito almeno una
volta
Quando lo schema S viene eseguito finché la condizione C si
mantiene vera si parla di iterazione per vero; si ha un’iterazione
per falso quando S viene eseguito finché C è falsa 71
analisi strutturata
L’uso dell’analisi strutturata garantisce:

• facilità di comprensione e modifica dei diagrammi a blocchi


• maggiore uniformità nella descrizione degli algoritmi

Ogni diagramma a blocchi non strutturato è trasformabile in un


diagramma a blocchi strutturato equivalente

Due diagrammi a blocchi sono equivalenti se, operando sugli


stessi dati, producono gli stessi risultati

72
analisi strutturata

teorema fondamentale della programmazione di


Bohm−Jacopini, 1966:
ogni programma può essere codificato riferendosi
esclusivamente ad un algoritmo strutturato e quindi
attenendosi alle tre strutture fondamentali:
• Sequenziale
• Iterativa
• Condizionale

73
analisi strutturata
In un diagramma strutturato non apparirà mai una
istruzione di salto incondizionato

I tre schemi fondamentali possono essere concatenati,


uno di seguito all’altro, o nidificati, uno dentro l’altro; non
possono in nessun caso essere “intrecciati” o “accavallati”

74
analisi strutturata
Diagramma a blocchi per
la selezione, in un mazzo
di chiavi, di quella che
apre un lucchetto
1) Si seleziona una chiave dal mazzo e la si
marca con un pennarello
2) Si tenta di aprire il lucchetto con la chiave
appena marcata; se funziona, si va al passo 4)

3) Altrimenti, si controlla la chiave


successiva
3.1) Se non è marcata, la si marca e si
torna al passo 2)
3.2) Viceversa, si prende atto che nel
mazzo non è presente la chiave che
apre il lucchetto
4) Fine della ricerca

75
Esercizi
• Rappresentare l’algoritmo per il calcolo della distribuzione
dell’età.
• Rappresentare l’algoritmo per il calcolo di xy.
• Rappresentare l’algoritmo per il calcolo dei primi N numeri di
Fibonacci. (1 1 2 3 5 8..)
• Rappresentare l’algoritmo per ordinare in ordine crescente N
numeri (bubble sort)

76
Pseudocodifica
La pseudocodifica è un linguaggio per la descrizione di
algoritmi secondo le regole della programmazione strutturata

La descrizione di un algoritmo in pseudocodifica si compone


di due parti:
• la dichiarazione delle variabili usate nell’algoritmo
• descrizione delle azioni dell’algoritmo

77
Pseudocodifica
Tipo delle variabili

Il tipo di una variabile indica l’insieme dei valori che possono
essere assegnati a quella variabile

integer, real, boolean, string

Su costanti e variabili di un tipo è possibile effettuare le


operazioni che sono proprie di quel tipo

78
Pseudocodifica: Dichiarazione
delle variabili
Elenco, preceduto dalla parola var, delle variabili sulle quali
l’algoritmo opera
Le variabili sono suddivise per tipo: quelle dello stesso tipo sono
separate l’una dall’altra da una “,”
L’elenco delle variabili dello stesso tipo è seguito dai “:” e
dall’indicazione del tipo;

Esempio:
var i, j, a(20): integer;
p, q: real;
nome: string;
sw: boolean;
79
Descrizione delle azioni
Gli schemi di flusso fondamentali sono descritti utilizzando
convenzioni linguistiche: ad ogni schema strutturato corrisponde
una convenzione linguistica

La descrizione di un algoritmo deve soddisfare le seguenti regole:



a) La prima azione dell’algoritmo è preceduta dalla parola Start;

b) L’ultima azione dell’algoritmo è seguita dalla parola Stop;
c) L’azione di lettura è rappresentata dalla parola Input;
d) L’azione di scrittura è rappresentata dalla parola Output;
e) Lo schema di sequenza di n flussi S1, S2,..., Sn è
rappresentato come
S1;
S2;
...
Sn;
80
Descrizione delle azioni
Gli schemi di selezione sono rappresentati come:

Gli schemi di iterazione sono rappresentati come:

S, Sf, Sv sono schemi di flusso strutturati

81
Esempio: stampare i primi 10 multipli di x.

Problema semplice e chiaro: un solo dato in


input (un parametro che caratterizza l’istanza del
problema): la “base” costituita dal numero x;
La strategia risolutiva è la seguente: iniziando
dal numero x letto in input, per 10 volte verrà
calcolato e stampato il multiplo di x,
moltiplicando x per una variabile i il cui valore
verrà incrementato di una unità ad ogni passo,
da 1 fino a 10.
Esempio: stampare i primi 10 multipli
di x.

1. Leggi in input un numero e chiama x il


numero letto;
2. Assegna il valore 1 alla variabile i;
3. Calcola x•i e assegna alla variabile y il
risultato;
4. Stampa y;
5. Calcola i+1 e assegna alla variabile i il 

risultato;
6. Se i≤10 allora vai al passo 3 altrimenti
prosegui;
No
7. Fermati. i>10
Esercizio: rappresentare il flow-chart Si
utilizzando l’iterazione per vero a
controllo iniziale
Esempio: letti due numeri stampare il
massimo
letti in input 3 numeri stampare il massimo.
Algoritmo trova il massimo tra
N elementi
non è possibile
adottare la stessa 1. Input N (numero elementi)
strategia: dovremmo 2. Input Ai ….. AN

utilizzare troppe 3. Poni max = A1

variabili (quante?) 4. Poni i = 2


5. ripeti
È necessario adottare
5.1 se Ai > max allora poni max = Ai
una diversa strategia:
5.2 poni i = i + 1
individuare una
5.3 Finchè i > N (iter. per falso)
operazione semplice
6 Stampa max
che ripetuta più volte
porti alla soluzione. 86
Letti in input n numeri stampare
il massimo
non è possibile
adottare la stessa
strategia: dovremmo
utilizzare troppe
variabili (quante?)
È necessario adottare
una diversa strategia:
individuare una
operazione semplice
che ripetuta più volte
No
porti alla soluzione. i>n
Si

Esercizio: rappresentare il flow-


chart utilizzando l’iterazione per
vero a controllo iniziale
Algoritmo per il calcolo del
fattoriale
start
1

n
Fatt = Fatt * i

Fatt = 1
i=i+1

i=1
i>n
vero falso
1
Fatt

stop

88
Esempio: il calcolo del M.C.D.

Algoritmo per il calcolo del Massimo Comun


Divisore (M.C.D.) tra due numeri interi non
negativi
Es.:

M.C.D. (20, 12) = 4
M.C.D. (7, 21) = 7
M.C.D. (5, 9) = 1
M.C.D. (0, 6) = 6
Proprietà di Euclide (300 a.C.)
ognuno dei divisori comuni di due numeri a e b è
contemporaneamente divisore sia di a e b, sia del
resto r della divisione di a con b se questo non è
nullo:
M.C.D.(a,b) = M.C.D.(b,r) dove r = a mod b
M.C.D. (20, 12)= (=> 20 mod 12 = 8)
M.C.D. (12, 8) = (=> 12 mod 8 = 4)
M.C.D. (8, 4) = (=> 8 mod 4 = 0)

M.C.D. (20, 12) = 4
Esempio: il calcolo del M.C.D.

• Dati due numeri naturali a e b, se b è zero a è il MCD.


• Se non lo è, si divide a / b e si assegna ad r il resto
della divisione (r = a mod b).

• Se r = 0 ==> b è il MCD cercato,


• altrimenti occorre assegnare a = b e b = r e si ripete
nuovamente la divisione.
Proprietà di Euclide
(300 a.C.)

M.C.D. (9, 5) = (=> 9 mod 5 = 4)


M.C.D. (5, 4) = (=> 5 mod 4 = 1)
M.C.D. (4, 1) = (=> 4 mod 1 = 0)
M.C.D. (9, 5) =1
Esempio: il calcolo del M.C.D.
1) Start
2) Input numeri a e b
3) Se b = 0 allora la risposta è a 

e vai alla fine
Altrimenti
r = a mod b
a=beb=r
Se r = 0 allora
Output b,
Altrimenti
torna all’istruzione 3
Stop
SVILUPPO TOP DOWN
Il processo di progettazione di una procedura complessa
necessita di metodologie e strumenti di analisi atti a
razionalizzare e velocizzare la fase di analisi di un problema .
I problemi reali sono spesso di un livello di complessita’ tale,
da impedire la visione contemporanea di tutto il problema.
Metodo di approssimazioni successive, i diversi aspetti del
problema sono scomposti in altrettanti sottoproblemi per i
quali si immagina inizialmente di possedere già le
corrispondenti procedure risolutive.
In tal modo, delegando procedure di livello gerarchico
inferiore a risolvere gli aspetti secondari, ci si puo’ dedicare al
problema nella sua interezza.
QUESTO TIPO DI ANALISI E’ DI TIPO TOP-DOWN.
94
SVILUPPO TOP DOWN
Si parte con le definizioni dei dati del problema e della logica
di controllo globale e si procede verso il basso, cioe’ verso i
diversi componenti funzionali della procedura, dettagliando
sempre di piu’ le componenti risolutive.

La strategia TOP_DOWN propone quindi di decomporre il


problema in tanti sottoproblemi, proseguendo nella
decomposizione fino a quando ogni singolo
sottoproblema potra’ essere essere espresso in termini di
istruzioni del linguaggio scelto.

95
SVILUPPO TOP DOWN
Es. realizzare un programma per l’analisi della frequenza
delle parole in un testo.

Leggere ed analizzare gli elementi di un testo, creando e


mantenendo una tabella che contenga tutti i tipi di parole
trovate e contando il numero di volte che queste si
presentano.
Alla fine deve essere prodotta a terminale e su archivio la
tabella e la corrispondente statistica.

96
SVILUPPO TOP DOWN
INIZIO
RIPETI FINCHE’ NON CI SONO ANCORA RIGHE NEL TESTO
LEGGI LA RIGA
RIPETI FINCHE NON VI SONO ANCORA PAROLE NELLA RIGA
ESTRAI LA PAROLA DALLA RIGA
RICERCA LA PAROLA NELLA TABELLA DELLE PAROLE
SE PAROLA ESTRATTA E’ TROVATA
ALLORA incrementa il contatore frequenza della parola
ALTRIMENTI Inserisci nella tabella la parola estratta
Imposta il contatore frequenza della parola al valore 1
FINE SE
Incrementa il contatore totale delle parole esaminate
ESTRAI LA SUCCESSIVA PAROLA DALLA RIGA
FINE RIPETI ( parole nella riga )
FINE RIPETI (lettura righe)
ARCHIVIA LA TABELLA DELLE PAROLE ARCHIVIA LE STATISTICHE DEL
TESTO
97
FINE
SVILUPPO BOTTOM-UP
Questa metodologia tende ad analizzare un progetto dalle
esigenze specifiche dei sottosistemi che fanno parte del
progetto per poi risalire a livelli piu’ alti per comporre pian piano
il progetto globale.

costruisce la soluzione individuando inizialmente un insieme di


passi o funzionalita’ elementari, e successivamente riunendo
via via tali funzionalita’ in frammenti piu’ grossi, fino alla
composizione dell’intera soluzione del problema.

es. costruzione di un di un libro; si puo’ cominciare a comporre


parti di un capitolo ( paragrafi ) e in seguito si raccordano per
formare il capitolo e cosi’ via fino a produrre i vari capitoli .
Successivamente si produce una introduzione al libro, un
sommario e un indice. 98
SVILUPPO BOTTOM-UP
es. Un progetto aziendale complesso di gestione
amministrativo- contabile puo’ iniziare con l’analisi delle
particolari esigenze dei vari reparti e uffici (stampe,
visualizzazioni, consuntivi, modelli, ordini, bilanci parziali,
etc.), scrivendone le procedure dettagliate ( in tal modo gli
uffici potranno gia’ iniziare ad avere i primi risultati, senza
aspettare che tutto il progetto sia ultimato).
In seguito, riunire tutto il lavoro , collegare tutti i programmi
che vengono usati nei rispettivi reparti , e presentare la
procedura finita

99
Sottoprogrammi:
esempio Somma di n numeri

1. Input: un naturale n; Output: la somma di n


naturali
2. Leggi n
3. sum = 0

mentre (n>0) fai
3.1 leggi x
3.2 sum = sum + x
3.3 n = n-1
4. stampa sum
Calcolare la media aritmetica
di n naturali
Sottoprogrammi

Per calcolare la media aritmetica fra n naturali


sarebbe comodo riutilizzare il codice per la somma
di numeri naturali.
I sottoprogrammi permettono di scrivere codice
• modulare
• leggibile
• riutilizzabile
Sottoprogrammi

In un sottoprogramma è necessario definire quali


sono i parametri di input e i parametri di output
(definizione dell’interfaccia)
Tali parametri sono chiamati parametri formali.

Sottoprogramma <nome sottoprg> (in: x1, x2,... xn;


out: y1,y2,... ym)
Blocco
Sottoprogramma
definizione
sottoprogramma somma (in:n; out: sum)
{
1. sum = 0

2. mentre (n>0) fai {
2.1 leggi x
2.2 sum = sum + x
2.3 n = n-1
}
}
Sottoprogramma
chiamata
Un sottoprogramma per essere eseguito deve essere
“chiamato” da un’altro programma che lo utilizza
esplicitando la lista di parametri di input e output
(parametri attuali).

I nomi dei parametri formali e attuali possono essere


distinti. Al momento della chiamata vengono messi in
corrispondenza in base all’ordine in cui sono stati
dichiarati.
nomesottoprg(in:a1,...,an; out:b1,...,bm)
Media aritmetica

leggi n1
somma(in:n1,out: sum1)
media = sum1/n1
stampa media
Gli algoritmi ricorsivi
Un algoritmo si dice ricorsivo quando è definito in termini di
se stesso. Una sua istruzione richiede una nuova esecuzione
dell’algoritmo stesso

La definizione ricorsiva di un algoritmo è suddivisa in due


parti:
a) la base della ricorsione, che stabilisce le condizioni
iniziali, cioè il risultato che si ottiene per i dati iniziali (in
generale per 0 e/o 1)
b) la regola di ricorsione, che definisce il risultato per un
valore n, diverso dal valore iniziale per mezzo di
un’espressione nella quale si richiede il risultato
dell’algoritmo calcolato per n−1
108
Gli algoritmi ricorsivi
Esempio: Prodotto di numeri interi
a×b = 0 se b=0 (base della ricorsione)
a×b = a×(b−1)+a se b≠0 (regola di ricorsione)

Secondo la definizione ricorsiva si ha:


3×2 = 3×1+3=3×0+3+3=0+3+3=6

L’esecuzione di un algoritmo ricorsivo termina sempre: la


regola di ricorsione prevede nuove esecuzioni su dati
decrescenti, fino ad ottenere i dati di inizio ricorsione (base
della ricorsione)

109
Gli algoritmi ricorsivi
Esempio: Calcolo del fattoriale di un numero intero

Il fattoriale di n è il prodotto di tutti gli interi da 1 ad n,
n! = n × (n−1) × (n−2) ×...× 2 × 1
Per definizione, 0! = 1
Start fattoriale(n)
input n
if n=0
then f = 1
else f = n × fattoriale(n−1)
endif
output f
Stop 110
Gli algoritmi ricorsivi
La successione di Fibonacci

Leonardo Pisano, detto Fibonacci, pose il seguente
quesito:
Una coppia di conigli giovani impiega una unità di tempo a
diventare adulta; una coppia adulta impiega una unità di
tempo a riprodursi e generare un’altra coppia di conigli
(chiaramente giovani); i conigli non muoiono mai

Quante coppie di conigli abbiamo al tempo t generico se al


tempo t=0 non abbiamo conigli e al tempo t=1 abbiamo una
coppia di giovani conigli?

111
Gli algoritmi ricorsivi
La successione di Fibonacci


112
La successione di Fibonacci

(soluzione Iterativa)
Start
Input N
A = 1; B = 1; I = 1;
finchè I < N ripeti
Fibo = A + B
A = B; B= Fibo;
I=I+1
Output Fibo
Stop

113
Gli algoritmi ricorsivi
La successione di Fibonacci


Il calcolo di Fn (numero di coppie di conigli), per qualsiasi


tempo t, genera la successione dei numeri di Fibonacci

base della ricorsione F0=0, F1=1,

relazione di ricorsione Fn = Fn−1 + Fn−2

114
Efficienza

• Problemi non risolvibili;


• Efficienza e complessità computazionale
• Modello di costo

115
Problemi non risolvibili
con Calcolatore Elettronico

Esistono problemi che non possono essere risolti


tramite un calcolatore elettronico perché:
• la soluzione del problema non esiste
• la soluzione del problema richiederebbe un tempo
di calcolo eccessivo (anche infinito)
• la natura del problema è percettiva e/o la soluzione
del problema è “soggettiva”

116
Problemi non risolvibili
con Calcolatore Elettronico

esempio di problema non risolubile: stabilire se un


programma terminerà per un dato input

esempio di problema la cui soluzione richiederebbe


un tempo infinito: stabilire se, data una funzione
intera f, f(x) è costante per ogni valore di x

esempio di problema la cui soluzione è soggettiva:


scelta, dato un insieme di immagini di paesaggi, del
paesaggio più rilassante
117
Efficienza
Un problema spesso può essere risolto utilizzando
algoritmi diversi
Come scegliere il “migliore”?
La bontà o efficienza di un algoritmo si misura in base alla
quantità minima di risorse sufficienti per il calcolo:
• Il tempo richiesto per eseguire le azioni elementari
• Lo spazio necessario per memorizzare e manipolare i
dati

La risorsa di maggior interesse è il tempo

118
Efficienza - Complessità
Computazionale
Un algoritmo A è più efficiente di un algoritmo B, se
risolve lo stesso problema usando meno risorse.

Risorse necessarie a un calcolatore per l’esecuzione di un


algoritmo: quantità di memoria (complessità spaziale)

tempo d’esecuzione (complessità temporale)

Il tempo d’esecuzione dipende dalle caratteristiche del


calcolatore. Non è significativo misurare il tempo di
calcolo di un algoritmo in base al numero di secondi
richiesti per l’esecuzione su un elaboratore di un
programma che lo descrive
119
Efficienza - Complessità
Computazionale
il tempo di calcolo di un algoritmo dipende da:
• Dati di ingresso
• il linguaggio in cui è scritto il programma
• la qualità della traduzione in sequenze di bit
• la velocità dell’elaboratore

Una buona misura dell’efficienza di un algoritmo deve


prescindere dal calcolatore utilizzato

Occorre una misura “astratta” che tenga conto del


“metodo di risoluzione” con cui l’algoritmo effettua la
computazione
120
Efficienza - Complessità
Computazionale
La complessità dei problemi da risolvere dipende dalla
dimensione dei dati in ingresso
Il tempo di calcolo si può quindi esprimere come:
il numero complessivo di operazioni elementari
in funzione della dimensione n dei dati in
ingresso
Le operazioni elementari sono quelle:
• aritmetiche
• logiche
• di confronto
• di assegnamento
121
Efficienza - Complessità
Computazionale

Esempio: Ricerca di un elemento in un vettore


Si possono distinguere 3 casi:

• Caso migliore: l’elemento cercato è in prima posizione.


• Caso peggiore: l’elemento cercato è in ultima posizione.
• Caso medio: ipotesi di distribuzione uniforme, ovvero che
i valori siano disposti casualmente nel vettore

il numero di operazioni considerate è solitamente valutato


nel caso peggiore tra tutti quelli di dimensione n.

122
Efficienza - Complessità
Computazionale
Esempio: Trovare l’elemento minimo in un insieme di n
numeri {x1,x2,...,xn}
Soluzione:
1. minimo = x1
2. confronto il minimo con tutti gli altri elementi e se
trovo un elemento più piccolo lo faccio diventare il
nuovo minimo
3. al termine dei confronti, il minimo corrente è
sicuramente il valore cercato
Complessivamente sono stati eseguiti n confronti

L’efficienza dell’algoritmo è quindi direttamente


proporzionale alla dimensione dell’input
123
Efficienza - Complessità
Computazionale
Esempio: Ordinare in ordine crescente un insieme di n
numeri {x1,x2,...,xn} Soluzione:

1. per i = 1,2,...,n ripeti le seguenti operazioni:


2. trova l’elemento minimo nel sottoinsieme {xi,xi+1,…,xn}
3. scambialo con xi
Per n volte bisogna trovare il minimo su insiemi sempre più
piccoli
Si eseguono n + (n-1) + (n-2) + ... + 2 + 1≈ n2 operazioni

124
Efficienza

L’efficienza di un algoritmo si può esprimere mediante una


funzione f(n):
• esprime il numero di operazioni compiute per un
problema di dimensione n
• rappresenta la complessità computazionale
dell’algoritmo
Se A e B sono due algoritmi che risolvono lo stesso
problema e se fA(n) e fB(n) esprimono la complessità dei due
algoritmi allora:

A è migliore di B se, al crescere di n, risulta fA(n) ≤ fB(n)

125
Efficienza

Supponiamo di avere due algoritmi diversi per ordinare n


numeri interi
• Il primo algoritmo riesce ad ordinare gli n numeri
con n2 istruzioni
• Il secondo con n * log n istruzioni
ipotesi: l’esecuzione di un’istruzione avvenga in un µ sec
µ sec

126
Efficienza
Quali fattori influenzano il tempo di esecuzione?
• L’algoritmo scelto per risolvere il problema
• La dimensione dell’input
• La velocità della macchina

Necessità di un modello di calcolo per la complessità


temporale che tenga conto di
• Algoritmo
• Dimensione dell’input

Modello di costo
127
Efficienza
Il modello di costo è funzione di:

• Dimensione dell’input
• Istruzione di costo unitario (passo base)
• Calcolo della complessità in numero di passi base
• Complessità
• Nel caso migliore
• Nel caso medio
• Nel caso peggiore

128
Efficienza
Dimensione dell’input
A seconda del problema, per dimensione dell’input si
indicano cose diverse:
• La grandezza di un numero (es: problemi di calcolo)
• Quanti elementi sono in ingresso (es: ordinamento)
• Quanti bit compongono un numero

Indipendentemente dal tipo di dati, indichiamo con n la


dimensione dell’input

129
Efficienza
Operazione di costo unitario
È un’operazione la cui esecuzione non dipende dai valori e
dai tipi delle variabili

• Assegnamento e operazioni aritmetiche di base


• Accesso ad un elemento qualsiasi di un vettore
residente in memoria centrale
• Valutazione di un’espressione booleana qualunque
• Istruzioni di input/output

130
Efficienza
Esempio: Calcolo della complessità in numero di passi
base
i = 0;
while(i < n)
i = i+1;
Assegnamento esterno al ciclo: i=0;
Ciclo while: si compone di un test (i<n) e di un corpo
costituito da un’operazione di assegnamento (i=i+1)
Assegnamento esterno: 1
Numero di test: n+1
Assegnamenti interni: 1*n

Numero totale di passi base: 2*n+2


131
Algoritmo di ricerca sequenziale
Problema: determinare se un certo numero è presente in una
data sequenza di numeri.

Dati da elaborare: una sequenza di numeri, e il numero da cercare.
Risultato desiderato: può essere considerato come una risposta
tra due possibili alternative, per es., “sì” oppure “no”.

Una possibile soluzione: scorrere la sequenza dal primo all’ultimo


elemento, confrontando ciascuno di essi con il valore cercato.
L’algoritmo termina:
• non appena si trova il valore cercato (gli eventuali elementi
successivi vengono trascurati): il risultato è “sì”
• quando si arriva alla fine della sequenza, senza aver trovato il
valore cercato: il risultato è “no”

132
Algoritmo di ricerca sequenziale
Il numero 31 è presente nella sequenza (52, −48, −34, 75, −80)?
primo passo: 52, −48, −34, 75, −80 

52 è uguale a 31? no, quindi si considera l’elemento successivo
secondo passo: 52, −48, −34, 75, −80 

−48 è uguale a 31? no, ... 


terzo passo: 52, −48, −34, 75, −80 



−34 è uguale a 31? no, ... 


quarto passo: 52, −48, −34, 75, −80 



75 è uguale a 31? no, ... 


quinto passo: 52, −48, −34, 75, −80 



−80 è uguale a 31? no, e non ci sono altri elementi
Risultato: no (31 non è presente nella sequenza considerata). 

133
Algoritmo di ricerca sequenziale
Il numero −34 è presente nella sequenza (52, −48, −34, 75, −80)?
primo passo: 52, −48, −34, 75, −80
52 è uguale a −34? no, …

secondo passo: 52, −48, −34, 75, −80
−48 è uguale a −34? no, ...

terzo passo: 52, −48, −34, 75, −80



−34 è uguale a −34? sì (si possono trascurare gli elementi
successivi)

Risultato: sì (−34 è presente nella sequenza considerata).

134
Algoritmo di ricerca
sequenziale: efficienza
La sua efficienza in termini del numero di operazioni richieste
(complessità temporale) può essere facilmente valutata, nel caso
peggiore, tenendo conto che:

l’algoritmo consiste in una serie di confronti tra coppie di elementi


(l’oggetto da cercare e un elemento della sequenza)
il caso peggiore corrisponde all’istanza che richiede il maggior
numero di confronti, a parità della lunghezza della sequenza

È facile convincersi che tra tutte le istanze in cui la sequenza ha una


stessa lunghezza N, il caso peggiore è quello in cui l’oggetto da
cercare è l’ultimo elemento della sequenza, oppure non è
presente in essa: in questi casi il numero di confronti è pari a N.
135
Efficienza
Supponiamo di avere il seguente vettore A contenente 14
elementi:
A = {3, -2, 0, 7, 5, 4, 0, 6, -3, -1, 9, 12, 20, 5}
• La dimensione dell’input è la lunghezza dell’array (n=14)
• Cerchiamo l’elemento "6" con un algoritmo di ricerca
lineare:
i = 0;
while(i<14 && A[i] != 6)
i = i+1;
Costo corrispondente :
1 assegnamento + 8 test + 7 cicli = 16 passi base

136
Efficienza
A = {3, -2, 0, 7, 5, 4, 0, 6, -3, -1, 9, 12, 20, 5}

Se avessimo cercato l’elemento “13"?

Elemento non è presente nel vettore:


scorrere interamente il vettore con una complessità:

1 + (n+1) + n = 2 + 2*n passi base (30 passi nell’esempio)

137
Efficienza
Ricerca di un elemento x in un vettore:
i = 0;
while(i<14 && A[i] != x)
i = i+1
Caso migliore: l’elemento cercato è in prima posizione. Il
costo effettivo dell’algoritmo è 2
Caso peggiore: l’elemento cercato è in ultima posizione. Il
costo effettivo è 2*n
Caso medio: ipotesi di distribuzione uniforme (valori siano
disposti casualmente nel vettore)

138
Efficienza
Caso medio: ipotesi di distribuzione uniforme (valori siano
disposti casualmente nel vettore)

valori sono distribuiti uniformemente, la probabilità di trovare


l’elemento cercato in posizione i nel vettore è:
Pr(i) = 1/n

il numero di test da esguire per trovare l’elemento nella


posizione i è: C(i) = i

numero medio di confronti:

139
Algoritmo di ricerca sequenziale

1 Prendi il dizionario

2 Aprilo alla prima pagina
3 Leggi le parole presenti
4 se “xxxx” è presente

5 leggi la definizione
6 altrimenti
7 se è l’ultima pagina
8 non c’è!
9 altrimenti

10 gira pagina

11 vai al punto 3

140
Algoritmo di ricerca sequenziale
1. Prendi il dizionario
2. Aprilo alla prima pagina
3. Leggi le parole presenti
4. se “xxxx” è presente
5. leggi la definizione
6. altrimenti se è dopo
7. gira due pagine
8. vai al punto 3
9. altrimenti se l’ho superato
10. torna indietro di una pagina
11. se “xxxx” è presente
12. leggi la definizione
13. altrimenti
14. non c’è!
141
Algoritmo di ricerca binaria
Un algoritmo più efficiente può essere formulato per il caso particolare in cui la
sequenza sia ordinata secondo un certo criterio, usando un approccio analogo
a quello che si segue nella ricerca di un nome in un elenco telefonico.

Informalmente: si apre l’elenco in corrispondenza delle due pagine centrali


(approsimativamente); se il nome cercato si trova in tali pagine, la ricerca
termina; altrimenti:
• se il nome cercato precede (in ordine alfabetico) il primo nome delle pagine
considerate, la ricerca procede nelle sole pagine precedenti, nello stesso
modo (aprendo l’elenco più o meno a metà di tali pagine, ecc.) 


• se il nome cercato segue l’ultimo nome delle pagine considerate, la ricerca


procede nelle sole pagine successive 


142
Algoritmo di ricerca binaria
si confronta l’elemento da cercare (indicato con x) con quello al centro della sequenza
(indicato con c);
se x è uguale a c la ricerca termina (la risposta è “sì”);
in caso contrario:
• se x precede c, la ricerca procede nello stesso modo tra gli elementi che precedono
c
• se x segue c, la ricerca procede nello stesso modo tra gli elementi che seguono c

L’algoritmo termina:
• non appena si trova il valore cercato: il risultato è “sì”
• quando la sottosequenza da considerare non contiene nessun elemento: il risultato è
“no”

Nota: se la sequenza (o sottosequenza) considerata contiene un 

numero pari di elementi (N), si considera come elemento centrale 

quello in posizione N/2 o N/2 + 1. 


143
Algoritmo di ricerca binaria
Determinare se il numero 31 sia presente nella sequenza: (1, 6, 8, 10, 18, 25, 32, 34,
37, 52, 60).

In rosso si mostra l’elemento centrale della sequenza considerata:


(1,6,8,10,18,25,32,34,37,52,60)
25 è uguale a 31? no; dato che 25< 31:
(32,34,37,52,60)
37 è uguale a 31? no; dato che 37> 31:
(32,34)
32 è uguale a 31? no; dato che 32> 31:


(): questa sottosequenza è vuota, l’algoritmo termina


Risultato: no (31 non è presente nella sequenza considerata).

144
Algoritmo di ricerca binaria
Determinare se il numero 8 sia presente nella sequenza: (1, 6, 8, 10, 18, 25, 32, 34,
37, 52, 60).

(1,6,8,10,18,25,32,34,37,52,60) 25 è uguale a 8? no; dato che 25> 8:

(1,6,8,10,18)

8 è uguale a 8? sì: l’algoritmo termina

Risultato: sì (8 è presente nella sequenza considerata).

145
Algoritmo di ricerca binaria:
efficienza
Caso peggiore: il numero maggiore di confronti si ha quando l’oggetto da cercare non
è presente nella sequenza considerata, oppure quando si trova nell’ultima
sottosequenza considerata, e questa risulta composta da un solo elemento.

Il corrispondente numero di confronti può essere approssimato tenendo conto che

• dopo ogni confronto la lunghezza della successiva sottosequenza si riduce della


metà

• l’ultimo confronto viene eseguito su una sottosequenza composta da un solo


elemento

146
Algoritmo di ricerca binaria:
efficienza
Per una sequenza iniziale di N elementi, il numero di confronti nel caso
peggiore (indicato con k), può essere calcolato come segue:

num. conf. Lung. seq.


1 N N/20
2 N/2 N/21
3 N/4 N/22

k 1 N/2k-1

Da cui si ottiene facilmente (N/2k-1 = 1):



k = ⌈log2 N + 1⌉
(⌈x⌉ indica il più piccolo intero maggiore o uguale a x).
147
Confronto tra ricerca
sequenziale e binaria
Il numero di confronti richiesto nel caso peggiore, per sequenze ordinate di
N elementi, è quindi pari a
N, per la ricerca sequenziale

≈ ⌈log2 N⌉, per la ricerca binaria
Poiché N > log2 N per qualsiasi valore di N, la ricerca binaria è più
efficiente di quella sequenziale. Il vantaggio diventa evidente al crescere di
N, come mostra il seguente esempio:

148
Ricerca binaria

1. Prendi il dizionario
2. Aprilo a metà
3. Leggi le parole presenti
4. se “cane” è presente
5. leggi la definizione ∎

6. altrimenti se è prima nel dizionario
7. apri la metà sinistra
8. vai al punto 3
9. altrimenti se è dopo nel dizionario
10. apri la metà destra
11. vai al punto 3
12. altrimenti
13. non c’è!

149
Algoritmi di ordinamento

Problema: ordinare secondo un certo criterio una data


sequenza di oggetti.

Dati di partenza: una sequenza di oggetti, e un criterio di
ordinamento.
Risultato desiderato: gli stessi elementi della sequenza
iniziale, ordinati secondo il criterio indicato.

ordinamento per selezione (selection sort)



ordinamento “a bolle” (bubble sort)

ordinamento per inserimento (insertion sort)

150
Ordinamento per selezione
Idea di base: spostare l’elemento più piccolo nella prima
posizione della sequenza, quello immediatamente successivo
nella seconda posizione, e così via.
L’algoritmo di ordinamento per selezione ottiene questo risultato
scandendo le posizioni della sequenza dalla prima alla penultima; per
ogni posizione:
• cercare la posizione dell’elemento più piccolo a partire da quello
nella posizione considerata (si può usare un algoritmo analogo a
quello per la ricerca del massimo) 


• se la posizione di tale elemento non coincide con la posizione


considerata, si scambiano gli elementi nelle due posizioni 


Una variante dell’algoritmo consiste nello spostare nell’ultima


posizione l’elemento più grande, nella penultima posizione quello
immediatamente precedente, e così via.
151
Ordinamento per selezione
Ordinare in senso crescente i seguenti numeri, con l’algoritmo di
ordinamento per selezione: 52, −34, −48, 58, 75, −80, 22.

In rosso si mostra la posizione considerata a ogni passo dell’algoritmo,
in grassetto l’elemento più piccolo da quella posizione in poi. Se i due
elementi coincidono, non si esegue nessuno scambio.

152
Ordinamento per selezione
1) Start
2) I=1
3) ripeti (4-13) I posizione elemento attuale
4) K=I, J=I+1 K posizione elemento minimo
5) ripeti (6-9)
se I e K coincidono
posizione correta passa elemento
6) se V(K)>V(J)
successivo
7) K=J altrimenti scambia elementi e
8) J=J+1 passa elemento successivo
9) finchè J diventa > N
10) se I != K
11) scambio V(I) con V(K)
12) I=I+1
13) finchè I diventa > N-1
14) STOP 153
Ordinamento per selezione
2
1

1 2

154
Ordinamento a bolle

L’idea di base è la stessa dell’ordinamento per selezione:


spostare in prima posizione l’elemento più piccolo, in seconda
posizione quello immediatamente successivo, e così via.
L’algoritmo di ordinamento “a bolle” ottiene però questo
risultato senza ricercare esplicitamente a ogni passo
l’elemento più piccolo di ogni sottosequenza.

L’algoritmo BUBBLESORT (ordinamento “a bolle”) si basa


sull’idea di far “emergere” man mano gli elementi più piccoli
verso l’inizio dell’insieme da ordinare, facendo “sprofondare”
al tempo stesso gli elementi più grandi verso il fondo
dell’insieme, un po’ come avviene con le bollicine gassose in
un bicchiere di acqua minerale.
155
Ordinamento a bolle
La strategia adottata è quella di scorrere più volte la sequenza da
ordinare, verificando ad ogni passo l’ordinamento reciproco degli
elementi contigui, ai e ai+1, ed eventualmente scambiando la
posizione di coppie di elementi non ordinate.
Procedendo dalla fine (basso) all’inizio (alto) della sequenza, al
termine di ogni scansione si è ottenuto che l’elemento minimo è
finito in testa alla sequenza stessa, mentre gli elementi più grandi
hanno cominciato a spostarsi verso la fine (basso) della sequenza
stessa.
Alla fine della prima scansione l’elemento minimo ha raggiunto la
sua posizione corretta in testa alla sequenza;
la scansione successiva potrà fermarsi senza considerare il primo
elemento, collocando nella posizione corretta (la seconda) il
secondo elemento più piccolo dell’insieme; e così via fino ad aver
completato l’ordinamento dell’intera sequenza.
156
Ordinamento a bolle
Ordinare in senso crescente i seguenti M = 7 numeri, con l’algoritmo
di ordinamento “a bolle”: 52, −34, −48, 58, 75, −80, 22.
per ogni sottosequenza considerata a ogni passo dell’algoritmo, in
grassetto sono indicati i due elementi confrontati; se i due elementi
non si trovano nell’ordine desiderato, vengono scambiati.

primo passo: si considera l’intera sequenza di 7 elementi


52, −34, −48, 58, 75, −80, 22: nessuno scambio

52, −34, −48, 58, 75, −80,22: scambio

52, −34, −48, 58, −80, 75,22: scambio
52, −34, −48, −80, 58, 75,22: scambio
52, −34, −80, −48, 58, 75,22: scambio
52, −80, −34, −48, 58, 75,22: scambio
−80, 52, −34, −48, 58, 75,22
157
Ordinamento a bolle
secondo passo: si considerano gli ultimi 7 − 1 elementi
−80, 52, −34, −48, 58, 75,22: scambio

−80, 52, −34, −48, 58, 22,75: scambio

−80, 52, −34, −48, 22, 58, 75: nessuno scambio
−80, 52, −34, −48, 22, 58,75: scambio
−80, 52, −48, −34, 22, 58,75: scambio
−80, −48, 52, −34, 22, 58,75
terzo passo: si considerano gli ultimi 7 − 2 elementi
−80, −48, 52, −34, 22, 58, 75: nessuno scambio
−80, −48, 52, −34, 22, 58, 75: nessuno scambio
−80, −48, 52, −34, 22, 58, 75: nessuno scambio
−80, −48, 52, −34, 22, 58,75: scambio
−80, −48, −34, 52, 22, 58,75
158
Ordinamento a bolle
quarto passo: si considerano gli ultimi 7 − 3 elementi
−80, −48, −34, 52, 22, 58, 75: nessuno scambio
−80, −48, −34, 52, 22, 58, 75: nessuno scambio
−80, −48, −34, 52, 22, 58,75: scambio
−80, −48, −34, 22, 52, 58,75
quinto passo: si considerano gli ultimi 7 − 4 elementi
−80, −48, −34, 22, 52, 58, 75: nessuno scambio
−80, −48, −34, 22, 52, 58, 75: nessuno scambio
−80, −48, −34, 22, 52, 58,75
sesto passo: si considerano gli ultimi 7 − 5 elementi
−80, −48, −34, 22, 52, 58, 75: nessuno scambio

Risultato: −80, −48, −34, 22, 52, 58,75.


159
Ordinamento a bolle
1) Input N, V(i)
2) i=2;
3) finchè i < = n-1 ripeti
4) j = n
5) finchè j < = i ripeti Ciclo esterno: I = 2 .. N-1
ciclo interno J = N .. I
6) se V(j) < V(j-1) confronta V(J) con V(j-1)
7) scambio
n.b. se I partisse dal valore
8) j=j-1 1, per I =1 si confronterebbe
9) fine ciclo interno (j) V(1) con V(0)
10) i=i+1
11) finchè ciclo esterno (i)
12) output V(i)

160
Ordinamento a bolle
Ordinamento a bolle
Ordinamento per inserimento

L’idea è la seguente: se gli elementi nelle prime M posizioni


(per un qualunque M ∈ {1, 2, . . . , N − 1}) si trovano già
nell’ordine desiderato (anche se questa non fosse la loro
posizione corretta nell’intera sequenza ordinata), si può
ottenere una sequenza ordinata nelle prime M + 1 posizioni
inserendo tra i primi M elementi quello attualmente in
posizione M + 1, in modo da rispettare l’ordinamento
desiderato.

Per trovare la posizione corretta di tale elemento è sufficiente


eseguire una successione di confronti con gli elementi che lo
precedono, a partire da quello in posizione M e procedendo a
ritroso.
163
Ordinamento per inserimento

Esempio: sequenza −12, −7, 5, 2, 9, 7


i primi M = 3 elementi sono in ordine crescente;
−12, −7, 5, 2, 9, 7
per far sì che i primi M + 1 = 4 lo siano:
inserire l’elemento in posizione M + 1 (il numero 2) in terza
posizione, spostando gli elementi maggiori di 2 (in questo
caso, il solo numero 5) di una posizione a destra:
−12, −7, 2, 5, 9, 7.

164
Ordinamento per inserimento
Si può allora partire dalla sottosequenza composta dal solo
primo elemento (M = 1), che si può sempre considerare già
ordinata, e:
inserire il secondo elemento nella posizione desiderata
rispetto a tale sottosequenza 


inserire il terzo elemento nella sottosequenza (già ordinata)


dei primi due 


inserire il quarto elemento nella sottosequenza dei primi tre


...
inserire l’ultimo elemento nella sottosequenza dei primi N − 1. 


165
Ordinamento per inserimento
Ordinare in senso crescente i seguenti numeri, con l’algoritmo
di ordinamento per inserimento: 52, −34, −48, 58, 75, −80, 22.
A ogni passo, in grassetto si mostra la sottosequenza già
ordinata, e in rosso l’elemento da inserire in tale
sottosequenza:
52, −34, −48, 58, 75, −80, 22
−34, 52, −48, 58, 75, −80, 22
−48, −34, 52, 58, 75, −80, 22
−48, −34, 52, 58, 75, −80, 22
−48, −34, 52, 58, 75, −80, 22
−80, −48, −34, 52, 58, 75, 22
−80, −48, −34, 22, 52, 58, 75
Risultato: −80, −48, −34, 22, 52, 58, 75. 166
Ordinamento per inserimento
Input N, V
Indice elemento da ordinare J (parte da 2)
inserisci nella sequenza odinata:
Ripeti (ciclo esterno per tutti gli elementi, J da 2 ad N)
(confronta elemento da inserire con elemento precedente
I = J - 1)
se elemento da inserire è più piccolo allora scambia e
confronta con elementi precedenti
( ciclo interno I = I - 1 )
incrementa J
ripeti

167
Ordinamento per inserimento

1 2

SI NO
I<=0

1 2 168
Algoritmi di ordinamento:
efficienza
La complessità temporale può essere valutata in termini del
numero di confronti da eseguire tra coppie di elementi (dopo
ogni confronto può esserci o meno uno scambio tra due
elementi).

Non è difficile rendersi conto che per l’ordinamento per


selezione e l’ordinamento “a bolle”, il numero di confronti
non dipende dalla sequenza di partenza.

Nel caso dell’ordinamento per inserimento, il numero di


confronti dipende invece dalla particolare sequenza di
partenza, e il caso peggiore è quello in cui tale sequenza sia
ordinata nel senso opposto a quello desiderato 169
Algoritmi di ordinamento:
efficienza
Esempio: ordinamento per selezione di una sequenza di N
elementi
passo 1: per trovare l’elemento più piccolo dell’intera
sequenza sono necessari N − 1 confronti (assumendo
inizialmente che il più piccolo sia il primo)
passo 2: N − 2 confronti 

... 

passo N − 1: un confronto

Numero totale di confronti: (N−1)+(N−2)+...+1= 


170
Algoritmi di ordinamento:
efficienza
Esempio: ordinamento per inserimento di una sequenza di
N elementi (caso meno vantaggioso, quando gli elementi
dell’insieme da ordinare sono inizialmente disposti secondo
l’ordinamento opposto a quello desiderato)
il ciclo interno eseguirà ogni volta il massimo numero di
iterazioni,perché ogni elemento ai si trova alla massima
distanza dalla posizione corretta in cui deve essere inserito.
Dunque per collocare l’elemento ai il ciclo interno dovrà
eseguire i − 1 iterazioni. Globalmente quindi il numero di
operazioni eseguite dall’algoritmo nel caso peggiore può
essere calcolato come segue:

1+2+…+n−1= 171
Algoritmi di ordinamento:
efficienza
Esempio: ordinamento a bolle di una sequenza di N
elementi
Il caso meno favorevole (sequenza inizialmente ordinata al
contrario).
Ad ogni iterazione del ciclo esterno si riesce a collocare nella
posizione corretta soltanto un elemento dell’insieme;
Sono necessarie n − 1 iterazioni del ciclo esterno per ordinare
l’intera sequenza.
Per ogni iterazione del ciclo esterno il ciclo nidificato al suo
interno esegue ogni volta una ripetizione in meno. Quindi il
numero di operazioni svolte nel caso peggiore può essere
calcolato ancora una volta con la seguente espressione:

172
Algoritmi di ordinamento:
efficienza
Il numero di confronti richiesti dai tre algoritmi di ordinamento
appena descritti, per una sequenza di N elementi, è quindi
proporzionale a N2.

Esistono altri algoritmi di ordinamento, più complessi, alcuni


dei quali hanno un’efficienza maggiore:
Quick Sort (numero di confronti proporzionale a N2)

Merge Sort (numero di confronti proporzionale a N log2 N)
Heap Sort (numero di confronti proporzionale a N log2 N)

173

Potrebbero piacerti anche