Ci sono due approcci fondamentali per progettare algoritmi
Servono ambedue ad aiutarci ad affrontare il problema
Nelle prossime slide:
Problema: confrontare due stringhe della stessa lunghezza
Obiettivo: definire una funzione "strcmp
":
strcmp('cane', 'casa')
Risultato:
< 0
se la prima parola precede la seconda0
se sono uguali> 0
se la seconda parola precede la primaApproccio (tendenzialmente) top-down:
Partiamo dal problema:
function z = strcmp(p1, p2)
z = <confronto>;
end
Approccio (tendenzialmente) top-down:
Per confrontare le parole dobbiamo confrontare le lettere:
function z = strcmp(p1, p2)
z = 0; % assumo che siano uguali
for ii = 1:length(p1)
<confronto le lettere in posizione ii>
end
end
Approccio (tendenzialmente) top-down:
Le lettere possono essere confrontate direttamente:
function z = strcmp(p1, p2)
z = 0;
for ii = 1:length(p1)
if p1(ii) < p2(ii)
<gestisci il risultato>
end
if p1(ii) > p2(ii)
<gestisci il risultato>
end
end
end
Approccio (tendenzialmente) top-down:
A questo punto calcolare il risultato è banale:
function z = strcmp(p1, p2)
z = 0;
for ii = 1:length(p1)
if p1(ii) < p2(ii)
z = -1
break % interrompo la computazione!
end
if p1(ii) > p2(ii)
z = 1
break % interrompo la computazione!
end
end
end
Approccio (tendenzialmente) bottom-up:
Le stringhe sono vettori di numeri: possiamo confrontarle
function z = strcmp(p1, p2)
s1 = p1 < p2
s2 = p1 > p2
end
Se provo ad invocare la funzione ottengo:
strcmp('cane', 'casa') % stampa [0, 0, 1, 0]
% stampa [0, 0, 0, 1]
Il risultato dipende da dove (s1
ed s2
) si trova l'1
più a sinistra
Funzione find
Octave offre la funzione:
find(V) % denota gli indici dei valori ~= 0 in V
find(V, N) % denota i primi N indici di cui sopra
Input:
Output:
Approccio (tendenzialmente) bottom-up:
Posso usare find
per trovare la posizione degli 1
function z = strcmp(p1, p2)
s1 = p1 < p2;
s2 = p1 > p2;
n1 = find(s1, 1)
n2 = find(s2, 1)
end
Se provo ad invocare la funzione ottengo:
strcmp('cane', 'casa') % stampa 3
% stampa 4
Approccio (tendenzialmente) bottom-up:
La differenza tra n1
ed n2
fornisce il risultato
function z = strcmp(p1, p2)
s1 = p1 < p2;
s2 = p1 > p2;
n1 = find(s1, 1);
n2 = find(s2, 1);
z = n1 - n2
end
Se provo ad invocare la funzione ottengo:
strcmp('cane', 'casa') % denota -1 (< 0)
Approccio (tendenzialmente) bottom-up:
Aggiungiamo una piccola modifica per gestire stringhe identiche:
function z = strcmp(p1, p2)
s1 = p1 < p2;
s2 = p1 > p2;
n1 = find(s1, 1);
n2 = find(s2, 1);
if length(n1) == 0
z = 0
else
z = n1 - n2
end
end
Il metodo top-down
Il metodo bottom-up
Di fatto, si usano sempre insieme
Vediamo alcune funzioni per costruire vettori/matrici
Per ottenere una matrice M x N
di zeri:
zeros(M, N)
Restituisce una matrice M x N
di 1
:
ones(M, N)
Per ottenre una matrice M x N
di numeri casuali in (0, 1)
rand(M, N)
Octave fornisce una funzione per calcolare il prodotto di un vettore:
prod(X)
Definite una nuova funzione:
XXX_prod(V) % XXX è un qualunque prefisso
V
, passato come argomentoQualche dritta:
XXX_prod.m
rand
per ottenere vettori casuali e fare testprod
Una possibile soluzione:
function yy = ch3_prod(xx)
yy = 1;
for v = xx
yy = yy .* v;
end
end
Octave fornisce una funzione per calcolare la norma di un vettore:
norm(X, P) % usate help per i dettagli
X
è il vettoreP
è l'ordine della normaPer P = 2
, la funzione calcola la distanza euclidea dall'origine:
‖
Definite una nuova funzione:
XXX_norm(X) % XXX è un qualunque prefisso
X
Qualche dritta:
ch3_prod.m
rand
per ottenere vettori casuali e fare testsqrt
.^0.5
Una possibile soluzione:
function yy = ch3_norm(xx)
yy = 0;
for vv = xx
yy = yy + vv.^2;
end
yy = sqrt(yy);
end
Un'altra possibile soluzione:
function yy = ch3_norm(xx)
xx2 = xx.^2;
yy = sqrt(sum(xx2));
end
Octave permette di trovare il massimo elemento di un vettore con:
max(X)
Se invocata con due elementi a sinistra dell'operatore "=
":
[V, I] = max(X)
La funzione restituisce:
V
I
Così, svolge anche il ruolo della funzione matematica {\rm argmax}
Definite una nuova funzione:
[V, I] = XXX_max(X) % XXX è un qualunque prefisso
Che replica il comportamento di max
in Octave, restituendo:
V
il massimo elemento del vettore X
I
l'indice della sua prima occorrenzaPer definire una funzione che restituisce più argomenti la sintassi è:
function [<r1>, <r2>, ...] = <nome>(<p1>, <p2>, ...)
...
end
Una possibile soluzione:
function [yy, ii] = ch3_max(xx)
ii = 1;
yy = xx(1);
for jj = 2:length(xx)
if xx(jj) > yy
yy = xx(jj);
ii = jj;
end
end
end
Una possibile invocazione:
[y1, i1] = ch3_max([1, 7, 3, 2, 5, 10, 9, 7, 10])
Octave permette di effettuare il prodotto scalare con:
X * Y' % HP: vettori riga
dot(X, Y) % funzione dedicata
X
e Y
sono due vettori della stessa lunghezzaDefinite una nuova funzione:
XXX_dot(X, Y) % scegliete il prefisso XXX
Che replichi la stessa funzionalità
Una possibile soluzione:
function zz = ch3_dot(xx, yy)
zz = 0;
for ii = 1:length(xx)
zz = zz + xx(ii) .* yy(ii);
end
end
Oppure:
function zz = ch3_dot(xx, yy)
zz = sum(xx .* yy);
end
Octave fornisce la funzione:
find(X)
0
Definite una nuova funzione:
XXX_find(X) % scegliete il prefisso XXX
Che replichi tale funzionalità.
Una possibile soluzione:
function yy = ch3_find(xx)
yy = [];
k = 1;
for ii = 1:length(xx)
if xx(ii) ~= 0
yy(k) = ii;
k = k + 1;
end
end
end
Octave permette di costruire una matrice diagonale con:
diag(X)
X
\left(\begin{array}{cccc}
x_1 & 0 & \ldots & 0 \\
0 & x_2 & \ldots & 0 \\
\ldots & \ldots & \ldots & \ldots \\
0 & 0 & \ldots & x_n \\
\end{array}\right)
Definite una nuova funzione:
XXX_diag(X) % XXX è un qualunque prefisso
Che replichi il comportamento di diag
in Octave
Qualche dritta:
Una possibile soluzione:
function yy = ch3_diag(xx)
nn = length(xx);
yy = zeros(nn);
for ii = 1:nn
yy(ii,ii) = xx(ii);
end
end
Un'altra possibile soluzione:
function yy = ch3_diag(xx)
nn = length(xx);
yy = zeros(nn);
ii = (0:nn-1) .* nn + (1:nn); % idx sulla diagonale
yy(ii) = xx; % indicizzazione via vettore unico
end
Abbiamo visto che Octave permette di accedere ad un vettore con:
V(I)
V
è un vettoreI
è un vettore di indiciDefinite una nuova funzione:
XXX_index(V, I)
Che replica la stessa funzionalità
Una possibile soluzione:
function yy = ch3_index(xx, ii)
nn = length(ii);
yy = zeros(1, nn);
for jj = 1:nn
kk = ii(jj); % Ottengo l'indice in xx
yy(jj) = xx(kk); % Assegno il valore
end
end
Abbiamo visto che Octave permette di accedere ad un vettore con:
V(I)
V
è un vettoreI
è un vettore di valori logiciDefinite una nuova funzione:
XXX_index2(V, I)
Che replica la stessa funzionalità
Una possibile soluzione:
function yy = ch3_index2(xx, cc)
yy = [];
kk = 1; % Indice corrente sul vettore risultato
for jj = 1:length(xx)
if cc(jj) ~= 0
yy(kk) = xx(jj); % Assegno un elemento
kk = kk + 1; % Incremento l'indice
end
end
end
Octave fornisce la funzione:
unique(X)
X
che restituisce gli elementi distinti del vettore X
Definite una nuova funzione:
XXX_unique(X, Y) % scegliete il prefisso XXX
Che replichi la stessa funzionalità
Una possibile soluzione:
function yy = ch3_unique(xx)
yy = []; % Uso vettore vuoto
ii = 1; % Indice corrente sul vettore risultato
for vv = xx
% Controllo se l'elemento non sia già presente
if ~any(yy == vv)
yy(ii) = vv; % Inserisco l'elemento
ii = ii + 1; % Incremento l'indice
end
end
end
Octave fornisce la funzione:
isprime(N)
N
è primoDefinite una nuova funzione:
XXX_isprime(N) % scegliete il prefisso XXX
Che replichi tale funzionalità
1
e per se stessoOctave permette di calcolare la divisione intera con:
idivide(x, y)
E il resto della divisione intera con:
mod(x, y)
Esempio:
idivide(5, 2) % denota 2
mod(5, 2) % denota 1
x
è divisibile per y
sse il resto è 0
Una possibile soluzione:
function yy = ch3_isprime(vv)
yy = true; % assumo che il numero sia primo
for ww = 2:sqrt(vv) % Cerco un divisore
if mod(vv, ww) == 0 % verifico il resto
yy = false % il numero non è primo
break % posso terminare
end
end
end
Possiamo smettere di cercare divisori quando arriviamo a \sqrt{v}