Complessità degli algoritmi
Complessità degli
algoritmi
• L’efficienza di un algoritmo si valuta in base
all’utilizzo che l’algoritmo fa delle risorse del
calcolatore:
(Cap. 13)
CPU
Memoria
tempo
spazio
• Algoritmi diversi, pur occupando lo stesso
spazio, possono richiedere tempi diversi:
• due algoritmi richiedono l’utilizzo di un array, ma
uno richiede la scansione di tutto l’array, e impiega
un tempo t1 , l’altro richiede la scansione solo di
una parte dell’array, e impiega un tempo t2<t1.
Complessità degli algoritmi
• Si preferisce valutare l’efficienza in base al
tempo impiegato per risolvere il problema.
• Come si può misurare il tempo impiegato
dall’algoritmo? Con un cronometro?
• Ogni linguaggio ammette delle funzioni per
calcolare il tempo: in alcuni si accede ad un
“orologio” della macchina; spesso viene fornito
un tempo calcolato in secondi o millisecondi e
viene indicato il tempo trascorso a partire da
una data fissa: in questo caso per calcolare il
tempo si effettuano due successive chiamate
della funzione tempo e si fa la differenza.
Complessità degli algoritmi
• Se vogliamo confrontare algoritmi che
risolvono lo stesso problema misurando il
tempo che impiegano, dovremo:
• usare la stessa macchina
• usare lo stesso linguaggio
• usare gli stessi dati.
• Si potrà costruire un array di dati abbastanza
grande e tale vettore sarà l’unico ingresso per
confrontare algoritmi che risolvono quel
problema in prove eseguite sullo stesso
calcolatore.
Complessità degli algoritmi
• È una buona misurazione?
• Lo è solo in parte; non lo è in senso assoluto.
Infatti il tempo misurato in tale modo dipende:
• dal calcolatore usato
• dal linguaggio usato
• dai dati che sono elaborati in quel momento
• Non ha perciò senso dire che “l’algoritmo di
ricerca impiega 10 sec.”; il tempo dipende da
fattori estranei all’algoritmo.
Complessità degli algoritmi
• Come possiamo stimare il tempo per
l’algoritmo in modo che sia una misura
assoluta, vale a dire, dipenda solo
dall’algoritmo?
• Osserviamo che il tempo di esecuzione è una
funzione crescente della dimensione dei dati di
ingresso.
• Esempio.
Sommare n numeri con n=10, 1000, 100000000;
il tempo per calcolare la somma aumenta con n
n → +∞ ⇒
T(n) → +∞
1
Complessità degli algoritmi
Complessità degli algoritmi
• Non per tutti gli algoritmi è così:
nell’algoritmo che calcola il massimo tra due
numeri, in quello che scrive l’acronimo,
nell’algoritmo che esegue delle prove sul
troncamento della divisione tra interi, … i dati
di ingresso sono una quantità fissa e non
variano con la dimensione.
• Nel calcolo della somma e del massimo su n
dati, nell’algoritmo di ricerca, andiamo ad
acquisire il valore di n e gli n valori.
• Osserviamo che il numero dei dati di ingresso
fa variare il numero di operazioni:
n = 10 l’algoritmo esegue 10 somme
n = 10000 l’algoritmo esegue 10000 somme
• Si chiama complessità computazionale la
funzione F(n) che calcola il numero di
operazioni eseguite dall’algoritmo:
n ∈ dimensione dei dati di ingresso
F(n) numero di operazioni
n → +∞ ⇒
F(n) → +∞
Complessità degli algoritmi
Complessità degli algoritmi
• Ha interesse sapere come la funzione F(n)
“cresce” , vale a dire quale è il suo ordine di
infinito.
• Quali sono le operazioni che un algoritmo
esegue?
• Confrontiamo le due funzioni
y = x e y = x2
osservando i valori che assumono per x = 5, 10, 100
x
y=x
y = x2
5
5
25
10
10
100
100
100
10000
intuiamo che y = x2 tende a +∞ più rapidamente di
y = x; y=x2 ha un ordine di infinito maggiore di y=x.
•
•
•
•
•
operazioni elementari
assegnazioni (accesso e assegnamento)
confronti
lettura/scrittura
invocazione di metodi (passaggio parametri e
gruppi di istruzioni all’interno del metodo)
• strutture di controllo
Complessità degli algoritmi
Complessità degli algoritmi
• Per trovare il tempo totale richiesto
dall’algoritmo, dobbiamo sommare i tempi
delle varie istruzioni.
• Non è necessario, però, essere così precisi:
possiamo fare delle semplificazioni.
• Indicheremo con ta il tempo per effettuare una
assegnazione e con tc il tempo per effettuare
un
confronto
indipendentemente
dalla
semplicità o meno dell’espressione.
• Invece di considerare assegnazioni e confronti
si può calcolare il numero di accessi ad alcune
variabili fondamentali nell’algoritmo: ad
esempio nel confronto a[i] == b, c’è un
accesso all’elemento i-esimo dell’array e a b.
• Le operazioni fondamentali sono:
confronti e assegnazioni
2
Complessità degli algoritmi
• Assegnazione.
1) a = 25;
2) a = Math.sqrt(3*Math.sin(x));
• Queste due assegnazioni necessitano di due
tempi diversi e sicuramente sarà:
t1 < t2 perché l’espressione 2) richiede più
calcoli dell’espressione 1).
• Indichiamo pertanto con ta il tempo per una
assegnazione generica (indipendentemente
dall’espressione a destra del simbolo =).
Complessità degli algoritmi
• Struttura condizionale.
se P
allora
<a1>
altrimenti <a2>
//finese
dove
<a1> e <a2> rappresentano gruppi di istruzioni
• Il predicato P potrà essere:
1) a<b
2) (a<b) o (non S) e ((a==c) o (c!=h)
• Certamente si avrà t1 < t2 ma consideriamo tp il
tempo per valutare un predicato generico.
Complessità degli algoritmi
• Sequenza di assegnazioni.
<a1>
<a2>
….
<ak>
k costante
il numero di istruzioni
non dipende da n
• Sommiamo i tempi:
k ta ~ ta
k non dipende da n
• Se abbiamo due funzioni F1(n) F2(n) con lo stesso
ordine di infinito, può però essere importante stimare
anche il valore di k.
Complessità degli algoritmi
• Avremo
tp + ta1
tp + ta2
• Se <a1> e <a2> non dipendono da n (sequenza
di istruzioni)
tp + ta1
~ tp + ta2 ~ tp
• abbiamo un numero costante di operazioni,
come nell’algoritmo del max(a,b).
Complessità degli algoritmi
Complessità degli algoritmi
• Quali sono le strutture che variano con n?
• Quando eseguiamo una scansione di un array
(lettura, stampa, somma, ricerca, …)
consideriamo un numero n (n>0) di dati di
ingresso.
• Per fare la scansione di un array utilizziamo
una struttura iterativa.
• Struttura iterativa.
• Consideriamo un ciclo con incremento fisso e
passo 1:
per i da 1 a n eseguire
iterazione //indipendente da n
//fineper
• Indichiamo con tp il tempo per il predicato e
con ti il tempo per l'iterazione (per ora
indipendente da n):
3
Complessità degli algoritmi
• Avremo:
tp : eseguito n+1 volte
(n volte con P vero, 1 volta con P falso)
ti : eseguita n volte
⇒ (n+1)·tp + n · ti ~ n · c
c = costante: “costo” dell'iterazione:
(n+1)·tp + n·ti = n·tp + tp + n·ti ≤ n·tp + n·tp +
n·ti = 2n·tp + n·ti = n·( 2·tp + ti ) = n · c
infatti
n>0
quindi n ≥ 1
Complessità degli algoritmi
(n+1)·tp1 + ((n+1) · tp2 + n·ti ) · n =
= (n+1)·tp1 + ((n+1) · tp2)·n + n2·ti ≤
≤ 2n· tp1 + 2n·tp2·n + n2·ti ≤
≤ n2 ( 2 tp1+ 2 tp2 + ti) = n2 · c
(avendo considerato:
n+1 ≤ 2n e 2n ≤ 2n2 , essendo n naturale positivo)
c= costante : “costo” dell'iterazione
Complessità degli algoritmi
• Cosa cambia se anche l’iterazione dipende da
n?
• Supponiamo che l’iterazione sia un ciclo dello
stesso tipo, che viene eseguito n volte:
1. per i da 1 a n eseguire
2.
per k da 1 a n eseguire
iterazione
//fineper
//fineper
Complessità degli algoritmi
• Consideriamo delle funzioni di riferimento e
calcoliamo la complessità degli algoritmi
confrontandola con queste funzioni.
y = log x
y=x
y = x log x
y = x2
y=2x
y=ex
y = x2
Complessità degli algoritmi
e
y=2x
si incontrano per x=2 e x=4
Complessità degli algoritmi
• Notazione O (o-grande), Ω, Θ.
(par. 13.3)
• Ci interessa sapere come cresce F(n) al
crescere di n, sempre nell’ipotesi che
F(n) → +∞ quando n → +∞
• Sia f(n) la funzione di complessità che
cerchiamo. Si dice che
f(n) è O(g(n))
se ∃ c, n0 > 0 | ∀ n ≥ n0 f(n) ≤ c · g(n)
• Come ordine di infinito f “non cresce più di” g.
4
Complessità degli algoritmi
Complessità degli algoritmi
• Se f(n) = O(g(n)) significa che g(n) è una
limitazione superiore per f(n): il calcolo
esatto di f(n) è troppo complicato e ci
limitiamo a stimarne una limitazione superiore.
• Esempio.
n2 + n è O(n2)
infatti:
n2 + n ≤ n2 + n2 = 2· n2
• Sia f(n) la funzione di complessità che
cerchiamo. Si dice che
f(n) è Ω(g(n))
se ∃ c, n0 > 0 | ∀ n ≥ n0 c · g(n) ≤ f(n)
• Come ordine di infinito f “cresce almeno
quanto” g;
g(n) è una limitazione inferiore per f(n).
• Esempio.
n2 + n è Ω(n)
infatti:
2n = n + n ≤ n2 + n
Complessità degli algoritmi
• Sia f(n) la funzione di complessità che
cerchiamo. Si dice che
f(n) è Θ(g(n))
se ∃ c1, c2, n0 > 0 | ∀ n ≥ n0
c1 · g(n) ≤ f(n) ≤ c2 · g(n)
• Come ordine di infinito f “cresce quanto” g.
Esempio.
n2 , 1000 n2, 1/100 n2 sono tutte
Θ(n2)
varia solo la costante moltiplicativa.
Classi di complessità
• Quando scriviamo un algoritmo, vogliamo
stimare la sua complessità cercando di
individuare la funzione g(n) che approssima
f(n). In tale modo gli algoritmi sono suddivisi
in classi di complessità:
costanti
k
(non dipendono da n)
logaritmo
log n , log2 n
lineari
n
n log n
polinomiali
nk
k = 2, 3, …
esponenziali
n! , an , nn a ≠ 0,1
Casi di prova per stimare la
complessità
Casi di prova per stimare la
complessità
• Quando si scrive un algoritmo si deve sempre
dare una stima della limitazione superiore; è
opportuno dare una stima della limitazione
inferiore, sarebbe importante stimare un
comportamento medio tra le due stime
(difficile).
• O(g) limitazione superiore: sono richieste al
più g(n) operazioni
• Ω(g) limitazione inferiore: sono richieste
almeno g(n) operazioni.
• Casi di prova.
• Caso peggiore: i dati sui quali l’algoritmo
richiede il massimo numero di operazioni
• Caso favorevole: i dati sui quali l’algoritmo
richiede il minor numero di operazioni
• Caso medio: i dati che richiedono un numero
medio di operazioni.
• Nel calcolo della complessità non si considerano i
cicli per acquisire i dati: sono uguali per tutti gli
algoritmi e sono Θ(n).
5
Complessità dell’algoritmo di
ricerca lineare
• Caso I: elementi distinti.
i←0
trovato ← falso
mentre i ≠ n e non trovato eseguire
i ← i+1
se a[i] == b
allora trovato ← vero
//fine
//fine
se trovato
allora stampa “trovato al posto “ i
altrimenti stampa “ elemento non presente”
//fine
Complessità dell’algoritmo di
ricerca lineare
• Caso peggiore.
Si ha quando il ciclo viene eseguito fino in
fondo.
1) l’elemento non c’è:
predicato
i←i+1
• Caso favorevole.
Si ha quando a[1] = b: primo elemento
dell’array:
2ta + tp + ta + tc + ta + tp + tc + tstampa
inizio
V
i←i+1
a[i] == b trovato
F
se
risultato
la funzione non dipende da n, quindi è
costante:
Ω(1)
è il caso con il minimo numero di operazioni.
Complessità dell’algoritmo di
ricerca lineare
2) l’elemento c’è ed è l’ultimo:
a[n] = b
2ta + (n+1) tp + nta + ntc + ta + tc + tstampa
2ta + (n+1) tp + nta + ntc + tc + tstampa
inizio
Complessità dell’algoritmo di
ricerca lineare
a[i] == b
se
risultato
la funzione è del tipo c·n, quindi O(n).
Complessità dell’algoritmo di
ricerca lineare
• Caso II: elementi ripetuti.
k←0
per i da 1 a n eseguire
se a[i] == b
allora k ← k+1
posiz[k] ← i
//finescelta
//fineciclo
se k==0
allora stampa “elemento non presente”
altrimenti stampare i primi k valori dell’array posiz:
posiz[1], …, posiz[k]
inizio
predicato
i←i+1
a[i] == b trovato
se
risultato
la funzione è del tipo c·n, quindi O(n).
• Il massimo numero di operazioni è dato da una
funzione lineare.
Complessità dell’algoritmo di
ricerca lineare
• La struttura iterativa for (“per”) viene eseguita
sempre completamente.
• Caso peggiore. Gli elementi sono tutti uguali a
b: ogni volta si assegna un valore a posiz
ta + (n+1)tp + ntc + 2nta + tc + tstampa ⇒ O(n)
• Caso favorevole. L’elemento non c’è: il
numero di assegnazioni è 0
ta + (n+1)tp + ntc + tc + tstampa
⇒ Ω(n)
Pertanto la complessità è: Θ(n)
6
Complessità dell’algoritmo di
ricerca lineare
Complessità dell’algoritmo di
ricerca lineare
• Caso III: dati ordinati.
• Il primo confronto a[1] <= b e b <= a[n]
viene eseguito per verificare se è oppure no
possibile eseguire la ricerca: nel caso in cui
questo predicato fosse falso, non si esegue
l’algoritmo.
• Per l’algoritmo di ricerca lineare la
complessità è:
Ω(1) nel caso favorevole (primo elemento) ed
è O(n) nel caso peggiore (ultimo o mancante).
• Esercizio.
Dimostrare l’affermazione precedente.
• Questo non viene considerato come caso
favorevole, perché di fatto l’algoritmo non
inizia nemmeno.
• Per la ricerca binaria si ha:
Complessità dell’algoritmo di
ricerca binaria
Complessità dell’algoritmo di
ricerca binaria
• Caso favorevole.
• L’elemento viene trovato con un solo
confronto; ma non è il primo elemento, è
quello centrale: b = a[(1+n)/2]
• Caso peggiore.
• Si esegue l’iterazione il massimo numero di
volte quando: l’elemento o è l’ultimo
“guardato” oppure non è presente.
• Quante operazioni ci sono nel ciclo?
ta + tc + tc + ta
numero costante
3ta + tp + ta + tc + ta + tp + tc + tstampe
a[im]==b
trovato
numero costante di operazioni:
se
Ω(1)
a[im]==b
a[im]>b
is o id
• Quante iterazioni? Facciamo una stima del
numero di iterazioni eseguite valutando quanti
elementi restano ad ogni divisione dell’array
(gli elementi sono in numero finito).
Complessità dell’algoritmo di
ricerca binaria
Complessità dell’algoritmo di
ricerca binaria
• Supponiamo che n = 2k
1a iterazione
restano 2k-1 elementi
2a iterazione
restano 2k-2 elementi
3a iterazione
restano 2k-3 elementi
…..
k-esima iterazione restano 2k-k = 1 elementi: is=id
(k+1)-esima iterazione: se l’elemento è presente trovato
diventa vero, altrimenti trovato è falso e is>id: il ciclo
termina:
O(k) con k = log2 n : O(log2 n)
• Si può dimostrare anche per valori di n
qualunque, poiché si ha
2k-1≤ n ≤ 2k
e che la complessità del caso peggiore è
O(log2 n)
7
Casi di prova
Casi di prova
Casi di prova
• Lo schema del programma sarà perciò del tipo:
//acquisire e stampare l'array
//acquisire la quantità dei casi di prova
// numprove
for(int k = 1; k <= numprove; k++){
//acquisire l'elemento b da cercare
//chiamare il metodo di ricerca
//stampare i risultati
System.out.println("prova numero "
+ k + " elemento cercato = " + b);
}
• Quando si vuole testare un algoritmo si devono
costruire vari casi di prova. Invece di eseguire
il programma più volte si può costruire un file
di dati contenente tutti i casi che si vogliono
provare.
• Esempio. Supponiamo di voler provare un
algoritmo di ricerca: si acquisiscono i valori di
un array (unico) e i casi di prova saranno
costituiti da diversi valori di b (b esterno
all’array, presente, presente più volte, ecc.)
Casi di prova
• Nel file dei dati si avrà pertanto la seguente
organizzazione sequenziale:
n e gli n elementi per l’array
num (numero di prove )
num elementi da cercare
• Il programma viene eseguito una sola volta e
produce i vari risultati dell’esecuzione
dell’algoritmo con i diversi valori da cercare.
Argomenti sulla riga di comando
Argomenti sulla riga di
comando
• Quando si esegue un programma Java, è
possibile fornire dei parametri dopo il nome
della classe che contiene il metodo main.
• Tali parametri vengono letti dall’interprete
Java e trasformati in un array di stringhe che
costituisce il parametro del metodo main.
• Supponiamo di inserire due valori per il
programma di nome Prova
java Prova mio 50
8
Argomenti sulla riga di comando
Argomenti sulla riga di comando
public class Prova {
public static void main
(String[] arg) {
System.out.println(arg.length);
System.out.println(arg[0]);
System.out.println(arg[1]);
. . .
}
}
• La lunghezza dell’array è 2: il primo argomento è la
stringa “mio”, il secondo argomento il valore 50.
• Questi valori inseriti possono essere dei dati di
ingresso da elaborare, spesso si inserisce il
nome di uno o più file che si vogliono
collegare al programma oltre ai file standard
System.in e System.out.
• Il parametro del main è un array di tipo String,
perciò se dobbiamo considerare valori
numerici dobbiamo poi trasformarli.
Argomenti sulla riga di comando
• Finora non abbiamo inserito argomenti: cosa
veniva associato all’array di stringhe arg?
• Se non vengono forniti parametri sulla riga di
comando, al metodo main viene passato un
array di lunghezza zero.
Scelte multiple:
switch
• Si poteva pensare che arg = null; invece il
parametro fornito al metodo main non ha mai il
valore null.
• Esercizio. Pensare ad un modo per verificare
quanto detto.
Scelte multiple: switch
• L’enunciato switch si usa al posto di una
situazione in cui ci siano scelte del tipo “else
if“ annidate.
(Argomenti avanzati 5.2)
• Esempio.
int x, y;
if (x == 1)
y = 1;
else if (x == 2)
y = 4;
else if (x == 4)
y = 16;
Scelte multiple: switch
• Sintassi.
//k deve essere intero
//oppure carattere
//acquisire o assegnare un valore a k
switch(k){
case costante1: istruzione;
case costante2: istruzione;
case costante3: istruzione;
}
int k;
9
Scelte multiple: switch
Scelte multiple: switch
• Semantica.
• Il valore di k viene cercato tra i valori presenti
nella case e se viene trovato si esegue
l’istruzione indicata e tutte le successive.
• Se si vuole che le istruzioni siano in
alternativa, come avviene con l’annidamento
delle if, si deve utilizzare la parola chiave
break.
switch(k){
case costante1: istruzione; break;
case costante2: istruzione; break;
case costante3: istruzione;
}
Scelte multiple: switch
Scelte multiple: switch
switch(k){
case costante1: istruzione; break;
case costante2: istruzione; break;
case costante3: istruzione; break;
default: System.out.println("il "+
"valore di k non e' presente");
}
• Se non si usa break, l’ordine delle case fa variare le
operazioni da eseguire.
• Se il valore di k non è presente, non vi è
alcuna segnalazione.
• Per gestire meglio il caso “valore di k non
presente”, si può introdurre un “default”.
• Esempio.
switch (x){
case 1: y = 1;
case 2: y = 4;
case 4: y = 16;
}
//se x = 1 y = 16
switch (x){
case 4: y = 16;
case 2: y = 4;
case 1: y = 1;
}
//se x = 1 y = 1
Scelte multiple: switch
• Questo enunciato è utile quando si devono
scegliere metodi diversi da eseguire in
alternativa, come un menu che propone delle
scelte: la lettura del menu è sicuramente più
chiara che non l’elenco delle varie “else if”.
Ciclo do
• Se la variabile non è intera (o carattere) o se i
valori non sono delle costanti non è possibile
utilizzare l’enunciato switch.
10
Ciclo do
Ciclo do
• Capita a volte di dover eseguire il corpo di un
ciclo almeno una volta, per poi ripeterne
l’esecuzione se è verificata una particolare
condizione.
(Argomenti avanzati 6.1)
• Sintassi.
• Esempio: leggiamo un valore in ingresso e
vogliamo che esso sia positivo: se il valore non
è positivo vogliamo poter ripetere la lettura.
• Semantica.
L’iterazione viene eseguita la prima volta;
viene valutata la condizione: se è vera si
esegue nuovamente l’iterazione, se è falsa il
ciclo termina.
Ciclo do
Ciclo do
• Equivale a scrivere:
iterazione;
while(condizione){
iterazione;
}
• oppure
boolean continua =true;
while(continua){
iterazione;
if(!condizione)
continua=false;
}
do {
//iterazione
}
while(condizione);
• Problema. Calcolare la somma
1 +1./2 + 1./3 + 1./4 + … + 1./n + …
fino a quando 1./n > t con t valore stabilito
(ad esempio t = 10-9).
• Esercizio. Scrivere l’algoritmo usando sia il
ciclo while che il ciclo do.
Il problema dell’ordinamento
Il problema
dell’ordinamento
• Dati n elementi sui quali si possa considerare
un relazione d’ordine totale (ogni coppia di
elementi è confrontabile) costruire la
permutazione ordinata, vale a dire trovare una
disposizione degli elementi in modo tale che si
abbia:
a1 < a2 < a3 < …. < an
• Analogamente possiamo considerare
elementi ordinati in ordine decrescente.
gli
11
Il problema dell’ordinamento
• Vedremo metodi diversi e ne calcoleremo la
complessità.
• I metodi di ordinamento saranno:
- ordinamento scansione (o selezione diretta)
- bubblesort
- ordinamento per inserimento
- ordinamenti ricorsivi
- quicksort
- mergesort
• La complessità varia da O(n2) a O(n··log2 n)
Ordinamento per scansione
mentre ci sono elementi da ordinare eseguire
mettere all’i-esimo posto il minimo
degli elementi {ai, ai+1, …., an}
//finementre
• Per trovare il minimo abbiamo due strategie:
• I. considerare gli elementi
ai e ak con k = i+1, …, n
e scambiarli se non sono nell’ordine
Ordinamento per scansione
• Non possiamo fare subito l’assegnazione x=y
altrimenti x e y avrebbero lo stesso valore.
Dobbiamo utilizzare una variabile di supporto,
s dello stesso tipo di x e y, per depositare
temporaneamente uno dei due valori:
1) s=x;
2) x=y;
3) y=s;
x 7 5
s
• Uno scambio si fa con 3 assegnazioni
y 5 7
7
Ordinamento per scansione
(selezione)
• Analisi.
(par. 13.1)
• Se vogliamo mettere in ordine gli elementi
osserviamo che: a1 è il minimo tra gli elementi
{a1, a2, … an}, a2 è il minimo tra gli elementi
{a2, a3, … an}, ecc. , an-1 è il minimo tra gli
elementi {an-1, an}, e an è al suo posto.
• Progetto.
• Avremo una struttura iterativa per “mettere a
posto” a1, a2,…, an-1 cercando il minimo.
Ordinamento per scansione
• II. calcolare il valore e la posizione del minimo ed
eseguire un solo scambio tra ai e il minimo trovato.
• Come si effettua lo scambio?
• Abbiamo due variabili x e y e vogliamo scambiare il
loro contenuto:
prima
dopo
7
5
x
y
5
x
7
y
Ordinamento per scansione
//ordinamento per scansione con scambio
per i da 1 a n-1 eseguire
//mettere in ai il minimo tra ai , ai+1 , …, an
per k da i+1 a n eseguire
se a[i] > a[k]
allora //scambiare ai con ak
s ← a[i]
a[i] ← a[k]
a[k] ← s
//finese
//fineper
//fineper
12
Ordinamento per scansione
• Calcoliamo la complessità dell’algoritmo per
scansione.
• L’operazione fondamentale eseguita nella
struttura iterativa è il confronto: questo viene
eseguito sempre, mentre lo scambio viene
eseguito solo nel caso vero.
• Quanti confronti vengono eseguiti?
• Il ciclo esterno varia da 1 a n-1, il ciclo interno
varia da i a n: i non è fisso e quindi il calcolo
esatto è un po’ più laborioso.
Ordinamento per scansione
n° iterazione
elementi
n° confronti
i=1
(a1, a2) (a1, a3) … (a1, an)
n-1
i=2
(a2, a3) … (a2, an)
n-2
……
…….
i=n-1
(an-1, an)
1
Sommiamo i confronti:
1 + 2 + 3 + …. + n-2 + n-1= n·(n-1)/2
Ordinamento per scansione
Ordinamento per scansione
• Un modo facile per ricordare la somma (intero:
n o n-1 è pari) è:
• Caso favorevole. Si fanno tutti i confronti e
nessuno scambio; dati: vettore ordinato:
n(n-1)/2 confronti + 0 assegnazioni
• Quindi Ω(n2/2)
(il primo + l’ultimo) × (metà del numero di elementi)
si può dimostrare la formula per induzione.
• Caso peggiore. Si fanno tutti i confronti e tutti
gli scambi; dati: vettore ordinato in ordine
inverso:
n(n-1)/2 confronti + 3n(n-1)/2 assegnazioni
Quindi
O(n2 / 2).
Ordinamento per scansione
//ordinamento per scansione con minimo
per i da 1 a n-1 eseguire
//mettere in ai il minimo tra ai , ai+1 , …, an
imin ← i
per k da i+1 a n eseguire
se a[imin] > a[k]
allora imin ← k
//finese
//fineper
s ← a[i]
//scambiare ai con aimin
a[i] ← a[imin]
a[imin] ← s
//fineper
• Si ha pertanto:
• O(n2 /2) come limitazione superiore
• Ω(n2/2) come limitazione inferiore
Θ(n2 / 2)
(anche Θ(n2) )
Ordinamento per scansione
• Caso peggiore.
confronti: n (n-1) / 2
assegnazioni: sono (al più)
4(n-1) (ciclo esterno) + n(n-1)/2 (ciclo interno)
• Caso favorevole.
confronti: n (n-1) / 2
assegnazioni: 4(n-1) (ciclo esterno)
13
Ordinamento per scansione
• Confrontiamo le due versioni:
• il numero di confronti è sempre: n(n-1) / 2
• varia il numero di assegnazioni:
• nel caso favorevole si ha:
0 (scambio)
4(n-1) (minimo)
• nel caso peggiore si ha:
3(n-1)n /2 (scambio) (n-1)n/2 + 4(n-1) (minimo)
• Verificare che per n>4
3(n-1)n /2 > (n-1)n / 2 + 4(n-1)
Ordinamento bubblesort
• Si eseguono confronti tra elementi successivi:
(a1, a2) (a2, a3) (a3, a4) … (an-1, an)
e se gli elementi non sono nell’ordine allora si
scambiano. Con questa tecnica la componente
con valore più grande va in fondo (al posto di
an). Si ricomincia dell’inizio e si “mette a
posto” an-1, poi an-2, … , infine a2.
• Si può anche partire dal fondo iniziando il
confronto dalla coppia (an-1, an) e andare
indietro: in tale modo la prima componente che
si “mette a posto” è a1 (in testa il minimo).
Ordinamento bubblesort
• Progetto.
per i da 1 a n-1 eseguire
per k da 1 a n-i eseguire
se a[k] > a[k+1]
allora //scambiare ak con ak+1
s ← a[k]
a[k] ← a[k+1]
a[k+1] ← s
//finese
//fineper
//fineper
Ordinamento bubblesort
• Quante assegnazioni?
• Caso favorevole.
0 assegnazioni (già ordinato)
• Caso peggiore.
3·n·(n-1)/2 (ordine inverso)
• Pertanto anche il bubblesort è Θ(n2 / 2).
Ordinamento bubblesort
• Quanti confronti vengono effettuati?
n° iterazione
i=1
i=2
i=n-1
elementi
n° confronti
(a1, a2) (a2, a3) …
(an-1, an)
n-1
(a1, a2) … (an-2, an-1)
n-2
……
…….
(a1, a2)
1
Sommiamo i confronti: sempre
1 + 2 + 3 + …. + n-2 + n-1= n·(n-1)/2 O(n2 /2)
Confronto tra i vari metodi di
ordinamento
• Consideriamo il seguente array e vediamo
come si muovono gli elementi (alla prima
iterazione).
a = ( 40, 20, 1, 7, 10, 0) n=6
1)lineare con scambio
40 20 1 7 10 0
scambio
40, 20
20 40 1 7 10 0
scambio
20, 1
1 40 20 7 10 0
scambio
1, 0
0 40 20 7 10 1
14
Confronto tra i vari metodi di
ordinamento
2) lineare con minimo
40 20 1 7 10 0
imin
0
1 2
3 6
scambio
40, 0
20 1 7 10 40
Confronto tra i vari metodi di
ordinamento
23) bubblesort
40 20 1 7
20 40 1 7
20 1 40 7
20 1 7 40
20 1 7 10
20 1 7 10
10
10
10
10
40
0
0
0
0
0
0
40
scambio
40, 20
scambio
40, 1
scambio
40, 7
scambio
40, 10
scambio
40, 0
Confronto tra i vari metodi di
ordinamento
Confronto tra i vari metodi di
ordinamento
• Se nel bubblesort si mette il minimo in testa si
ottiene:
0 40 20 1 7 10
• Esercizio1. Costruire una variante di
bubblesort che tenga conto che se non si fanno
scambi l’array è ordinato.
• Osserviamo che, dal momento che nel
bubblesort si confronta ak con ak+1, se non ci
sono scambi allora significa che l’array è
ordinato.
• Si può sfruttare questa proprietà per scrivere
una versione più efficiente.
Confronto tra i vari metodi di
ordinamento
• Esercizio2.
• Costruire una variante che tenga conto della
posizione dell’ultimo scambio.
• Esempio.
a = ( 3, 1, 2, 5, 7, 8, 9, 12, 34, 35) n=10
dalla quarta posizione in poi non si fanno scambi.
Suggerimento. Pensare agli estremi dell’indice come
“inizio” e “fine” della parte di array da esaminare;
memorizzare in una variabile “sc” la posizione
dell’ultimo scambio (fine cambierà il suo valore tramite
sc). Il ciclo terminerà con inizio = fine
Suggerimento. Utilizzare una variabile logica
“ordinato” da inizializzare a falso e da inserire
nel predicato del ciclo esterno (se non ci sono
scambi ordinato deve essere vero).
Confronto tra i vari metodi di
ordinamento
• Per entrambe queste varianti la complessità nel
caso favorevole è inferiore a quella del caso
senza la variante: infatti, se si fornisce un array
già ordinato, dopo la prima iterazione il ciclo
termina: ordinato=vero, oppure inizio=fine.
• Pertanto il caso favorevole è Ω(n).
• Anche nell’ordinamento per scansione è
possibile eseguire tali varianti? No.
15
Esercizio
• Si calcoli la complessità dell’algoritmo
seguente, considerando i casi a>b, a=b, a<b;
specificando il numero di assegnazioni e di
confronti nei tre casi.
• Si supponga n>0.
• Quale sarebbe il valore di a e b se si
stampassero a e b alla fine della struttura
iterativa “mentre”?
Esercizio
Algoritmo
definizione variabili
a, b, n, i, k
intero
continua logico
acquisire valori per a, b, n
continua ← vero
i←0
mentre i ≠ n e continua eseguire
i ← i+1
se a < b
allora a ← a-1
Esercizio
altrimenti se a == b
allora
per k da 1 a n eseguire
a ← a+1
b ← b+1
//fineper
altrimenti continua ← falso
//finese
//finese
//finementre
Soluzione esercizio
Soluzione esercizio
Soluzione esercizio
• Caso n = 0 .
i = 0, quindi il predicato “mentre i ≠ 0 …” è
falso: il ciclo non viene eseguito.
• Caso n > 0 .
• Caso a < b .
Viene eseguita l’istruzione a ← a-1, quindi è
ancora a < b; il ciclo termina quando i = n; il
numero di operazioni è:
2ta + (n+1) tp + nta + ntc + nta ⇒ c·n
• Caso n < 0 .
se a < b il ciclo non termina: a diminuisce e
quindi rimane minore di b
se a = b il ciclo non termina: entrambi
crescono di 1
se a > b il ciclo termina: continua diventa
falso.
continua←V
i←0
i←i+1
a<b
a←a-1
b non varia, a diventa a-n
16
Soluzione esercizio
• Caso a > b .
Viene eseguita l’istruzione continua ← falso,
quindi il ciclo termina; il numero di operazioni
è:
2ta + tp + ta + tc + tc + ta + tp ⇒ costante
continua←V
i←i+1 a<b
a=b
continua← F
i← 0
b non varia, a non varia
Soluzione esercizio
• Caso a = b .
Vengono eseguite le istruzioni a←a+1 e b←b+1; il
ciclo interno viene eseguito n volte e il ciclo esterno
termina quando i = n; il numero di operazioni è:
2ta + (n+1) tp + nta + ntc + n tc + n(n+1) tp+
continua←V
i←i+1
a<b
a=b
i←0
+ 2 n2ta
⇒ c·n2
a← a+1
b← b+1
a diventa a + n2 , b diventa b + n2
Rappresentazione
dell’informazione numerica
• Esistono vari tipi di numerazione.
Rappresentazione
dell’informazione
numerica
• Primitiva: sequenza di simboli tutti uguali;
numerazione nei dadi, carte da gioco, …
Rappresentazione
dell’informazione numerica
Rappresentazione
dell’informazione numerica
• Posizionale: le cifre che compongono la
sequenza di simboli che rappresentano il
numero hanno un valore che varia con la
posizione, si esegue poi la somma; è la
numerazione attuale.
• Nella rappresentazione posizionale il numero
viene scritto con una notazione del tipo
seguente:
N = cn cn-1 … c0 . c-1 c-2 … c-m
• Additiva: ogni simbolo ha un valore fisso; si
esegue una somma dei valori per determinare
il numero; numerazione dei romani, greci,
egiziani,…
• Il valore di ogni cifra è:
cifra × bp
dove b è la base del sistema di numerazione
(con b ≠ 0,1) e p è la posizione della cifra.
• Esempio.
3
300
0.03
unità
centinaia
centesimi
17
Rappresentazione
dell’informazione numerica
• Il valore del numero viene dalla somma:
N = cn bn + cn-1 bn-1 + … + c0 b0 +
+ c-1 b-1 + …. + c-m b-m
Rappresentazione
dell’informazione numerica
• Il più piccolo numero naturale rappresentabile
con n cifre ha tutte le cifre uguali a 0:
0 bn-1 + 0 bn-2 + … + 0 b0 = 0
• con ck = 0, 1, 2, …, b-1
• N si ottiene quindi sommando i valori ck bk.
• Formula di rappresentazione dei numeri
(per i numeri con infinite cifre dopo la virgola,
si ha una serie).
(Appendice H)
• Il più grande numero naturale rappresentabile
con n cifre ha tutte le cifre uguali a b-1:
Rappresentazione
dell’informazione numerica
Rappresentazione
dell’informazione numerica
Si possono considerare varie basi.
b=10
sistema decimale ck = 0, 1, 2,.., 9
b=2
sistema binario ck = 0, 1
b=8
sistema ottale
ck = 0, 1, 2,.., 7
b=16
sistema esadecimale
ck = 0, 1, 2,.., 9, A, B, C, D, E, F
• b=60
sistema sessagesimale: si usa per
misurare il tempo, non si usano 60 simboli
diversi, ma simboli sono scritti in decimale con
due cifre: 00, 01, 02, … 57, 58, 59.
•
•
•
•
•
Rappresentazione
dell’informazione numerica
b=2
b=10
9
(10)
(11)
(12)
(13)
(14)
(15)
b=8
b=16
9
A
B
C
D
E
F
4bit
1001
1010
1011
1100
1101
1110
1111
• Per scrivere in base 2 i numeri della base 16 = 24
occorrono 4 bit.
(b-1) bn-1 + (b-1) bn-2 + … + (b-1) b0 = bn-1
• Cifre delle basi 2, 10, 8 , 16
b=2
b=10
b=8
b=16
0
0
0
0
1
1
1
1
2
2
2
3
3
3
4
4
4
5
5
5
6
6
6
7
7
7
8
8
4 bit
0000
0001
0010
0011
0100
0101
0110
0111
1000
Cambi di base
• Passaggio da base b a base 10.
• Il numero è scritto con le cifre della base b: si
rappresentano le cifre nella base 10 e si fanno i
calcoli in base 10.
• Esempio. b=2
a) 1102 = 1×
×22 + 1×
×21 + 0×
×20 = 4 + 2 = 610
1
0
b) 112 = 1×
×2 + 1×
×2 = 2 + 1 = 310
c) 11002 = 1×
×23 + 1×
×22 + 0×
×21 + 0×
×20 =
= 8 + 4 = 1210
18
Cambi di base
• Le regole per la base 10 valgono anche nelle
altre basi:
- spostare la virgola a destra o aggiungere uno
zero destra vuole dire “moltiplicare” per la
base: b) → a) e a) → c)
- spostare la virgola a sinistra o togliere uno
zero sinistra vuole dire “dividere” per la base:
a) → b) e c) → a) .
Cambi di base
• Esempio. b=8
20.48 = 2 ×81 + 0 ×80 + 4 ×8-1 =
= 16 + 0 + 4/8 = 16.510
• Esempio. b=16
AF.C16 = 10 ×161 + 15 ×160 + 12 ×16-1 =
= 160 + 15 + 12/16 =
= 175 + 3/4 = 175.7510
Cambi di base
• Esempio. b=7
1527 = 1 ×72 + 5 ×71 + 2 ×70
= 49 + 35+ 2 = 8610
257 = 2 ×71 + 5 ×70 = 14 + 5 = 1910
• Esempio. b=5
4445 = 4 ×52 + 4 ×51 + 4 ×50 =
= 4 ×25 + 20 + 4 = 100 + 24 = 12410
Cambi di base
• Esempio. b=16
30B.A16 = 3 ×162 + 0×
×161 + 11×
×160 + 10 ×16-1
= 3 ×256 + 0 + 11 + 10/16 =
= 768 + 11 + 0.625 =
= 779.62510
• Regola. Si scrivono le cifre del numero nella
base 10 (che per b<10 coincidono con quelle
della base 10) e anche la base b (in base 10) e
si fanno i calcoli in base 10.
Cambi di base
Cambi di base
• Passaggio da base 10 a base b.
• Se sapessimo fare con disinvoltura i conti nella
base b potremmo scrivere le cifre in base b ed
eseguire i conti in base b in maniera analoga.
• Per poter fare sempre i conti in base 10
usiamo la seguente regola: consideriamo il
numero suddiviso nella sua parte intera e parte
frazionaria
PI . PF = cn cn-1 … c0 . c-1 c-2 … c-m
• PI : si divide PI e i successivi quozienti per la
base e si prendono i resti
• PF : si moltiplica PF e le successive parti
frazionarie per la base si prendono le parti
intere.
19
Cambi di base
Cambi di base
• Sia N10 = c3 c2 c1 c0 . c-1 c-2 c-3 in base b;
noi volgiamo trovare le cifre ck.
PI = c3 b3 + c2 b2 + c1 b1 + c0 b0 =
= b (c3 b2 + c2 b1 + c1 b0 ) + c0
(b0 =1)
dividendo per b si ottiene:
c3 b2 + c2 b1 + c1 quoziente e
c0 è il resto
Si prosegue con il quoziente trovato:
c3 b2 + c2 b1 + c1 = b (c3 b1 + c2 ) + c1
dividendo per b
c1 è il resto
c3 b + c2 = b (c3) + c2
c2 è il resto
c3 = b·0 + c3
c3 è il resto
(quoziente = 0 perché c3 < b)
• Abbiamo estratto nell’ordine c0, c1, c2, c3 che
andranno scritti:
c3 c2 c1 c0 .
Cambi di base
PF = c-1 b-1 + c-2 b-2 + c-3 b-3 = (finita)
= b-1 (c-1 + c-2 b-1 + c-3 b-2)
moltiplicando per b si ottiene:
b·b-1 (c-1 + c-2 b-1 + c-3 b-2) =
= c-1 + c-2 b-1 + c-3 b-2
c-1 è la parte intera
c-2 b-1 + c-3 b-2 la parte frazionaria
Cambi di base
Si prosegue con la parte frazionaria trovata:
c-2 b-1 + c-3 b-2 = b-1 (c-2 + c-3 b-1)
c-2 è la parte intera
moltiplicando per b
b· c-3 b-1 = c-3
c-3 è la parte intera
• Abbiamo estratto nell’ordine c-1, c-2, c-3 che
andranno scritti:
. c-1 c-2 c-3
Cambi di base
• Esempio.
22710 = ? 2
Cambi di base
• Esempio.
111000112
13610 = ? 2
resti
227 2
113
56
28
14
7
3
1
0
1
1
0
0
0
1
1
1
100010002
resti
Verifica:
27 + 26 + 25 + 21 + 20 =
= 128 + 64 + 32 + 2 + 1 =
= 227
136 2
68
34
17
8
4
2
1
0
0
0
0
1
0
0
0
1
Verifica:
27 + 23 =
= 128 + 8 = 136
20
Cambi di base
• Esempio.
0.7510 = ? 2
0.112
0.75 × 2
parte intera
1.5 : 0.5 × 2
1
1.0
1
Verifica:
1 /2 + 1 /4 = 3 / 4 =
= 0.75
la parte frazionaria è 0
Cambi di base
• Esempio.
0.410 = ? 2
0. 01102
0.4 × 2
parte intera
0.8 : 0.8 × 2
0
Verifica:
1 / 4 + 1 /8 + 1 / 64 + … =
1.6 : 0.6 × 2
1
=?
1.2 : 0.2 × 2
1
il numero è approssimato
0.4 : 0.4 × 2
0
0.8 ….
le parti frazionarie si ripetono: c’è un
periodo: 0110
Cambi di base
• Esempio.
0.110 = ? 2
0.1 × 2
parte intera
0.2 : 0.2 × 2
0
0.4 : 0.4 × 2
0
0.8 : 0.8 × 2
0
1.6 : 0.6 × 2
1
1.2 : 0.2 × 2
1
0.4 …. periodo è 0011
0. 0 00112
0.1 è periodico
in base 2
Numeri frazionari
periodici
Numeri frazionari periodici
Numeri frazionari periodici
• Abbiamo visto che 0.110 = 0.0 00112
• Consideriamo un numero razionale:
p/q con p e q naturali e primi fra loro e q≠0
Eseguendo la divisione
p:q=…
si può ottenere una rappresentazione finita
oppure infinita e periodica della parte
frazionaria.
a) Se i fattori primi di q sono solo quelli della
base, allora la rappresentazione è finita.
b) Se tra i fattori primi di q ci sono fattori
diversi dai fattori della base, allora la
rappresentazione è infinita e periodica.
• Vediamo per la base b = 10 = 2··5
• Caso a) q = 2k · 5h ; sia n = max(h,k);
allora si può scrivere
p
m
=
q
10n
con m naturale
m = cs cs-1 …. c1 c0
21
Numeri frazionari periodici
Numeri frazionari periodici
• Spostando la virgola di n posti verso sinistra a
partire da c0 si trova la rappresentazione finita,
perché finito è il numero di cifre di m.
• Esempio.
• Caso b) q = ak · bh con a e b anche diversi
da 2 e 5; ne segue che non si potrà scrivere q
come potenza di 10.
Quando si esegue la divisione:
p = q k1 + k2
0 ≤ k2 < q
se le cifre di p sono terminate si “abbassa lo 0”
e si prosegue con la divisione. Il resto k2,
variando in un intervallo limitato, ad un certo
punto si ripete: si ha pertanto il periodo.
31 / 20 = 31 / (22 · 5 ) = (31 · 5)/ (22 · 52 ) =
= 155 / 102 = 1.55
7 / 25 = 7 / 52 = (7 · 22)/ (22 · 52 ) =
= 28 / 102 = 0.28
Numeri frazionari periodici
• Esempio.
10 / 3
5/7
10 : 3 = 3. 3 3 3 …..
10
1
1 ….. periodo
5 : 7 = 0.714285 714……
50
10
30
20
60
40
5 ….. periodo
Passaggi
2 2n
• Un numero periodico in base 10 (ci sono
fattori diversi da 2 e 5) è anche periodico in
base 2.
• Un numero non periodico in base 10 può
essere periodico in base 2 (ad esempio 0.1,
0.4).
• I numeri irrazionali, i reali che non si possono
scrivere come quoziente di due interi, hanno
una rappresentazione infinita e non periodica.
Passaggi
• 2n →2 : si espandono le cifre della base 2n in
gruppi di n bit 16 = 24 → 2
• 2 →2n : si raggruppano le cifre della base 2 n
a n 2 = 2 → 24
• Esempio.
A91.2 16 = ? 2 = ? 8
A 9 1 . 2
1010 1001 0001 . 0010
5 2
2 1 . 1
Numeri frazionari periodici
base 16
2 2n
• Esempio.
2E0.FC 16 = ? 2 = ? 8
2
E
0 . F
C
0010 1110 0000 . 1111 1100
1 3
4 0 . 7 7
base 16
base 2
base 8
base 2
base 8
22
Operazioni in base 2
Operazioni in base 2
• Addizione.
0+0=0
1+0=0+1= 1
1 + 1 = 1 (riporto) 0
Esempio.
1 5+
5
21 0
Operazioni in base 2
• Sottrazione.
0-0=0
1-0=1
0 - 1 = 1 (prestito)0 - 1 = 1
Esempio.
1 1 1 1+
1 0 1
1 0 11 10 10
Operazioni in base 2
• Moltiplicazione.
0×0=0
1×0=0×1=0
1 × 1 =1
con 1 si ricopia il numero
con 0 spostamento a sinistra, poi si somma
Esempio.
21 0 5
1 5
11 01 11 01 0 0 1 0 1
0 1 1 1 1
Operazioni in base 2
• Divisione.
se il divisore ci sta, si mette 1; altrimenti 0
Esempio.
15 : 3 = 5
1111 : 11= 101
11
011
11
0
3 × 5 = 15
1 1×
×
101
11
11-1111
Esercizio
• Risolvere l’equazione
231x = 4510
vale a dire: determinare la base x in modo da
soddisfare l’uguaglianza.
• Soluzione. Utilizzando la formula di
rappresentazione dei numeri abbiamo:
2 ⋅ x2 + 3 ⋅ x1 + 1 = 45
2x2 + 3x - 44 = 0
x1 = -11/2 x2 =4
Pertanto la soluzione è: x = 4.
Verifica: 2 ⋅ 16 + 3 ⋅ 4 + 1 = 32 + 12 + 1 = 45
23
Esercizio
Esercizio
• Un matematico incontra un extraterrestre e gli
pone il problema di trovare le soluzioni della
seguente equazione:
x2 - 13x + 36 = 0
• Soluzione.
• La base 10 corrisponde al numero delle nostre
dita. Per l’extraterrestre i numeri 1, 13, 36 sono
nella base b che corrisponde al numero delle
sue dita.
Poiché la risposta è 5 e 6, che sono anche
numeri della base 10, ricordando
x2 – s x + p = 0
s = 11 e p = 30
si ha:
13b = 1110 36b = 3010
e pertanto
b=8.
L’extraterrestre dice che le soluzioni sono: 5 e
6.
Quante dita ha l’extraterrestre?
Rappresentazione degli interi
Rappresentazione
degli interi
• Si hanno i seguenti tipi di dato byte, short, int,
long corrispondenti ad un’occupazione di
memoria rispettivamente di n =8, 16, 32, 64 bit
segno
numero
0
per lo zero e i positivi
1
per i negativi
segno
Rappresentazione degli interi
• L’intervallo rappresentato è
[ -2n-1 , 2n-1-1]
• Rappresentazione di x ≥ 0
• Indichiamo con r(x) la rappresentazione
binaria di x.
• x=0
r(x) = 0
(tutti i bit 0)
0 00000…00000
Rappresentazione degli interi
• x>0
quindi 0 < x ≤ 2n-1 -1
r(x) si ottiene dividendo x per 2 e prendendo i
resti:
r(x) = ck ck-1… c1 c0
2n-1 -1
…...
2
1
0
0 1111 …… 1111
……
0 0000 …… 0010
0 0000 …… 0001
0 0000 …… 0000
0 00…ck ck-1 …c1 c0
se le cifre sono meno di
n-1 si aggiungono degli 0
a sinistra
24
Rappresentazione degli interi
Rappresentazione degli interi
• Rappresentazione dell’intervallo dei numeri
negativi.
• Si rappresenta l’opposto di x > 0: -x < 0
Si definisce r(-x) in modo tale che sia:
r(x) + r(-x) = 2n = 1 000…000 2
n
Si dice che l’opposto di x è rappresentato in
“complemento a 2”.
• Vediamo un esempio con n=3 bit:
[ -23-1, 23-1-1] =
3
0 11
= [ -22 , 22 -1]
• Ci sono state anche altre scelte (complemento a 1); oggi è la
rappresentazione degli interi in uso nella maggior parte dei
calcolatori.
2
1
0
-1
-2
-3
-4
0
0
0
1
1
1
1
10
01
00
11
10
01
00
r(-1) = 2n – r(1) = 1000 – 001 =
= 111
r(-2) = 2n – r(2) = 1000 – 010 =
= 110
Rappresentazione degli interi
Rappresentazione degli interi
• Regola.
• La regola si può esprimere in due modi diversi:
si calcola r(x) e poi
• Spiegazione.
• Si sottrae una sequenza di n bit da 2n=100…00
(n+1 bit).
• Finché si sottrae 0 da 0 il bit è 0 (quindi è
uguale); quando si sottrae il primo 1 da 0,
“scatta” il prestito 10-1=1 (il bit è uguale); con
il prestito (che si propaga verso sinistra) il bit 0
diventa 1 (infatti, per poter fare 10 il bit 1
viene sottratto dal bit 0 subito a sinistra) e
nella sottrazione da 1 il bit si inverte.
• si invertono tutti i bit e si aggiunge 1
• si invertono tutti i bit tranne gli zero a
destra e il primo 1 a destra.
• Il bit del segno è trattato come gli altri bit.
Rappresentazione degli interi
• Questa regola è facile da realizzare.
• In tale modo si fanno solo somme:
- somma
- differenza: si somma l’opposto;
invece di a-b si fa r(a) + r(-b)
- moltiplicazione: si sommano le righe
- divisione: si fanno differenze e quindi
somme
Rappresentazione degli interi
• Modularità degli interi e overflow.
• Cosa accade se si esce dall’intervallo di
rappresentazione? Il “trabocco” (overflow) è
un errore logico non segnalato: il numero
viene calcolato e si ripropone un valore
dell’intervallo, come se l'intervallo si ripetesse.
• Esempio. 2 + 2 = 4 (fuori dall’intervallo, n=3)
010 + 010 = 100 = r(-4)
25
Rappresentazione degli interi
Rappresentazione degli interi
• Quando si somma un numero con il suo
opposto, la somma è una sequenza di bit tutti
nulli e il bit 1 che “trabocca” dagli n bit si
“trascura”:
ad esempio con n=8
r(1) + r(-1) = 00000001 + 11111111 = 2n =
= 100000000
il bit 1 si trascura nella somma (2n è 1 seguito
da otto 0).
È una conseguenza della definizione di r(-x)
nel complemento a due.
• Quando, invece, sommiamo 1 a 127 (2n-1-1),
otteniamo:
127 + 1 = 128
ma 128 non appartiene all’intervallo.
• Abbiamo:
r(127) + r(1) = 01111111 + 00000001 =
= 10000000 = r(-128)
r(-128) è 1 seguito da sette 0; la somma di due
positivi è un negativo !!!! overflow.
Rappresentazione degli interi
Rappresentazione degli interi
• Aritmetica modulo m.
• Siano a ed m due numeri naturali, indichiamo
con
a mod m
il resto della divisione a/m
• Esempio.
Consideriamo i numeri 0, 4, 5, 9, 10, 15, 24,
30, 50, 55, ... abbiamo:
Sia m = 10 abbiamo:
per a < m
a mod m = a
per a ≥ m
ritroviamo come resti i
valori compresi tra 0 e 9
• 0/10
ha quoziente 0 e resto 0
e i numeri 10, 30, 50 hanno resto 0 (quozienti
1, 3, 5 rispettivamente)
• 5/10
ha quoziente 0 e resto 5
e i numeri 15, 55 hanno resto 5 (quozienti 1 e
5)
Rappresentazione degli interi
Rappresentazione degli interi
• Possiamo quindi raggruppare tutti i numeri
che hanno lo stesso resto quando si esegue la
divisione per m: l’insieme di questi numeri si
chiama classe di equivalenza modulo m.
• Se vogliamo rappresentare sempre i numeri
con una sola cifra possiamo utilizzare le classi
di equivalenza:
4 + 8 = 12 12 mod m = 2 (m=10)
• Quando sommiamo due numeri rappresentati
con una sola cifra la loro somma può essere
ancora rappresentata da una sola cifra (es.
2+4=6) oppure da due cifre (es. 4+8=12).
4 + 8 = 2 in una aritmetica modulo m
• Graficamente “trascuriamo” la cifra
significativa che “trabocca” a sinistra.
più
26
Rappresentazione degli interi
Rappresentazione degli interi
• Si può dimostrare che:
• nell’addizione di due numeri in complemento a
due si ha una situazione di overflow se e solo
se:
• si ha un riporto tra la colonna (n-1)-esima e la
colonna n-esima e non si ha un riporto tra la
colonna n-esima e la colonna (n+1)-esima
(esempio: r(127) + r(1))
• non si ha un riporto tra la colonna (n-1)-esima
e la colonna n-esima e si ha un riporto tra la
colonna n-esima e la colonna (n+1)-esima
• Il verificarsi di un overflow deriva dal fatto che
si è “usciti” dall’intervallo di rappresentazione:
[minint, maxint].
• Non c’è segnalazione di errore, perché viene
costruito un “numero valido” ossia un numero
della rappresentazione:
…..
minint
è come se l’intervallo
maxint
si ripetesse. Si
….
….
parla di modularità
minint
della rappresentazione
maxint
……
degli interi.
Rappresentazione degli interi
• Nelle operazioni tra numeri interi la divisione
per 0 è un errore.
• Se si effettua una divisione
a / b con a e b variabili intere e b=0
si genera un’eccezione che interrompe
l’esecuzione del programma:
ArithmeticException
Esponente intero dei
reali
Esponente intero dei reali
Esponente intero dei reali
• I reali sono rappresentati con mantissa ed
esponente. Tale esponente è intero.
• La rappresentazione per l’esponente intero
segue una regola diversa: l’esponente e è
rappresentato in traslazione (con eccesso):
• L'esponente
traslato
r(e)
appartiene
all'intervallo [0, 2n -1] dove n è il numero di bit
della sua rappresentazione.
• Viene perciò memorizzato r(e) invece di e
eccesso
2n-1
pari
2n-1 -1
dispari
k =
e
r(e) = e + k
27
Esponente intero dei reali
• Negli anni ci sono state varie scelte per la
rappresentazione dei reali. Per i reali in semplice
precisione:
• Vediamo un esempio con n=3 bit:
max
min
1
1
1
1
0
0
0
0
1
1
0
0
1
1
0
0
1
0
1
0
1
0
1
0
Esponente intero dei reali
[min, max] corrispondono a
[-4, 3]
k = 2n-1
[-3, 4]
k = 2n-1-1 dispari
pari
• base 16, esponente in 7 bit, eccesso pari 64 (sui
grossi calcolatori); con l’eccesso pari vale ancora la
regola che r(x) + r(-x) = 2n.
• base 2, esponente in 8 bit, eccesso dispari.
• Lo standard IEEE (Institute for Electrical and
Electronics Engeneering) nel 1985 ha scelto per i
Personal Computer (ora in uso nella maggior
parte dei calcolatori): base 2, eccesso dispari.
Esponente intero dei reali
• Nel caso dei float (32 bit) l’eccesso k = 27 -1 =
127.
• L’intervallo rappresentato è [0, 255] che
corrisponde all’intervallo (vero, non traslato)
[-127, 128].
255
11111111
128
• In binario:
254
…
2
1
0
11111110
127
….
….
-125
00000001
-126
00000000
-127
28