Laboratorio 2 : Introduzione a MATLAB - II
Contents
Laboratorio 2 : Introduzione a MATLAB - II#
Array e Matrici#
Abbiamo discusso fino ad ora l’utilizzo di variabili scalari, tuttavia in MATLAB i dati sono rappresentati in maniera naturale come matrici, array e, più in generale, tensori. È possibile applicare tutte le operazioni dell’algebra lineare agli array, in più si possono creare griglie comuni, combinare array esistenti, manipolare la forma e il contenuto di un array e utilizzare diverse modalità di indicizzazione per accedere ai loro elementi.
In realtà, abbiamo già surrettiziamente lavorato con degli array, poiché in MATLAB anche le variabili scalari non sono nient’altro che array unidimensionali, in notazione matematica un \(a \in \mathbb{R}\) è sempre un \(a \in \mathbb{R}^{1\times 1}\). Infatti se scriviamo nella command window:
a = 100;
whos a
il sistema ci restituirà
Name Size Bytes Class Attributes
a 1x1 8 double
Immaginiamo ora di disporre di uno specifico insieme di dati, che vogliamo disporre
in una matrice. Possiamo farlo utilizzando la semantica delle parentesi quadre [ ]
.
Una singola riga di dati contiene spazi o virgole ,
tra gli elementi e fa uso di
un punto e virgola ;
per separare le righe.
Ad esempio, se vogliamo crea una singola riga di tre elementi numerici
v = [ 1 2 3]
oppure
v = [ 1, 2, 3]
La dimensione della matrice risultante è 1 per 3, poiché ha una riga e tre colonne.
Una matrice di questa forma viene definita vettore riga e se chiamiamo la
funzione isrow(v)
ci vediamo rispondere
ans =
logical
1
Similmente, possiamo costuire un vettore colonna come
w = [ 1; 2; 3]
per cui abbiamo che invece iscolumn(w)
ci restituirà
ans =
logical
1
Più in generale, possiamo costruire una matrice di \(4 \times 4\) elementi come
A = [ 1 2 3 4; 5 6 7 8; 9 10 11 12; 13 14 15 16]
oppure come
A = [ 1, 2, 3, 4; 5, 6, 7, 8; 9, 10, 11, 12; 13, 14, 15, 16]
o
A = [ 1, 2, 3, 4
5, 6, 7, 8
9, 10, 11, 12
13, 14, 15, 16]
In tutti i casi ci vedremo restituire
A =
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16
Possiamo indagare le dimensioni di una matrice mediante la funzione size(A)
,
che nel caso precedente ci restituirà
ans =
4 4
Costruttori predefiniti#
Costruire matrici semplicemente inserendo i valori all’interno in sequenza o in maniera esplicita è chiaramente scomodo (e molto noioso). A questo scopo MATLAB possiede i costruttori riportati in Tabella 2.
Funzione |
Operazione |
---|---|
|
Crea un array di tutti zeri Per costruire la matrice \(0 = (O)_{i,j}\) \(i,j=1,\ldots,n\) scriviamo O = zeros(n,m);
|
|
Crea un array di tutti uno Per costruire la matrice \(1 = (E)_{i,j}\) \(i,j=1,\ldots,n\) scriviamo E = ones(n,m);
|
|
Crea un array di numeri casuali uniformemente distribuiti in \([0,1]\). R = rand(n,m);
Possiamo generare numeri uniformemente distribuiti nell’intervallo \([a,b]\) come R = a + (b-a).*rand(n,m);
|
|
Matrice identità costruisce la matrice identità \((I)_{i,i} = 1\), \(i=1,\ldots,n\)
e \(0\) altrimenti |
|
Crea una matrice diagonale o estrae gli elementi diagonali di una matrice data. Se |
Altre funzioni dello stesso tipo sono true
, false
, blkdiag
e i loro
funzionamento può essere esplorato mediante la funzione help
.
Il secondo insieme di funzioni estremamente utile per costruire matrici è quello
che si occupa di gestire le concatenazioni. Queste possono essere ottenute sia
utilizzando la notazione con le parentesi quadre [ ]
, ad esempio,
A = rand(5,5);
B = rand(5,10);
C = [A,B];
costruisce a partire dalle matrici \(A \in \mathbb{R}^{5 \times 5}\), \(B \in \mathbb{R}^{5\times 10}\), la matrice \(C \in \mathbb{R}^{5\times 15}\) ottenuta ponendo una accanto alle altre tutte le colonne di \(A\) seguite da quelle di \(B\). Alla stesso modo, si può ottenere la concatenazione verticale come
A = rand(8,8);
B = rand(12,8);
C = [A;B];
che genererà la matrice \(C \in \mathbb{R}^{20,8}\) ottenuta impilando tutte le righe di \(A\) seguite dalle righe di \(B\).
Pericolo
Le operazioni di concatenazione devono essere fatte tra matrici di dimensione compatibile, non possono esserci «avanzi» tra le dimensioni in oggetto. Per provare ad ottenere un errore si esegua:
A = rand(5,5);
B = rand(5,10);
C = [A;B];
che restituirà
Error using vertcat
Dimensions of arrays being concatenated are not consistent.
Al posto della notazione con le parentesi quadre è possibile fare uso delle
funzioni horzcat
e vertcat
che corrispondono, rispettivamente, alle
concatenazioni della forma [,]
e [;]
.
Se vogliamo costruire invece una matrice diagonale a blocchi a partire dai blocchi
diagonali possiamo utilizzare la funzione blkdiag
il cui help
ci restituisce
esattamente
blkdiag Block diagonal concatenation of matrix input arguments.
|A 0 .. 0|
Y = blkdiag(A,B,...) produces |0 B .. 0|
|0 0 .. |
Class support for inputs:
float: double, single
integer: uint8, int8, uint16, int16, uint32, int32, uint64, int64
char, logical
Slicing: accedere agli elementi#
Il modo più comune di accedere ad un determinato elemento di un array o di una matrice è quello di specificare esplicitamente gli indici degli elementi. Ad esempio, per accedere a un singolo elemento di una matrice, specificare il numero di riga seguito dal numero di colonna dell’elemento:
A = [ 1 2 3 4
17 8 2 1];
A(2,1)
che stamperà nella command window 17
, cioè l’elemento in posizione riga
2 e colonna 1 di A
(\(a_{2,1}\)).
Possiamo anche fare riferimento a più elementi alla volta specificando i loro
indici in un vettore. Ad esempio, accediamo al primo e al quarto elemento
della seconda riga della A
precedente, facendo
A(2,[1,4])
che restituirà
ans =
17 1
Per accedere agli elementi in un intervallo di righe o colonne, è possibile
utilizzare l’operatore due punti :
. Ad esempio, possiamo accedere accedi
agli elementi dalla prima alla quinta riga e dalla seconda alla sesta colonna di
una matrice \(A\) come
A = rand(10,10);
A(1:5,2:6)
Nel caso si voglia scorrere fino al termine di una dimensione è possibile
sostituire il valore di destra nei :
con la parola chiave end
, ad esempio:
A(1:5,2:end)
Invece l’utilizzo di :
senza valori di inizio/fine estrae tutte le entrate della
dimensione relativa, ad esempio, 5 colonna, A(:,5)
, oppure colonne dalla quarta
alla settima, A(:,4:7)
.
Nota
In generale, si può utilizzare l’indicizzazione per accedere agli elementi di qualsiasi array in MATLAB indipendentemente dal tipo di dati o dalle dimensioni.
L’ultimo modo di fare uno slicing di un vettore di cui vogliamo parlare è
quello mediante vettori logici. L’utilizzo di indicatori logici true
e
false
è particolarmente efficace quando si lavora con istruzioni condizionali.
Ad esempio, supponiamo di voler sapere se gli elementi di una matrice \(A\)
sono maggiori degli elementi corrispondenti di un’altra matrice \(B\).
L’operatore di confronto applicato alle due matrici restituisce un array
logico i cui elementi sono 1 quando un elemento in \(A\) soddisfa il confronto con il
corrispondente elemento in \(B\).
A = [1 2 6; 4 3 6];
B = [0 3 7; 3 7 5];
ind = A>B
A(ind)
B(ind)
ci restituisce
ind =
2×3 logical array
1 0 0
1 0 1
ans =
1
4
6
ans =
0
3
5
Gli operatori di confronto non sono gli unici per cui è possibile compiere questa
operazione, MATLAB stesso implementa un certo numero di funzioni utili a questo
scopo, si vedano isnan
, isfinite
, isinf
, ismissing
. È inoltre possibile
combinare insieme diverse richieste mediante l’uso degli operatori logici,
si veda più avanti la sezione su Selezione e Strutture Condizionali.
Operazioni tra array e matrici#
MATLAB supporta tutte le operazioni che hanno senso in termini di algebra lineare, dunque prodotti matrici-vettore, somme di matrici e di vettori, trasposizione, coniugio, etc., con i medesimi vincoli di consistenza tra gli operatori. Specificamente, il prodotto \(A \mathbf{v}\) con \(A \in \mathbb{R}^{n \times k_1}\) e \(\mathbf{v} \in \mathbb{R}^{k_2}\) è possibile se e solo se \(k_1 \equiv k_2\), consideriamo ad esempio
A = [1 2 3 4;
4 3 2 1;
2 4 3 1;
3 2 4 1];
v1 = [1,2,3,4];
v2 = [1;2;3;4];
e proviamo a calcolare
A*v1
da cui otteniamo un errore:
Error using *
Incorrect dimensions for matrix multiplication. Check that the number of columns in the first matrix matches the number of rows in the second matrix. To perform elementwise multiplication, use '.*'.
poiché stiamo cercando di fare un’operazione che non ha senso dal punto di vista dell’algebra lineare.
A*v2
è invece l’operazione corretta e otteniamo
ans =
30
20
23
23
v1*A
anche questa operazione ha senso dal punto di vista matriciale e infatti otteniamo
ans =
27 28 32 13
v2*A
ha il medesimo problema della prima, cioè abbiamo di nuovo delle dimensioni che non sono consistenti
Error using *
Incorrect dimensions for matrix multiplication. Check that the number of columns in the first matrix matches the number of rows in the second matrix. To perform elementwise multiplication, use '.*'.
A*v1'
introduciamo qui l’operazione di trasposta-coniugata (che poiché stiamo utilizzando vettori di numeri reali coincide con la semplice operazione di trasposizione.'
), che ci porta ad avere le dimensioni corrette e ci restituisce
ans =
30
20
23
23
A*v2'
la trasposizione in questo caso rende le dimensioni div2
incompatibili per cui otteniamo di nuovo il medesimo errore
Error using *
Incorrect dimensions for matrix multiplication. Check that the number of columns in the first matrix matches the number of rows in the second matrix. To perform elementwise multiplication, use '.*'.
Avvertimento
Il messaggio di errore che abbiamo incontrato ci dice che l’operazione non ha senso
in termine delle usuali operazioni dell’algebra lineare, tuttavia ci suggerisce
che quello che potevamo avere intenzione di fare era un prodotto elementwise
ovvero «elemento ad elemento». Questa operazione si ottiene utilizzando l’operatore
.*
, cioè con un .
prefisso davanti all’operatore di prodotto che in genere
significa proprio «esegui in maniera elemento-ad-elemento». Proviamo con la prima
operazione sbagliata che abbiamo proposto:
A.*v1
che ci restituisce
ans =
1 4 9 16
4 6 6 4
2 8 9 4
3 4 12 4
Che operazione abbiamo eseguito? Si provi ad eseguire anche le operazioni di elevamento a potenza
A^3
A.^3
v1^3
v1.^3
e si descriva il risultato.
Le ultime due operazioni che ci sono rimaste da descrivere sono +
/-
. Di nuovo
dobbiamo preoccuparci della compatibilità delle operazioni che vogliamo compiere.
Se vogliamo essere sicuri di star eseguendo la somma tra array e matrici che abbiamo
in mente dobbiamo assicurarci che le dimensioni siano compatibili.
Supponiamo di avere i vettori riga e colonna
v = [1 2 3 4]
w = [1
2
3
4]
se vogliamo sommarli o sottrarli e ottenere un vettore rispettivamente riga o colonna dobbiamo fare in modo che abbiano ambedue la dimensione corretta. Ovvero, dobbiamo avere, rispettivamente,
v + w'
oppure
v' + w
Se inavvertitamente sommiamo i due vettori come
v + w
otteniamo invece
ans =
2 3 4 5
3 4 5 6
4 5 6 7
5 6 7 8
che è invece una matrice! Non esattamente quello che ci aspettavamo (riuscite a capire che operazione abbiamo ottenuto?). Uguale cautela va esercitata nello scrivere operazioni di somma/sottrazioni tra matrici e vettori. Si provi ad eseguire:
A = pascal(4);
v = [1 3 2 4];
A + v
A + v'
A + 0.5
v + 1
È legittimo domandarsi a questo punto perché mai queste operazioni vengano eseguite senza restituire errori. Anche se dal punto di vista della pura algebra lineare queste operazioni non sono di immediata sensatezza, dal punto di vista implementativo permettono di semplificare (e velocizzare) un certo numero di operazioni che altrimenti richiederebbero diverse righe di codice (la cui ottimizzazione per le performance non è scontata) per essere implementate.
Selezione e Strutture Condizionali#
Funzione di MATLAB |
Descrizione |
---|---|
|
Esegue comando se la condizione |
|
Esegue uno o più gruppi di comandi a seconda che la variabile |
|
Esegue i comandi nel gruppo |
Consideriamo il seguente esempio che simula il lancio di una moneta.
a = rand();
if a < 0.5
disp('Testa!')
else
disp('Croce')
end
Ad ogni nuova esecuzione rand()
genera un numero casuale in \([0,1]\) con
probabilità uniforme. Il comando if
controlla se il numero casuale generato
è \(< 0.5\) ed in questo caso entra nel primo gruppo di codice. Altrimenti, abbiamo
ottenuto un numero \(> 0.5\) e rientriamo nel secondo gruppo di codice. Possiamo
decidere di simulare anche un dado a tre facce nel seguente modo
a = rand();
if a < 1/3
disp('1')
elseif a >= 1/3 & a < 2/3
disp('2')
else
disp('3')
end
in cui abbiamo utilizzato il comando elseif
per avere un ramo aggiuntivo.
Nota
All’interno dei nostri controlli di tipo if
(e più in generale) possiamo combinare
insieme il risultato di diverse operazioni logiche. Queste sono raccolte nella
Tabella Tabella 4. Altre funzioni che agiscono sui vettori di logici
e che potete esplorare richiamando la funzione help
sono any
e all
.
Funzione di MATLAB |
Descrizione |
---|---|
|
AND Logico |
|
NOT Logico |
|
OR Logico |
|
OR Esclusivo Logico |
Vediamo anche un esempio dell’istruzione di tipo switch
.
controllo = input("Inserisci un numero intero tra 0 e 3: ");
switch controllo
case 0
A = pascal(4,4);
disp(A);
case 1
A = ones(4,4);
disp(A);
case 2
A = eye(4,4);
disp(A);
case 3
A = rand(4,4);
disp(A);
otherwise
disp("Non so cosa fare con questo input!")
end
Si sarebbe potuto implementare lo stesso codice con una serie di if
e elseif
ed un else
, ma questo approccio è più rapido se non c’è la necessità di
imporre molti controlli logici.
L’ultimo controllo che vogliamo testare è try
. Per cui potete provare il seguente
codice.
try
A = rand(5,5);
b = ones(1,5);
A*b
catch
disp("C'è qualche problema con le dimensioni!");
end
Poiché l’operazione algebrica che abbiamo richiesto non è ben posta (provate ad
eseguirla al di fuori dell’operazione di try
) il try
cattura l’errore e
invece di arrestare l’esecuzione esegue il codice nella clausola catch
. Potete
correggere il codice nel primo blocco e verificare che in tal caso non entrerete
nel catch
.
Cicli e Cicli Innestati#
All’interno di qualsiasi programma, è possibile definire sezioni di codice che si ripetono in un ciclo.
Funzione di MATLAB |
Descrizione |
---|---|
|
ciclo |
|
ciclo |
|
Termina in maniera forzata l’esecuzione di un ciclo |
|
Passa il controllo all’iterata successiva di un ciclo |
|
Mette temporaneamente in pausa l’esecuzione di MATLAB. |
Utilizziamo un ciclo for
per calcolare la somma dei primi n
numeri interi.
n = input("Inserisci un numero intero n: ");
somma = 0;
for i=1:n
somma = somma + i;
end
fprintf("La somma degli interi da 1 a %d è %d.\n",n,somma);
Questo non è ovviamente il modo migliore di fare questa operazione, avremmo
ad esempio potuto calcolare la stessa quantità come sum(1:10)
. Oppure, sfruttare
un po” di idee matematiche per ricordarci che
\(\displaystyle S_n = \sum_{i=1}^{n} i = \frac{n(n+1)}{2}.\) Ma è servito al nostro
scopo dimostrativo.
Vediamo ora un esempio di ciclo while
.
somma = 0;
while somma < 10
somma = somma + rand();
end
fprintf("Il valore finale della somma è: %f\n",somma);
Poiché abbiamo inizializzato la variabile somma
a 0, la condizione di innesco
del ciclo while
è true
e dunque cominciamo ad iterare. Ad ogni nuova istanza
del ciclo un nuovo numero casuale viene generato e aggiunto alla variabile somma
.
Non appena il valore di somma
supera 10, il ciclo viene interrotto e il messaggio
viene stampato a schermo.
Alcuni esercizi#
Gli esercizi qui raccolti hanno per lo più lo scopo di verificare che abbiate assorbito queste informazioni generali sul linguaggio MATLAB, cosicché dal prossimo laboratorio ci si possa concentrare sull’implementazione di algoritmi prettamente numerici e sulle questioni affrontate nelle lezioni di teoria.
Esercizio 1
Scrivere una funzioni che calcoli la norma \(L^p\) di un vettore, definita come
se \(p\neq\infty\), altrimenti
La funzione dovrebbe prendere come argomenti vettore di \(R^d\), e un numero \(p\)
nell’intervallo \([1, \infty]\), che indica l’esponente della norma. Nota:
\(\infty\) in matlab si indica con Inf
.
Scrivere un errore se l’esponente non è nell’intervallo atteso.
function [norm] = lp_norm(v, p)
%% Calcola la norma l_p del vettore v, con esponente p
% qui il tuo codice
end
Esercizio 2
In questo esercizio dovrai capire quali sono i valori ottimali dell’intervallo di passo quando si approssimano le derivate utilizzando il metodo delle differenze finite.
Scrivi un programma per calcolare l’approssimazione della differenza finita
(FD
) della derivata di una funzione f
, calcolata al punto x
, utilizzando
un passo di dimensione h
. Ricorda la definizione di derivata approssimata:
Applica questo metodo alla funzione cos
, calcolane la derivata approssimata in
1
, e confronta il tuo risultato con la soluzione esatta -sin(1)
. Ripeti il
procedimento per h
in \(2^{-i}\) con \(i\) che varia tra 1 e 60, e salva l’errore
in un vettore.
Plotta il risultato (in scala logaritmica), e commenta quello che osservi. Dovresti ottenere qualcosa di simile a
Nota: se si vuole passare una funzione come argomento ad un altra funzione,
si possono usare le function handle
, ovvero il simbolo @
. Consideriamo per
esempio la seguente funzione, che si aspetta di poter chiamare il suo primo
argomento come se fosse una funzione:
function [res] = use_function(f,x)
res = f(x);
end
per poter usare use_function
passando come argomento una funzione già
definita, ad esempio la funzione cos
, devo scrivere:
res = use_function(@cos, x);
% equivalente a scrivere: res = cos(x);
La «function handle», può essere usata anche per definire funzioni «al volo», ad esempio:
res = use_function(@(x) x^2, x);
la scrittura f = @(x) x^2
è equivalente a definire una funzione
function res = f(x)
res = x^2
end
ovvero:
f = @(x) x^2
f =
function_handle with value:
@(x)x^2
>> f(2)
ans =
4
Esercizio 3
La costante aurea \(\varphi\) si può esprimere in forma compatta come
Ammettiamo di non avere un algoritmo per estrarre le radici quadrate, allora possiamo provare ad approssimare il valore di \(\phi\) usando la sua espansione in frazione continua:
Si scriva una funzione con il seguente prototipo:
function phi = frazionephi(n)
%%FRAZIONEPHI prende in input il numero di termini da utilizzare
%nell'approssimazione con frazione continua della sezione aurea e
%restituisce l'approssimazione.
end
Per costruire l’implementazione della funzione si usi un ciclo
for
. Suggerimento si organizzi il calcolo partendo dal «livello più basso» verso quello più alto.Supponiamo di sapere che un valore esatto di \(\varphi\) con 16 cifre significative è
1.6180339887498949
. Si modifichi la funzione precedente in una nuova funzione con il seguente prototipofunction [n,phi] = quantiterminiphi(tol) %%QUANTITERMINIPHI data in input una tolleranza tol sulla distanza tra %l'approssimazione della costante phi e il valore reale questa funzione %ci restituisce il numero di termini necessari e il valore dell'approssimazione phitrue = 1.6180339887498949; end
Per farlo si usi un ciclo
while
e la funzioneabs
(che implementa il valore assoluto) per misurare l”errore assoluto tra la nostra approssimazione e il valore vero.Si usi la funzione
fprintf
per stampare a schermo una tabella con tolleranza, numero di termini per raggiungerla, valore ottenuto ed errore per le tolleranze da1e-1
,1e-2
, fino a1e-10
.
Esercizio 4
Esercitiamoci ora nel costruire una funzione ricorsiva. Una sequenza collegata alla costante aurea \(\varphi\) è la sequenza di Fibonacci, ovvero la sequenza di numeri interi \(\{F_n\}_n = \{1,1,2,3,5,\ldots\}\) data da
Si implementi una funzione ricorsiva che calcola l”\(n\)mo numero di Fibonacci \(n\) utilizzando solamente la struttura condizionale
switch
, di cui riportiamo al solito il prototipo.
function f = fibonacci(n)
%FIBONACCI Implementazione ricorsiva della successione di Fibonacci. Prende
%in input il numero n e restituisce l'n-mo numero di Fibonacci Fn.
end
La funzione così costruita ha uno spiacevole difetto, se gli diamo in pasto \(n\) ci fornisce l”\(n\)mo numero, tuttavia se successivamente chiediamo l”\(n+1\)mo il calcolo per ottenerlo non ha memoria di quello che abbiamo fatto e ricalcola comunque tutti i precedenti. Costruiamo ora una versione non ricorsiva della funzione
fibonacci
. Possiamo ottenerla in diversi modi, ma quasi sicuramente avremo bisogno di un ciclofor
. Riportiamo come al solito il prototipo.
function f = fibonaccinonrecursive(n)
%FIBONACCINONRECURSIVE Implementazione non ricorsiva della successione
% di Fibonacci. Prende in input il numero n e restituisce un vettore
% che contiene tutti i numeri di Fibonacci da F0 a Fn.
end
Sfruttiamo ora la seconda implementazione che abbiamo fatto della funzione di Fibonacci per costruire una sequenza diversa. Consideriamo la sequenza di Viswanath [Vis00] così definita
dove \(v_{0}\) e \(V_{1}\) sono assegnati a piacere e il \(\pm\) ha la seguente
interpretazione: con probabilità \(1/2\) sommiamo, con probabilità \(1/2\)
sottraiamo. Un’idea per implementare questa funzione è quella di utilizzare la
funzione sign
(provate a vedere a cosa serve facendo help sign
).
Un prototipo per questa funzione è ad esempio
function v = viswanath(n,v0,v1)
%VISWANATH Implementazione non ricorsiva della successione
% di Viswanath. Prende in input il numero n, i valori di v0 e v1 e
% restituisce un vettore che contiene tutti i numeri di Viswanath da v0 a vn.
end
Una volta costruita la nostra funzione, possiamo visualizzare cosa otteniamo con essa (e confrontarlo con la successione di Fibonacci) tramite lo script:
%% Test della successione di Viswanath
n = 1000; % Numero di termini
% Calcoliamo la successione di Fibonacci
f = fibonaccinonrecursive(n);
% Calcoliamo la successione di Viswanath
v0 = 1;
v1 = 1;
v = viswanath(n,v0,v1);
% Valore asintotico
c = 1.13198824;
phi = (1+sqrt(5))/2;
figure(1)
semilogy(0:n,abs(v),'o',0:n,c.^(1:n+1),'-',0:n,f,'x',0:n,phi.^(1:n+1),'-');
legend({'Viswanath','Stima Asintotica','Fibonacci','Stima Asintotica'},...
'Location','northwest')
Nella parte terminale dello script % Valore asintotico
andiamo a confrontare
in scala semi-logaritmica sull’asse delle \(y\) i valori assoluti dei numeri
\(\{v_n\}_n\) e gli \(\{F_n\}_n\). In particolare possiamo osservare che per
entrambe le sequenza troviamo un numero \(k\) per cui queste crescono come
\(k^{n+1}\). In particolare, per la sequenza di Fibonacci questo \(k\) è la costante
aurea \(\varphi\) dell’esercizio precedente, mentre per la sequenza di Viswanath
è il valore \(c = 1.13198824\ldots\) (per una dimostrazione si veda [Vis00]).
Approfittiamo di questo anche per guarda come si possono ottenere dei
grafici di funzione su MATLAB, per decodificare i comandi di questa sezione
aiutatevi con help
. Un esempio del grafico ottenuto con lo script precedente
è il seguente:
Esercizio 5
Esploriamo ancora le funzioni di plot di MATLAB. Cerchiamo di produrre una stampa dell’insieme frattale di Mandelbrot. Questo insieme è l’insieme è l’insieme dei numeri complessi \(c\) per cui la funzione \(f_{c}(z)=z^{2}+c\) non diverge quando viene iterata a partire da \(z = 0\), cioè, l’insieme di quei punti \(c\) per cui la sequenza \(f_{c}(0),f_{c}(f_{c}(0)),\ldots\) resta limitata in valore assoluto. Possiamo costruire uno script MATLAB che ci permetta di disegnare un’approssimazione di questo insieme.
Utilizziamo la funzione
linspace
per costruire l’insieme dei numeri complessi \(c\) su cui vogliamo fare la valutazione. Poichélinspace
produce per noi un vettore reali, dobbiamo costruirne due – uno per ciascuna direzione – e trasformarli in un insieme di coppie di valutazioni con la funzionemeshgrid
. Un buon insieme reale da valutare per disegnare l’insieme di Mandelbrot è \([-2.1,0.6]\times[-1.1,1.1]\).Ora che abbiamo le valutazioni reali è necessario trasformare in numeri complessi \(c\). Possiamo ottenere questo risultato utilizzando la funzione
complex
:C = complex(X,Y)
sulla coppia di matrici di valutazioni ottenute dameshgrid
.Possiamo ora implementare un certo numero fissato di iterazioni della funzione \(f_{c}(z)=z^{2}+c\) mediante un ciclo
for
.Concludiamo l’esercizio disegnando l’insieme di Mandelbrot con la funzione
contourf(x,y,double(abs(Z)<1e6))
title('Insieme di Mandelbrot')
che è una buona occasione per guardare cosa fa la funzione di plot contourf
(help contourf
).
Bibliografia#
- Vis00(1,2)
Divakar Viswanath. Random Fibonacci sequences and the number $1.13198824…$. Math. Comp., 69(231):1131–1155, 2000. URL: https://doi.org/10.1090/S0025-5718-99-01145-X, doi:10.1090/S0025-5718-99-01145-X.