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 commerciali 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 Matlab R /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 Matlab R /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 Matlab R /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 memoria 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,... ] = nomefunzione ( Input1, Input2,... ) 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 nomefunzione 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 Matlab R /Octave (ad esempio nella cartella corrente). Nel seguente file, che chiameremo det2.m, si implementa una funzione che calcola il determinante di una matrice quadrata di dimensione 2: f u n c t i o n [ det ]= det2 (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 quadrata d i o r d i n e 2 [ n,m]= s i z e (A ) ; i f n==m i f n==2 det=a( 1, 1 ) *A(2,2) A( 2, 1 ) *A( 1, 2 ) ; e l s e e r r o r ( ' Solo m a t r i c i 2 x2 ' ) ; end 1
e l s e e r r o r ( ' Solo m a t r i c i q u a d r a t e ' ) ; end r e t u r n 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 determinante 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 : n det(m) = ( 1) i+j m ij det(m ij ) j=1 dove M ij la matrice M decurtata dell i-esima riga e della j-esima colonna, ovvero il minore complementare dell elemento m ij. Nota: Questo algoritmo è estremamente inefficiente, in quanto il numero di operazioni richiesto per il calcolo del determinante di una matrice quadrata di ordine n è dell O(n!). Funzioni più flessibili Una funzione Matlab R /Octave può modificare il suo comportamento 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: f u n c t i o n c = t e s t n a r g i n ( 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, out2 ] = t e s t n a r g o u t ( a, b ) i f ( nargout == 1) out1 = a * b ; e l s e i f ( nargout == 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 Matlab R /Octave le istruzioni: t i c... o p e r a z i o n i... toc permettono di visualizzare il tempo fisico in secondi intercorso tra il lancio del comando tic e del comando toc. In Matlab R /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 ;... o p e r a z i o n i... dt = etime ( c l o c k, t0 ) ; per ottenere l elapsed time di un programma. Siccome clock è sincronizzato con l orologio 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 ;... o p e r a z i o n i... 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 R n R n ed il vettore v R n : vogliamo quantificare il costo computazionale dell usuale algoritmo per eseguire il prodotto Av. Il calcolo della j-esima componente del vettore prodotto, data da a j1 v 1 + a j2 v 2 + a jn v n richiede n prodotti e n 1 somme. In tutto abbiamo n componenti da calcolare, quindi dovremo eseguire n(n + n 1) = 2n 2 n operazioni. Dunque l usuale algoritmo richiede O(n 2 ) 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, tempi ] = es2 ( 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 time [s] 0.015 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 ( ' Ciao 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 ' ' ' ) % doppio a p i c e per 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 ( [ ' Gennaio ' ' 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 Gennaio F e b b r a i o Marzo >> d i s p ( [ ' Gennaio ' ; ' F e b b r a i o ' ; ' Marzo ' ] )% v e t t o r e c olonna d i s t r i n g h e Gennaio F e b b r a i o 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 numeriche. La prima possibilità è quella di convertire una variabile numerica in stringa tramite la funzione num2string: >> x = 1 0 ; >> s t r i n g a = [ ' La v a r i a b i l e x v a l e : ', num2str ( x ) ] ; >> d i s p ( s t r i n g a ) 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 mentre 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 mentre y v a l e 5.500000 >> 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.500000e+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. 0 0 1.00000000 0. 1 0 1.10517092... 1. 0 0 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 ( ' S t r i n g a ', '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 ( f i d, ' 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
f c l o s e ( f i d ) 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 Matlab R /Octave da assegnare alla variabile Var. Il valore assegnato potrà quindi essere di tipo scalare, vettore oppure matrice utilizzando la sintassi standard di Matlab R /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 ( ' S t r i n g a ', ' r ' ) dove fid è una variabile che identifica il file e 'Stringa' definisce il nome del file, apre il file in lettura (parametro 'r'). v a r = f s c a n f ( f i d, ' Formato ', S i z e ) legge tutti i dati contenuti nel file identificato da fid, convertendoli in base al formato specificato e memorizzandoli nella variabile var. Size indica la dimensione della variabile var e può essere scritto come: nval inf [nrighe ncol] legge nval numeri memorizzandoli in un vettore colonna legge fino alla fine del file 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 f c l o s e ( f i d ) chiude il file. Ad esempio, supponendo di aver generato il file exp.txt con i valori dell esponenziale nell intervallo [0, 1] 0. 0 0 1.00000000 0. 1 0 1.10517092... 1. 0 0 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 ' ; f c l o s e ( f i d ) 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: >> es3 I n s e r i s c i i l numero d i v a l o r i : 11 >> type e s 3. o u t ========================================== k x ( k ) cos ( x ( k ) ) s i n ( x ( k ) ) ========================================== 1 0. 000 1.00000000 0.00000000 2 0. 314 0.95105652 0.30901699... 11 3.142 1.00000000 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 Matlab R /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 Nomefile 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 ) ; >> s a v e 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.0000000e+00 1.0000000e 01... 1.0000000e+00 1.0000000e+00 1.1051709e+00... 2.7182818e+00 >> l o a d t a b e l l a. d a t 9
>> t a b e l l a t a b e l l a = 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 oppure tramite l errore relativo E a = x ˆx E r = x ˆx 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 approssimato tramite la funzione di Stirling S(n) = ( n ) 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: >> es4 I n s e r i r e N : 12 >> type e s 4. o u t n n! S ( n ) Ea Er 1 1 0. 9 2 7.79e 02 7.79e 02 2 2 1. 9 2 8.10e 02 4.05e 02... 12 479001600 475687486. 4 7 3. 3 1 e +06 6.92e 03 10