Sei sulla pagina 1di 10

Laboratorio 4 – Funzioni e Input/Output

© 2009 - Questo testo (compresi i quesiti ed il loro svolgimento) è coperto da diritto d’autore. Non può essere sfruttato a fini com-
merciali o di pubblicazione editoriale. Non possono essere ricavati lavori derivati. Ogni abuso sarà punito a termine di legge dal
titolare del diritto. This text is licensed to the public under the Creative Commons Attribution-NonCommercial-NoDerivs2.5 License
(http://creativecommons.org/licenses/by-nc-nd/2.5/)

1 Definizione di funzioni tramite .m files


Come abbiamo visto nell’esercitazione 3, in Matlabr /Octave è possibile eseguire degli script
chiamando nel prompt dei comandi il nome di un .m file presente nella cartella di lavoro.
Ad esempio, se ho raccolto una serie di comandi nel file miofile.m, digitando nel prompt
l’istruzione

>> m i o f i l e
Matlabr /Octave eseguirà fedelmente tutti i comandi presenti nel file. Le caratteristiche degli
script sono quelle di non avere parametri in ingresso modificabili e di poter lavorare solo su
variabili globali presenti in memoria.
Una funzione Matlabr /Octave risponde esattamente a queste limitazioni, ossia è uno script
al quale è possibile passare parametri in ingresso ed ottenerne in uscita, inoltre le variabili
utilizzate durante l’esecuzione della funzione vengono automaticamente cancellate dalla me-
moria al termine della stessa. A differenza di uno script standard la sintassi di una funzione
richiede che la prima riga del file abbia la struttura

f u n c t i o n [ Output1 , Output2 , . . . ] = n o m e f u n z i o n e ( I n p u t 1 , I n p u t 2 , . . . )
dove le parentesi quadre possono essere omesse se l’output è uno solo. È buona norma inserire
alcune righe di commento (ovvero precedute dal carattere %) dopo la definizione, che possono
essere visualizzate tramite il comando

>> h e l p n o m e f u n z i o n e
Successivamente saranno inserite tutte le istruzioni necessarie all’esecuzione della funzione.
Il file dovrà essere salvato con l’estensione .m e dovrà essere visibile a Matlabr /Octave (ad
esempio nella cartella corrente).
Nel seguente file, che chiameremo det2.m, si implementa una funzione che calcola il determi-
nante di una matrice quadrata di dimensione 2:

f u n c t i o n [ d e t ]= d e t 2 (A)
%DET2 c a l c o l a i l d e t e r m i n a n t e d i una m a t r i c e q u a d r a t a d i o r d i n e 2
[ n ,m]= s i z e (A ) ;
i f n==m
i f n==2
d e t=A( 1 , 1 ) * A(2 ,2) −A( 2 , 1 ) * A ( 1 , 2 ) ;
else
e r r o r ( ' S o l o m a t r i c i 2 x2 ' ) ;
end

1
else
e r r o r ( ' Solo m a t r i c i quadrate ' ) ;
end
return
dove si utilizza la funzione error('...') per interrompere l’esecuzione del programma e
visualizzare una stringa di errore nel prompt dei comandi.

Esercizio 1 Modificare la funzione det2.m in modo che riesca a calcolare anche il deter-
minante di matrici quadrate di dimensione 3
Suggerimento: Usare la formula ricorsiva (la funzione chiama se stessa) per il determinante
di una matrice quadrata di ordine n, detta sviluppo di Laplace:

Scegliere una riga qualsiasi i :


Xn
det(M ) = (−1)i+j mij det(Mij )
j=1

dove Mij la matrice M decurtata dell’i-esima riga e della j-esima colonna, ovvero il minore
complementare dell’elemento mij .
Nota: Questo algoritmo è estremamente inefficiente, in quanto il numero di operazioni richie-
sto per il calcolo del determinante di una matrice quadrata di ordine n è dell’O(n!).

Funzioni più flessibili Una funzione Matlabr /Octave può modificare il suo comporta-
mento a seconda del numero di variabili in input o output che riceve: per questo scopo,
le funzioni nargin e nargout contano rispettivamente il numero di parametri di input
(output) che sono stati passati alla funzione. Ad esempio, la semplice funzione:

function c = test nargin (a , b)


i f ( n a r g i n == 1 )
c = a . ˆ 2;
e l s e i f ( n a r g i n == 2 )
c = a + b;
end
modifica il suo comportamento a seconda che le venga passato un solo parametro in input
(ne calcola il quadrato) o due (ne calcola la somma). Per differenziare il comportamento
della funzione è standard l’uso della struttura if(). . . elseif(). . . end. Analogamente, la
semplice funzione:

f u n c t i o n [ out1 , o u t 2 ] = t e s t n a r g o u t ( a , b )
i f ( n a r g o u t == 1 )
out1 = a * b ;
e l s e i f ( n a r g o u t == 2 )
out1 = a + b ;
out2 = a − b ;
end

2
differenzia il suo comportamento a seconda del numero di parametri di output che richiediamo,
infatti:

>> a = t e s t n a r g o u t ( 3 , 4 )
a =
12
>> [ b , c ] = t e s t n a r g o u t ( 3 , 4 )
b =
7
c =
−1

2 Misura del tempo di calcolo di un algoritmo


Esistono due misure di tempo di esecuzione degli algoritmi:
ˆ Elapsed time: è il tempo fisico che intercorre tra il momento in cui un programma è
stato lanciato e il suo completamento; ad esempio; in Matlabr /Octave le istruzioni:

tic
... operazioni ...
toc

permettono di visualizzare il tempo fisico in secondi intercorso tra il lancio del comando
tic e del comando toc.
In Matlabr /Octave il comando clock restituisce il vettore:

[ Year Month Day Hour Minute Second ] ,

che può essere utilizzato in combinazione con il comando etime con la seguente sintassi:

t0 = c l o c k ;
... operazioni ...
dt = etime ( clock , t0 ) ;

per ottenere l’elapsed time di un programma. Siccome clock è sincronizzato con l’o-
rologio di sistema (che è aggiornato periodicamente dal sistema operativo) si preferisce
usare tic. . . toc che fa partire un vero e proprio cronometro interno.

ˆ CPU time, o tempo macchina, è il tempo effettivo impiegato dal processore per calcolare,
e non tiene conto dei tempi di attesa necessari per acquisire i dati necessari ad iniziare
l’elaborazione né di quelli necessari per salvare i risultati ottenuti. Le istruzioni:

t0 = cputime ;
... operazioni ...

3
t1 = cputime ;
dt = t1 − t0 ;

permettono di calcolare il tempo macchina dt necessario ad eseguire le operazioni.

Esercizio 2 Si consideri la matrice quadrata A ∈ Rn × Rn ed il vettore v ∈ Rn : vogliamo


quantificare il costo computazionale dell’usuale algoritmo per eseguire il prodotto Av. Il
calcolo della j-esima componente del vettore prodotto, data da

aj1 v1 + aj2 v2 + · · · ajn vn

richiede n prodotti e n − 1 somme. In tutto abbiamo n componenti da calcolare, quindi


dovremo eseguire n(n + n − 1) = 2n2 − n operazioni. Dunque l’usuale algoritmo richiede
O(n2 ) operazioni ed ha pertanto complessità quadratica rispetto al parametro n.
In questo esercizio vogliamo verificare questo calcolo stimando la complessità dell’operazione
con il suo tempo di esecuzione (cpu). Costruiremo quindi una funzione

f u n c t i o n [ dim , t e m p i ] = e s 2 ( nmin , nmax , s t e p )


che restituisca il vettore dim contenente i valori delle dimensioni n utilizzate nel calcolo e il
vettore dei tempi cpu necessari al calcolo del prodotto matrice vettore. I valori di input sono
la dimensionalità minima nmin e massima nmax del calcolo e il passo di incremento sulla
dimensionalità step.
Impostare la funzione in modo tale che se vengono passati solo due valori (nmin, nmax) si
assuma step = 100.
Suggerimenti Per ottenere un grafico significativo e tempi di calcolo ragionevoli si consiglia
di usare i seguenti paramentri:

ˆ nmin = 100

ˆ nmax = 4000

ˆ step = 100

Si consiglia anche di preallocare una matrice A quadrata di dimensione nmax e un vettore


colonna v di dimensione nmax di elementi casuali e di utilizzare nei calcoli successivi delle
sottomatrici di A e sottovettori di v.
Il grafico ottenuto plottando i due vettori plot(dim,tempi,'o−−') è in Fig. 2.

4
0.025

0.02
CPU TIME

0.015
time [s]

0.01

0.005

0
0 500 1000 1500 2000 2500 3000 3500 4000
n

Figura 1: Risultato dell’Es.2

5
3 Gestione dell’Input/Output
3.1 Output su monitor
Come già accennato in precedenza, la funzione disp permette di visualizzare una stringa
di caratteri (o, più in generale, una matrice di stringhe), racchiusa tra apici. Ad esempio
potremmo scrivere:

>> d i s p ( ' C i a o a t u t t i ' )


Ciao a t u t t i
>> d i s p ( ' Oggi e ' ' m a r t e d i ' ' ' ) % d o p p i o a p i c e p e r v i s u a l i z z a r n e uno
Oggi e ' l u n e d i '
>> d i s p ( [ ' G e n n a i o ' ' F e b b r a i o ' ' Marzo ' ] ) % v e t t o r e r i g a d i s t r i n g h e
G e n n a i o F e b b r a i o Marzo
>> d i s p ( [ ' G e n n a i o ' ; ' F e b b r a i o ' ; ' Marzo ' ] )% v e t t o r e colonna di s t r i n g h e
Gennaio
Febbraio
Marzo
dove le componenti di un vettore colonna di stringhe devono avere la stessa dimensione (ad
esempio, aggiungendo degli spazi se necessario).
In molte circostanze si ha la necessità di rappresentare come stringa valori di varibili nume-
riche. La prima possibilità è quella di convertire una variabile numerica in stringa tramite la
funzione num2string:

>> x = 10;
>> s t r i n g a = [ ' La v a r i a b i l e x v a l e : ' , num2str ( x ) ] ;
>> disp ( stringa )
La v a r i a b i l e x v a l e : 10
mentre un modo molto più versatile (e consigliato) consiste nell’utilizzare le funzioni fprintf
e sprintf mutuate direttamente dal linguaggio C. La sintassi della prima è:

f p r i n t f ( ' Formato ' , V a r i a b i l i )


e scrive su video il valore delle Variabili indicate, utilizzando il Formato definito, che altro
non è che una stringa che contiene i caratteri che si vogliono visualizzare e, nelle posizioni in
cui si vuole venga inserito il valore delle Variabili, deve essere indicato uno dei formati di
visualizzazione preceduti dal carattere % . Analoga la sintassi della funzione sprintf:

s t r = s p r i n t f ( ' Formato ' , V a r i a b i l i )


dove in questo caso l’output viene indirizzato sulla stringa str. I formati di visualizzazione
sono riassunti nella tabella 1: Ad esempio:

>> x = 1 0 ; y = 5 . 5 ;
>> f p r i n t f ( ' x v a l e %d m en t re y v a l e %f \n ' , x , y ) ;

6
Codice di formato Azione
%s stringa
%d numero intero
%f numero in virgola fissa (es. 1234.5678)
%e numero notazione scientifica (es. 1.2345678e + 003)
%g numero in notazione compatta (sceglie tra %f e %e)
\n inserisce carattere ritorno a capo
\t inserisce carattere di tabulazione

Tabella 1: Formati

x v a l e 10 m en t re y v a l e 5 . 5 0 0 0 0 0
>> s t r = s p r i n t f ( ' I l p r o d o t t o d i x e y v a l e : %e \n ' , x * y ) ;
>> d i s p ( s t r )
I l p r o d o t t o d i x e y v a l e : 5 . 5 0 0 0 0 0 e +01
È utile sapere che (analogamente al linguaggio C) è possibile definire la larghezza di campo e
la precisione da utilizzare nella visualizzazione di un numero decimale, inoltre (a differenza
del linguaggio C) possibile utilizzare fprintfe sprintf in maniera marticiale. Ad esempio:

>> x = [ 0 : . 1 : 1 ] ;
>> y = [ x ; exp ( x ) ] ;
>> f p r i n t f ( ' %6 . 2 f %12 . 8 f \n ' , y ) ;
0 .00 1 .00000000
0 .10 1 .10517092
...
1 .00 2 .71828183
dove la cifra dopo il % indica il numero minimo di caratteri da utilizzare per la visualizzazione
del numero (riempiti con uno spazio se necessario) e la cifra dopo il . indica il numero di cifre
da utilizzare dopo il separatore decimale. Si noti come la variabile passata y sia una matrice
2 × 11, mentre la visualizzazione sia 11 × 2; infatti il comando legge la matrice per colonne,
applicando ad ogni elemento il corrispondente descrittore di formato. La visualizzazione
avviene naturalmente per righe e questo porta ad una sorta di trasposizione della matrice.

3.2 Output su file


Per scrivere su file un insieme di dati di output con un certo formato si utilizzano i comandi
fopen, fprintf e fclose:

f i d = fopen ( ' Stringa ' , ' w')


dove fid è una variabile che identifica il file e 'Stringa' definisce il nome del file, apre il
file in scrittura (parametro 'w').

f p r i n t f ( fid , ' Formato ' , V a r i a b i l i )


scrive nel file identificato da fid il valore delle variabili con il formato assegnato.

7
fclose ( fid )
chiude il file.

3.3 Input da tastiera


Il comando input permette di gestire l’input da tastiera. La sua sintassi è:

Var = i n p u t ( ' S t r i n g a d i c a r a t t e r i ' )


Dopo aver visualizzato a terminale la Stringa di caratteri, attende in ingresso da
tastiera un’espressione Matlabr /Octave da assegnare alla variabile Var. Il valore assegnato
potrà quindi essere di tipo scalare, vettore oppure matrice utilizzando la sintassi standard di
Matlabr /Octave.

3.4 Input da file


Per leggere da un file un insieme di dati si utilizzano i comandi fopen, fscanf e fclose:

f i d = fopen ( ' Stringa ' , ' r ')


dove fid è una variabile che identifica il file e 'Stringa' definisce il nome del file, apre il
file in lettura (parametro 'r').

var = fscanf ( fid , ' Formato ' , S i z e )


legge tutti i dati contenuti nel file identificato da fid, convertendoli in base al formato spe-
cificato e memorizzandoli nella variabile var. Size indica la dimensione della variabile var
e può essere scritto come:

nval legge nval numeri memorizzandoli in un vettore colonna


inf legge fino alla fine del file
[nrighe ncol] legge nrighe × ncol numeri memorizzandoli (per colonne) in
una matrice che ha tale dimensione. ncol può essere inf,
nrighe no.
Dato il metodo di lettura (per righe) e di scrittura (per colonne), per ottenere esattamente la
struttura presente nel file bisognerà trasporre la matrice. Infine

fclose ( fid )
chiude il file.
Ad esempio, supponendo di aver generato il file exp.txt con i valori dell’esponenziale
nell’intervallo [0, 1]

0 .00 1 .00000000
0 .10 1 .10517092
...
1 .00 2 .71828183

8
per leggerlo possiamo usare la sintassi:

f i d = fopen ( ' e x p . t x t ' , ' r ' ) ;


y = f s c a n f ( f i d , '%g %g ' , [ 2 i n f ] ) % Ora y ha 2 r i g h e
y = y ';
fclose ( fid )
Esercizio 3 Generare uno script che scriva su un file una semplice tabella contenente i
valori del seno e coseno in N punti equispaziati nell’intervallo [0, π]. Il numero N dei punti
deve essere inserito tramite input da tastiera. La chiamata dello script dovrà produrre un
output simile a:

>> e s 3
I n s e r i s c i i l numero d i v a l o r i : 11
>> t y p e e s 3 . o u t
==========================================
k x(k) cos ( x ( k )) sin (x(k ))
==========================================
1 0 .000 1 .00000000 0 .00000000
2 0 .314 0 .95105652 0 .30901699
...
11 3 .142 −1. 0 0 0 0 0 0 0 0 0 .00000000
dove il comando type nomefile permette di visualizzare il file creato senza bisogno di
utilizzare editor esterni.

3.5 Save/Load
Per salvare delle variabili e poterle eventualmente caricare in una sessione successiva, sono
disponibili i comandi Matlabr /Octave save e load, la cui sintassi è:

s a v e N o m e f i l e E l e n c o V a r i a b i l i Formato
l o a d N o m e f i l e Formato
Formato è un parametro opzionale. Se tale parametro è omesso il file viene salvato (letto) in
formato binario, altrimenti si può specificare il formato −ascii che scrive (legge) in formato
testo. Provare ad effettuare le seguenti istruzioni a titolo di esempio.

>> x = [ 0 : .1 : 1 ] ;
>> y = exp ( x ) ;
>> save t a b e l l a . d a t x y −a s c i i
>> type t a b e l l a . d a t

0 . 0 0 0 0 0 0 0 e +00 1 . 0 0 0 0 0 0 0 e −01 ... 1 . 0 0 0 0 0 0 0 e +00


1 . 0 0 0 0 0 0 0 e +00 1 . 1 0 5 1 7 0 9 e +00 ... 2 . 7 1 8 2 8 1 8 e +00

>> l o a d t a b e l l a . d a t

9
>> t a b e l l a

tabella =

0 0 .1000 ... 1 .0000


1 .0000 1 .1052 ... 2 .7183

4 Introduzione all’errore assoluto e relativo


Abbiamo già visto (Laboratorio 2) l’insorgere dei errori di arrotondamento dovuti al modo in
cui i numeri reali vengono memorizzati con un numero finito di bit; come vedremo esistono
numerosi altri tipi di errore che insorgono nell’ambito del calcolo numerico. Da un punto di
vista del tutto generale, la relazione tra la quantità approssimata x̂ ed il suo corrispondente
valore esatto x potrà essere misurata tramite l’errore assoluto

Ea = |x − x̂|

oppure tramite l’errore relativo


|x − x̂|
Er =
|x|
In particolare l’errore relativo è un buon indicatore della qualità dell’approssimazione in
quanto non è influenzato dall’ordine di grandezza delle quantità che si stanno misurando.
Esercizio 4 Dato un numero intero n, il fattoriale n! di tale numero, può essere appros-
simato tramite la funzione di Stirling
√  n n
S(n) = 2πn
e
Scrivere uno script che per n = 1, . . . , N con N richiesto da tastiera visualizzi una tabella
adeguatamente formattata con i valori di n! ottenuti con la usuale formula ricorsiva, la relativa
approssimazione S(n) e gli errori assoluti e relativi compiuti nell’approssimazione. Il risultato
della chiamata della funzione dovrà essere equivalente a:

>> e s 4
I n s e r i r e N : 12
>> t y p e e s 4 . o u t
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
n n! S(n) Ea Er
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
1 1 0 .92 7 . 7 9 e −02 7 . 7 9 e −02
2 2 1 .92 8 . 1 0 e −02 4 . 0 5 e −02
...
12 479001600 475687486 . 4 7 3 . 3 1 e +06 6 . 9 2 e −03

10