Introduzione a ”MAPLE”
Andrea Ellero, Alberto Zorzi - AA.2002/2003
Questi appunti hanno lo scopo di introdurre all’uso del programma Maple
(Waterloo Maple Inc.) e sono di supporto alle lezioni di Informatica (idoneità),
Facoltà di Economia dell’Università Ca’Foscari di Venezia. La conoscenza del
programma puù essere approfondita utilizzando un qualsiasi manuale di Maple.
INDICE DEI PARAGRAFI
1. Calcoli, espressioni e funzioni.
2. Risoluzione di equazioni e sistemi.
3. Limiti, derivate, integrali.
4. Funzioni di più variabili e grafici.
5. Algebra delle matrici.
6. Introduzione alla programmazione.
1
1.1
1. Calcoli, espressioni e funzioni
Calcoli numerici e letterali
Maple è in grado di soddisfare esigenze di calcolo sia di tipo simbolico che
numerico.
1.1.1
Numeri razionali e numeri floating point
Provate a calcolare una somma algebrica di due numeri interi. Seguite
l’esempio e battete il tasto di ritorno a capo.
> 3-5;
−2
NB: non è necessario scrivere ”=” mentre è obbligatorio concludere
l’espressione con ”;”
> 7/2;
7
2
NB: non viene eseguita la divisione in modo numerico per non perdere
precisione negli eventuali calcoli successivi. Per Maple, 7/2 e 3.5 sono oggetti
diversi: il primo è un numero razionale, il secondo è un numero in virgola
mobile (floating point). Per chiarire la differenza consideriamo uno stesso
calcolo eseguito con Maple utilizzando la notazione razionale e con virgola:
> (1/3.0)*3;
.9999999999
> (1/3)*3;
1
NB: il simbolo ’*’ indica la moltiplicazione e non puù essere sottinteso.
Si noti che il primo calcolo viene effettuato in virgola mobile anche se un solo
numero (3.0) è di tipo floating point. Il secondo calcolo è esatto perchè 1/3 è
riconosciuto e trattato come numero razionale. Vediamo che pasticci possono
accadere: consideriamo un calcolo in cui siano utilizzate le due notazioni
appena viste
> (1/3.0)*3-(1/3)*3;
−.1 10−9
cioè -0.1 per un miliardesimo ovvero -0.0000000001 mentre, ovviamente,
dovrebbe risultare 0.
Possiamo chiedere un calcolo numerico più accurato chiedendo a Maple di
utilizzare un maggior numero di cifre (digits) decimali:
> Digits:= 10;
Digits := 10
>
(1/3.0)*3-(1/3)*3;
−.1 10−24
ora gli zeri che precedono la cifra 1, dopo la virgola, sono 24: si tratta di un
numero molto vicino allo zero ma non è esattemente zero.
1.1.2
Arrotondamenti, troncamenti ed errori
La precisione nei calcoli effettuati da Maple è assai elevata, ma alla precisione
dei calcoli è necessario accompagnare attenzione nel fornire i dati alla
macchina e nell’interpretare i risultati che la macchina restituisce. Ad esempio
se forniamo dati poco precisi è inutile, se non dannoso, utilizzare molte cifre
decimali nei calcoli effettuati da Maple. Vediamo allora come comportarci in
alcuni casi frequenti.
Anzitutto, quando è opportuno trascurare alcune cifre decimali di un numero
possiamo semplicemente tenere solo le cifre che ci interessano: effettuiamo in
tal caso un troncamento del numero.
Possiamo diminuire l’errore nel considerare il dato effettuando un
arrotondamento. Effettuare un arrotondamento significa aumentare di una
unità l’ultima cifra che consideriamo qualora la prima cifra che stiamo per
eliminare sia maggiore o uguale a 5, troncare il numero altrimenti.
Ad esempio se
> x:=123.4492;
x := 123.4492
volendo considerare due cifre decimali con un troncamento otteniamo il
numero 123.44, arrotondando troviamo invece 123.45. Se desideriamo una sola
cifra decimale allora sia troncando che arrotondando otteniamo 123.4.
E’ chiaro che in generale l’arrotondamento, a parità di cifre considerate,
consente di ridurre l’errore commesso ed è pertanto preferibile.
Vediamo ora cosa accade se effettuiamo gli arrotondamenti troppo presto.
Supponiamo di dover calcolare quanto vino sia necessario per riempire un
contenitore cilindrico di alluminio (qui semplifichiamo e supponiamo si tratti
di un cilindro perfetto). Prendiamo le misure interne del cilindro. Con un
calibro (precisione 1/10=0.1 mm) riusciamo a misurare il diametro:
> Diam:= 9.31 ; # cm
Diam := 9.31
per l’altezza usiamo un metro con precisione di un millimetro:
> Altezza:= 19.4 ; # cm
Altezza := 19.4
Calcoliamo il volume:
> Volume:=(Diam/2)^2*Pi*Altezza; # il risultato è in cm^3
Volume := 420.3790850 π
> evalf(%);
1320.659845
AnzichÈ considerare l’intera cifra decidiamo di considerare solo le quattro cifre
a sinistra della virgola.
Quindi, arrotondando all’unità, otteniamo 1321 centimetri cubi (in litri:
1,321). Supponiamo che il vino costi 0,51 euro al litro e di dover riempire
19800 lattine. Il costo che dovremo sostenere sarà allora:
> Costo_totale:=1.321*0.51*19800;
Costo totale := 13339.45800
ovvero 13339,46 euro.
E’ una bella cifra che ci spinge ad effettuare una valutazione più precisa.
Tornando al calcolo del volume prendiamo subito qualche cifra in più, anzi
teniamo tutte quelle che vediamo scritte, non ci costa più fatica:
> Costo_totale:=1.320659845*0.51*19800;
Costo totale := 13336.02312
La differenza, in euro, è data da
> 13339.46-13336.02;
3.44
L’esempio ci suggerisce di effettuare i calcoli ritardando il più possibile il
momento nel quale effettuare gli arrotondamenti.
Errori nei dati iniziali e trasmissione dell’errore
Ma il nostro conto è anche affetto da un altro errore, un errore inevitabile
anche se meno evidente, dovuto alla precisione con la quale sono noti i dati
iniziali.
Per misurare il diametro del cilindro avevamo infatti a disposizione un calibro
con precisione di un decimo di millimetro. In realtà dunque siamo certi che il
diametro (Diam=9,31 cm) ha una misura compresa tra 9,30 e 9,32 cm; di più
non possiamo chiedere al calibro. Diciamo che l’errore assoluto in questa
misura è di 0,01 cm e possiamo scrivere Diam = 9,310,01. L’errore percentuale,
o errore relativo, è dato dal rapporto tra l’errore assoluto e la misura effettuata
> er_Diam:=0.01/9.32;
er Diam := .001072961373
vale a dire circa uno su mille.
Analogamente per l’altezza, l’errore assoluto è di 0,1 cm, ovvero Altezza=19,4
0,1 cm. L’errore relativo è in tal caso:
>
er_Altezza:=0.1/19.4;
er Altezza := .005154639175
quindi dell’ordine del 5 per mille.
Ma quale errore commettiamo nel calcolo del volume?
Si dimostra che moltiplicando tra loro due misure l’errore percentuale
trasmesso nel calcolo è dato dalla somma degli errori percentuali. Nel nostro
caso allora:
> er_Volume:=er_Diam+er_Diam+er_Altezza;
er Volume := .007300561921
L’errore relativo nel calcolo del volume è dunque del 7 per mille (perchÈ
sommiamo due volte l’errore relativo del diametro?). Lo stesso errore relativo
si ripercuote sul calcolo del costo totale. Si noti che gli altri fattori che
compaiono nel calcolo del costo totale sono il numero 1/2, che è esatto cioè con
errore assoluto nullo, e π, che Maple tiene in memoria con un numero di cifre
significative sufficientemente elevato da poter essere considerato con errore
relativo trascurabile.
In termini assoluti il calcolo del volume è affetto allora dall’errore:
> ea_Volume:=er_Volume*Volume;
ea Volume := 3.069003540 π
>
evalf(%);
9.641558976
In realtà quindi il volume di una singola lattina è 1320.659845 9.641558976
ovvero di millilitri 132110. L’errore su di una singola lattina è dell’ordine di
mezzo cucchiaio da cucina!
Per quanto riguarda il costo complessivo abbiamo invece:
> ea_Costo_totale:=er_Volume*Costo_totale;
ea Costo totale := 97.36046257
quindi un errore dell’ordine di un centinaio di euro, un errore che rende in
realtà trascurabile, in questo caso, l’errore complessivo dovuto al precedente
arrotondamento al centimetro cubo del volume della lattina!
Problemi simili a quelli incontrati nel caso di una moltiplicazione si hanno
anche nella trsmissione degli errori effettuando altre operazioni tra i dati: per
un loro adeguato trattamento rimandiamo ai testi specializzati in analisi
numerica.
1.1.3
Calcoli simbolici
Maple è in grado di fare calcoli di tipo letterale utilizzando moltissimi comandi
per la manipolazione algebrica. Vediamo qualche esempio.
> 3*a-5*a+4*a-b*a;
2a−ba
Manipoliamo l’espressione raccogliendone i fattori:
> factor(%);
−a (−2 + b)
NB: per indicare la riga di calcolo (execution group) appena eseguita si utilizza
il simbolo di percentuale (%), nelle versioni di Maple meno recenti lo stesso
effetto si ha utilizzando il doppio apice (”).
> 3*x*(x^2-y^2)^2/((x+y)*9*x);
1 (x2 − y2 )2
3 x+y
NB: l’elevamento a potenza viene indicato dal simbolo ’ˆ’.
Si puù notare che Maple effettua immediatamente le semplificazioni ovvie. Ma
puù fare di meglio:
> simplify(%);
1
(x − y) (x2 − y2 )
3
Vediamo qualche altra semplificazione:
> root(x^2,2);
√
x2
> simplify(%);
csgn(x) x
NB: csgn(x) indica il segno di x. Poteva far di meglio scrivendo il valore
assoluto di x, che pure è definito (si provi a scrivere >abs(x);) e si legge
meglio: questa è una avvisaglia del fatto che Maple puù perdersi in un bicchier
d’acqua.
Ancora un esempio:
> (x+2)*(x^2-1)/(x+1);
(x + 2) (x2 − 1)
x+1
cerchiamo di riscrivere l’espressione in modo più semplice:
>
factor(%);
(x − 1) (x + 2)
Proviamo ad eseguire le operazioni
> expand(%);
x2 + x − 2
ancora non ci soddisfa, torniamo a fattorizzare
> factor(%);
(x − 1) (x + 2)
Rifacciamo il conto in ordine diverso
> (x+2)*(x^2-1)/(x+1);
(x + 2) (x2 − 1)
x+1
proviamo prima ad espandere
> expand(%);
x3
x
2 x2
2
−
+
−
x+1 x+1 x+1 x+1
un disastro! Proviamo a vedere se si semplifica qualcosa
> simplify(%);
x2 + x − 2
meglio. Ora proviamo a fattorizzare
> factor(%);
(x − 1) (x + 2)
L’ordine delle operazioni è ovviamente importante: l’abbiamo provato tante
volte facendo i conti a mano, qui almeno possiamo riprovare velocemente.
Provate a rifare il conto cambiando ancora l’ordine delle manipolazioni e con
altre espressioni.
Dagli esempi fin qui visti simplify puù apparire come il comando più utile; in
realtà il comando più flessibile è expand che consente, ad esempio, di stabilire
quale fattore raccogliere:
> expand((x+2)*(x+3)*(y+z),x);
>
x2 y + x 2 z + 5 x y + 5 x z + 6 y + 6 z
expand((x+2)*(x+3)*(y+z),x+2);
>
(x + 2) x y + (x + 2) x z + 3 (x + 2) y + 3 (x + 2) z
expand((x+2)*(x+3)*(y+z),y+z);
(y + z) x2 + 5 (y + z) x + 6 y + 6 z
>
factor(%);
(x + 2) (x + 3) (y + z)
1.1.4
Altre informazioni
Maple consente operazioni sui numeri interi quali la divisione con resto, il
fattoriale, la fattorizzazione..., sui numeri complessi, su insiemi, vettori,
matrici,...
Sono inoltre definite alcune costanti quali
> exp(1);
e
> Pi;
π
1.2
Assegnazioni e funzioni
Possiamo associare un nome ad un oggetto matematico quale ad esempio
un’espressione, un’equazione, una funzione. La sintassi è nome := oggetto; .
Ad esempio
> fattore1:=x*y;
fattore1 := x y
ora fattore1 è una variabile che vale xy. Consideriamo una seconda variabile
> fattore2:=x*z;
fattore2 := x z
e moltiplichiamo
> prodotto:=fattore1*fattore2;
prodotto := x2 y z
Maple ha già effettuato qualche conto riconoscendo il valore delle variabili
utilizzate.
Possiamo definire in questo modo anche le funzioni:
> f:=x->x^2-1;
f := x → x2 − 1
La variabile f è in questo caso un oggetto di tipo funzione che possiamo
utilizzare come siamo abituati, per esempio per valutarla in un punto
>
f(2);
3
ma anche in un punto simbolico
> f(antonio);
antonio 2 − 1
o componendola
> f(y-1);
(y − 1)2 − 1
Combiniamo una variabile di tipo numero con la funzione f
> alfredo:=3;
alfredo := 3
> f(alfredo);
8
E’ poi semplicissimo disegnare grafici mediante il comando plot:
> plot(f,-2..4,-2..5);
5
4
3
2
1
–2
–1
0
1
2
3
4
–1
–2
Nell’esempio la porzione di grafico che interessa è quella racchiusa nel
rettangolo [-2,4]x[-2,5]. Il secondo argomento del comando plot è un intervallo
contenuto nel dominio di f relativamente al quale interessa rappresentare il
grafico di f. Il terzo argomento è opzionale e, se presente, individua l’intervallo
di interesse relativamente al codominio.
2
2. Risoluzione di equazioni e sistemi
2.1
Equazioni
La risoluzione di equazioni è particolarmente semplice, il comando da
utilizzare è solve.
> solve({x^2=4},{x});
{x = 2}, {x = −2}
Maple commette errori nel caso di calcoli letterali, anche semplici, se ci sono
parametri
> solve({a*x+b=0},{x});
b
{x = − }
a
(cosa succede se a=0?) e non risponde in alcuni casi numerici ”proibiti”
> solve({0*x+3=0},{x});
E’ capace di lavorare con i numeri complessi
> solve({x^2+1=0},{x});
{x = I}, {x = −I}
(I è l’unità immaginaria vale a dire il numero complesso tale che Iˆ2=-1).
Vediamo qualche altro esempio
> solve({x^3+x^2-5*x=0},{x});
1 1√
1 1√
{x = 0}, {x = − +
21}, {x = − −
21}
2 2
2 2
> solve({x^3-13*x+12=0},{x});
>
{x = 1}, {x = 3}, {x = −4}
solve({x^2+y=0},{x,y});
>
{y = −x2, x = x}
solve({x^2+y^2=0},{x,y});
>
{x = I y, y = y}, {x = −I y, y = y}
solve({x^7+x^2=0},{x});
√
1 1 √ p
1√
5 + − I 2 5 + 5},
{x = 0}, {x = 0}, {x = −1}, {x = −
4
4 4
√
√
1 1√
1 1√
1 √ p
1 √ p
{x = +
5 − I 2 5 − 5}, {x = +
5 + I 2 5 − 5},
4 4
4
4 4
4
√
1√
1 1 √ p
{x = −
5 + + I 2 5 + 5}
4
4 4
>
solve({exp(x)*x^2-3=0},{x});
1√
1√
{x = 2 LambertW(
3)}, {x = 2 LambertW(−
3)}
2
2
Nell’ultimo esempio LambertW è una funzione che soddisfa l’equazione
LambertW(x) * exp(LambertW(x)) = x .
Maple è riuscito a fornirci una soluzione esatta anche in questo caso.
In genere siamo perù più interessati alla risoluzione numerica di una equazione
come la precedente. Il comando da usare è in tal caso fsolve, con la stessa
sintassi di solve
> fsolve({exp(x)*x^2-3 = 0},{x});
{x = 1.033230904}
Ecco un altro caso interessante
> f:=sin(x);
>
f := sin(x)
sol1:=solve({f=0.3},{x});
>
sol1 := {x = .3046926540}
sol2:=fsolve({f=0.3},{x});
sol2 := {x = .3046926540}
Quale soluzione è corretta? In realtà queste sono solo due delle infinite
soluzioni dell’equazione considerata. Maple non è in grado, in questo caso, di
avvisarci della presenza di altre soluzioni.
Possiamo comunque accertare la bontà delle soluzioni trovate sostituendole
come argomento della funzione f utilizzando il comando subs
> subs(sol1,f);
sin(.3046926540)
chiedendo il calcolo del valore della funzione nel punto mediante il comando
evalf
> evalf(%);
.3000000000
> subs(sol2,f);
sin(.3046926540)
> evalf(%);
.3000000000
2.2
Sistemi di equazioni
Per risolvere sistemi di equazioni è sufficiente scrivere come argomenti del
comando solve l’elenco delle equazioni e delle variabili rispetto alle quali
risolverle
> eq1:=3*x+2*y=2;
eq1 := 3 x + 2 y = 2
> eq2:=x-y=4;
eq2 := x − y = 4
> solve({eq1,eq2},{x,y});
{x = 2, y = −2}
Vediamo un caso in cui il sistema presenta infinite soluzioni
> eq3:=x+2*y=1;
eq3 := x + 2 y = 1
> eq4:=2*x+4*y=2;
eq4 := 2 x + 4 y = 2
> solve({eq3,eq4},{x,y});
{y = y, x = −2 y + 1}
Mettiamo ora alla prova Maple su di un sistema di due equazioni non lineari.
Sia
>
primo1:=3*x^3-exp(y);
primo1 := 3 x3 − ey
il primo membro della prima equazione e
> primo2:=y^2+x;
primo2 := y2 + x
il primo membro della seconda equazione e risolviamo il sistema primo1=0,
primo2=3 dando anche un nome all’insieme delle soluzioni
> soll:=fsolve({primo1=0,primo2=3},{x,y});
soll := {x = .4052948807, y = −1.610808840}
Controlliamo l’esattezza del risultato sostituendo le soluzioni trovate in primo1
e primo2
> subs(soll,primo1);
>
evalf(%);
.1997260023 − e(−1.610808840)
0.
>
subs(soll,primo2);
3.000000000
Si noti che la soluzione trovata non è unica (anche se Maple non lo dice):
> subs({x=1.098015136,y=1.379124673},primo1);
3.971423811 − e1.379124673
>
evalf(%);
>
0.
subs({x=1.098015136,y=1.379124673},primo2);
3.000000000
3
3. Limiti, derivate, integrali
Maple fornisce tutti gli strumenti tradizionali dell’analisi. Iniziamo a vedere
alcune delle possibilità offerte nel caso di funzioni reali di variabile reale.
Arriveremo a poter svolgere il tradizionale ”studio di funzione” in modo
completo.
3.1
3.1 Limiti
Definiamo una funzione della quale andremo poi a cercare alcuni limiti
> g:= x-> (x+1)*(x+2)*(x-3)/(x-1);
(x + 1) (x + 2) (x − 3)
x−1
Proviamo a calcolare il limite per x - > 1. Prima definiamo il limite che ci
interessa
g := x →
>
Limit(g(x),x=1);
lim
x→1
e adesso cerchiamone il valore
> value(%);
(x + 1) (x + 2) (x − 3)
x−1
undefined
La risposta è corretta infatti calcolando i limiti da sinistra e da destra troviamo
> Limit(g(x),x=1,left);
lim
x→1−
>
value(%);
(x + 1) (x + 2) (x − 3)
x−1
∞
Per il limite destro utilizziamo la lettera minuscola nel comando limit , questo
consente di ottenere subito il valore numerico del limite
> limit(g(x),x=1,right);
−∞
Se gli ultimi due limiti calcolati da Maple sono giusti (e lo sono) allora il primo
limite cercato non esiste. Vediamo come si calcolano i limiti a + e - infinito.
Ad esempio
> Limit(g(x),x=-infinity);
lim
x→(−∞)
>
value(%);
(x + 1) (x + 2) (x − 3)
x−1
∞
3.2
3.2 Derivate
Consideriamo una funzione di x e contenente il parametro k:
> f:=x->x*cos(k*x);
f := x → x cos(k x)
> diff(f(x),x);
cos(k x) − x sin(k x) k
In realtà il fatto che f sia definita come funzione di x a Maple interessa poco.
Possiamo infatti derivare anche rispetto a k:
> diff(f(x),k);
−x2 sin(k x)
Cerchiamo ora i punti stazionari di una funzione. Definiamo la funzione
> h:=x->x^3-2*x^2-4*x;
h := x → x3 − 2 x2 − 4 x
e calcoliamone i punti stazionari ai quali diamo anche un nome (staz ):
> staz:=solve(diff(h(x),x)=0,x);
−2
staz := 2,
3
Più in generale possiamo studiare la monotonia della funzione determinando
gli intervalli nei quali la derivata si mantiene non negativa:
> solve(diff(h(x),x)>=0,x);
−2
), RealRange(2, ∞)
RealRange(−∞,
3
Otteniamo cosı̀ che -2/3 è un punto di massimo locale mentre è di minimo
locale il punto 2.
Vediamo come classificare i punti stazionari utilizzando la derivata seconda.
Anzitutto calcoliamola nel generico punto x:
> diff(h(x),x,x);
6x−4
Speghiamo ora a Maple di considerare l’espressione appena trovata come
immagine di x tramite la funzione derivata seconda di h, che chiameremo
DDh; per far questo usiamo il comando unapply
> DDh:=unapply(%,x);
DDh := x → 6 x − 4
Adesso possiamo valutare la derivata seconda nei due punti stazionari. Nel
primo punto si ha
> DDh(staz[1]);
8
perciù si tratta di un massimo. Nel secondo punto stazionario si ha
> DDh(staz[2]);
−8
che ci conferma che il punto 2 è di minimo per h. Vediamo il grafico
(numerico) di h fornito da Maple:
> plot(h(x),x=-3..5,-10..10);
10
8
6
4
2
–3
–2
–1
0
–2
–4
–6
–8
–10
1
2
x
3
4
5
3.3
3.3 Uno studio di funzione
Esaminiamo le caratteristiche del grafico di una funzione in modo dettagliato.
Consideriamo la funzione
> f:=x->x^3*(x+1)/(x^2+2);
f := x →
x3 (x + 1)
x2 + 2
Proviamo a farci disegnare il grafico da Maple:
> plot(f(x),x=-10..10);
100
80
60
40
20
–10 –8
–6
–4
–2 0
2
4
x
6
8
10
Pare che in 0 ci sia un minimo. Ma vediamo di effettuare uno studio delle
qualità delle proprietà della funzione in modo completo. Partiamo con il
calcolo dei limiti:
> limit(f(x),x=-infinity);
∞
> limit(f(x),x=+infinity);
∞
Studiamo il segno di f:
> solve(f(x)>=0,x);
RealRange(−∞, −1), RealRange(0, ∞)
pertanto f è negativa tra -1 e 0, positiva altrove. Per studiare la monotonia
calcoliamo la derivata di f
>
diff(f(x),x);
3
x2 (x + 1)
x3
2 x4 (x + 1)
+ 2
−
2
x +2
x +2
(x2 + 2)2
che si puù semplificare un po’:
> simplify(%);
x2 (2 x3 + 8 x + x2 + 6)
(x2 + 2)2
Cerchiamo i punti stazionari
> staz:=solve(diff(f(x),x)=0,x);
47
1
1
(1/3)
6
+
− ,
staz := 0, 0, − %1
(1/3)
6
6
%1
47
1 1 √
1
1
1
(1/3)
(1/3)
%1
−
−
− + I 3 (− %1
12
12 %1(1/3) 6 2
6
1
1
1
47
1 1 √
(1/3)
(1/3)
%1
−
−
− I 3 (− %1
−
12
12 %1(1/3) 6 2
6
√
%1 := 253 + 18 518
47
1
),
6 %1(1/3)
1
47
)
6 %1(1/3)
Si leggono proprio male! Prima di tentare di scrivere meglio i punti stazionari
vediamo cosa si puù dire riguardo alla monotonia fornendo a Maple solo il
fattore che determina il segno della derivata
> solve(2*x^3+8*x+x^2+6>=0,x);


47
√
1

 1
6√
− , ∞
RealRange − (253 + 18 518)(1/3) +
6
(253 + 18 518)(1/3) 6
ancora una volta qualcosa di poco leggibile. Proviamo a riscrivere i punti
stazionari:
> s1:=evalf(staz[1]);
s1 := 0.
>
s2:=evalf(staz[2]);
s2 := 0.
>
>
>
s3:=evalf(staz[3]*1.);
s3 := −.7212304534
s4:=evalf(staz[4]);
s4 := .1106152263 − 2.036497585 I
s5:=evalf(staz[5]);
s5 := .1106152263 + 2.036497585 I
Valutiamo f nel terzo punto stazionario:
> f(s3);
−.04149894724
Proviamo ora ad utilizzare la derivata seconda per classificare i punti stazionari
> DDf:=unapply(diff(f(x),x,x),x);
DDf := x → 6
x (x + 1)
6 x2
14 x3 (x + 1)
4 x4
8 x5 (x + 1)
+ 2
−
− 2
+
2
2
2
2
x +2
x +2
(x + 2)
(x + 2)
(x2 + 2)3
Nei punti stazionari si ottiene:
> evalf(DDf(staz[1]));
0.
>
evalf(DDf(staz[3]));
.7926818053
Il punto terzo punto stazionario è pertanto di minimo locale. Per classificare il
primo punto stazionario abbiamo bisogno di calcolare la derivata terza:
> DDDf:=unapply(diff(f(x),x,x,x),x);
DDDf := x → 6
−
x+1
18 x
54 x2 (x + 1)
42 x3
96 x4 (x + 1)
24 x5
+
−
−
+
+
x2 + 2 x2 + 2
(x2 + 2)2
(x2 + 2)2
(x2 + 2)3
(x2 + 2)3
48 x6 (x + 1)
(x2 + 2)4
> evalf(DDDf(staz[1]));
3.
L’origine è dunque un punto di flesso.
Avendo capito che i punti stazionari sono certamente compresi nell’intervallo
[-1, 1], proviamo a tracciare un secondo grafico numerico di f, questa volta
restringendo il range da considerare
> plot(f(x),x=-1.2..1);
0.6
0.5
0.4
0.3
0.2
0.1
–1
0
–0.5
0.5
x
1
Lo studio di funzione consente di guidare il modo di tracciare anche il grafico
numerico molto meglio rispetto al fuorviante grafico numerico tracciato
all’inizio.
3.4
3.4 Integrali
Possiamo calcolare sia integrali indefiniti che definiti. Ad esempio, calcoliamo
una primitiva della funzione
> f:=x->x*exp(x+1);
f := x → x e(x+1)
usando il comando int:
> int(f(x),x);
e(x+1) (x + 1) − 2 e(x+1)
Verifichiamo i calcoli derivando
> diff(%,x);
e(x+1) (x + 1) − e(x+1)
e semplificando
> simplify(%);
x e(x+1)
ottenendo la conferma della correttezza del calcolo dell’integrale.
Nota: anche se non sappiamo se il programma ha fornito una primitiva
corretta ci possiamo senz’altro fidare delle derivate fornite da Maple. Si tratta
di un conto puramente meccanico che un programmatore puù insegnare ad un
elaboratore con poche righe di istruzioni in un qualsiasi linguaggio di
programmazione.
Per calcolare un integrale definito è sufficiente indicare, oltre alla variabile,
l’intervallo di integrazione
> Int(f(x),x=0..2);
Z 2
x e(x+1) dx
0
>
value(%);
e3 + e
Consideriamo ora una classico esempio di funzione le cui primitive non
possono essere scritte a partire da funzioni elementari utilizzando operazioni
algebriche o composizione di funzioni:
> g:=x->exp(-x^2);
g := x → e(−x
2
)
calcoliamone l’integrale generalizzato nell’intervallo tra 0 e più infinito
> int(g(x),x=0..+infinity);
1√
π
2
che è corretto. Se vogliamo calcolare lo stesso integrale tra 0 e 1, ad esempio,
Maple ovviamente non puù far molto:
> int(g(x),x=0..5);
√
1
erf(5) π
2
dove erf ( x ) = 2/sqrt(Pi) * int ((exp(-xˆ2), t = 0..x ). Quindi Maple si mangia
la coda, e non poteva che
essere cosı̀. Possiamo perù chiedere una valutazione numerica dell’integrale:
> evalf(value(%));
.8862269255
4
4. Funzioni di più variabili e grafici
4.1
4.1 Limiti e derivate
Vediamo molto velocemente come quanto visto per le funzioni di una sola
variabile reale si estenda al caso di due variabili; l’estensione a più variabili
risulterà poi del tutto ovvia. Cominciamo definendo una funzione nelle
variabili x e y:
> f:=(x,y)->x^2*y-3*y/x;
3y
f := (x, y) → x2 y −
x
Proviamo a calcolare due limiti (facili)
> limit(f(x,y),{x=2,y=3});
15
2
>
limit(f(x,y),{x=0,y=3});
undefined
Se tentiamo con altre funzioni facciamo presto a mettere in difficoltà Maple.
La funzione
> g:=(x,y)->x^2*y^2/(x^2*y^2+(x-y)^2);
g := (x, y) →
x2 y 2
x2 y 2
+ (x − y)2
non ha limite in (0,0), Maple non risponde:
> limit(g(x,y),{x=0,y=0});
limit(
x2 y 2
, {x = 0, y = 0})
x2 y2 + (x − y)2
Ma anche nel caso di una funzione con limite 0 in (0,0) Maple si arena
> limit(x^3/(x^2+y^2),{x=0,y=0});
limit(
x3
, {x = 0, y = 0})
x2 + y 2
Non ci sono ovviamente problemi nel calcolo delle derivate parziali:
> diff(g(x,y),x);
x2 y2 (2 x y2 + 2 x − 2 y)
x y2
−
2
+ (x − y)
(x2 y2 + (x − y)2 )2
simplify(diff(g(x,y),y));
2
>
x2 y 2
2
x3 y (x − y)
(x2 y2 + x2 − 2 x y + y2 )2
Ed ecco una derivata seconda mista
> simplify(diff(g(x,y),x,y));
2
x2 y2 (6 x y − 3 x2 + x2 y2 − 3 y2 )
(x2 y2 + x2 − 2 x y + y2 )3
Per valutare in un punto la derivata parziale seconda appena calcolata
definiamo la funzione
> DDXY:=unapply(%,(x,y));
DDXY := (x, y) → 2
x2 y2 (6 x y − 3 x2 + x2 y2 − 3 y2 )
(x2 y2 + x2 − 2 x y + y2 )3
che ora possiamo valutare dove ci interessa, ad esempio in (1,-1):
> DDXY(1,-1);
−22
125
4.2
4.2 Grafici
Le possibilità grafiche sono uno dei punti forti dei programmi tipo Maple.
Analizziamo prima alcune possibilità non ancora esplorate riguardo alle
funzioni di una sola variabile. Passeremo poi alle funzioni di due variabili.
4.2.1
>
4.2.1 Alcune potenzialità grafiche per funzioni di una sola variabile
plot(sin(x),x=0..2*Pi,title="funzione seno");
funzione seno
1
0.5
0
1
2
3
x
4
5
6
–0.5
–1
Possiamo anche tentare una compressione dell’asse delle x per avere un’idea
del comportamento della funzione per valori molto grandi dell’argomento
> plot(sin(x),x=0..infinity);
0
x
infinity
Ma attenzione all’interpretazione delle proprietà geometriche del grafico.
Guardate cosa accade del grafico della funzione logaritmo in base e (che è una
funzione concava!):
> plot(ln(x),x=0..infinity);
infinity
0
x
infinity
-infinity
Per utilizzare potenzialità grafiche disponibili solo a domanda, chiediamo a
Maple di caricare il modulo plots:
> with(plots);
Warning, the name changecoords has been redefined
[animate, animate3d , animatecurve, arrow, changecoords, complexplot, complexplot3d ,
conformal, conformal3d, contourplot, contourplot3d , coordplot, coordplot3d ,
cylinderplot, densityplot, display, display3d , fieldplot, fieldplot3d, gradplot,
gradplot3d , implicitplot, implicitplot3d, inequal , listcontplot, listcontplot3d,
listdensityplot, listplot, listplot3d, loglogplot, logplot, matrixplot, odeplot , pareto,
pointplot, pointplot3d, polarplot, polygonplot, polygonplot3d ,
polyhedra supported, polyhedraplot, replot, rootlocus, semilogplot, setoptions,
setoptions3d , spacecurve, sparsematrixplot, sphereplot, surfdata, textplot,
textplot3d , tubeplot]
Possiamo ora utilizzare tutti i comandi elencati sopra. Per esempio possiamo
utilizzare le coordinate polari
> polarplot(1,theta=0..2*Pi);
1
0.5
–1
–0.5
1
0.5
–0.5
–1
Per un confronto tra coordinate cartesiane e polari prendiamo la curva di
equazioni parametriche x=t, y=t in coordinate cartesiane
> plot([t,t,t=-2*Pi..2*Pi]);
6
4
2
–6
–4
–2
0
2
4
6
–2
–4
–6
e in coordinate polari, vale a dire interpretando la prima coordinata come la
distanza dall’origine e la seconda coordinata come angolo rispetto al semiasse
positivo delle ascisse
> polarplot([t,t,t=-2*Pi..2*Pi]);
1
–6
–4
–2
2
4
6
0
–1
–2
–3
–4
Veniamo ora ad un grafico in scala semilogaritmica
> logplot(10^x,x=0..10);
1e+10
1e+09
1e+08
1e+07
1e+06
1e+05
.1e5
.1e4
.1e3
.1e2
1.
2
4
x
6
8
e ad una curva definita mediante una equazione ben nota
> implicitplot(x^2+y^2=1,x=-1..1,y=-1..1);
10
1
y
–1
0.5
0
–0.5
1
0.5
x
–0.5
–1
Quest’ultimo comando è particolarmente utile quando si hanno curve ”meno
note” che Maple traccia
senza protestare
> implicitplot(exp(x^2*y)-exp(y)=sin(4*x),x=-2..2,y=-5..5);
4
y
2
–1.5
–1
–0.5 0
–2
–4
0.5
1
x
1.5
2
4.2.2
4.2.2 Grafici per funzioni di due variabili
Vediamo alcuni esempi di grafici in tre dimensioni ottenuti con il comando
plot3d e come tracciare le curve di livello.
> plot3d(x^2+y^2,x=-4..4,y=-4..4,style=patch,color=x,grid=[50,50],orien
> tation=[160,100],axes=boxed,title=‘paraboloide‘);
paraboloide
30
25
20
15
10
5
0
>
4 2
0
–2
x
–4
2
0
y
–2
–4
contourplot(x^2+y^2,x=-5..5,y=-5..5,contours=25);
4
y
2
–4
–2
0
–2
–4
2
x
4
>
>
plot3d(y^2-x^2,x=-4..4,y=-4..4,style=contour,contours=50,grid=[50,50]
,orientation=[200,60],color=black,axes=boxed,title=‘sella‘);
sella
15
10
5
0
–5
–10
–15
4
4
>
–2
2
0
y
–2
–4
–4
densityplot(x^2+y^2,x=-5..5,y=-5..5);
4
y
2
–4
–2
0
–2
–4
2
x
4
2
0 x
5
5. Algebra delle matrici
Esaminiamo ora alcune delle possibilità che Maple offre nel calcolo matriciale.
Anzitutto vi sono due pacchetti che possiamo richiamare per operare sulle
matrici e, più in generale, su probelmi di algebra lineare: linalg eLinearAlgebra .
Qui esamineremo solo alcune delle possibilità offerte dal primo che andiamo a
caricare:
> with(linalg);
Warning, the protected names norm and trace have been redefined and
unprotected
[BlockDiagonal , GramSchmidt, JordanBlock , LUdecomp, QRdecomp, Wronskian, addcol,
addrow, adj , adjoint, angle, augment , backsub, band , basis, bezout , blockmatrix,
charmat, charpoly, cholesky, col, coldim, colspace, colspan, companion, concat,
cond , copyinto, crossprod, curl, definite, delcols, delrows, det, diag, diverge,
dotprod, eigenvals, eigenvalues, eigenvectors, eigenvects, entermatrix, equal,
exponential , extend , ffgausselim, fibonacci , forwardsub, frobenius, gausselim,
gaussjord, geneqns, genmatrix , grad, hadamard , hermite, hessian, hilbert,
htranspose, ihermite, indexfunc, innerprod, intbasis, inverse, ismith, issimilar,
iszero, jacobian, jordan, kernel, laplacian, leastsqrs, linsolve, matadd , matrix ,
minor, minpoly, mulcol, mulrow, multiply, norm, normalize, nullspace, orthog,
permanent, pivot, potential, randmatrix , randvector , rank , ratform, row, rowdim,
rowspace, rowspan, rref , scalarmul, singularvals, smith, stackmatrix, submatrix ,
subvector, sumbasis, swapcol, swaprow, sylvester, toeplitz, trace, transpose,
vandermonde, vecpotent, vectdim, vector, wronskian]
Definiamo anzitutto due vettori di 3 componenti:
>
v:= vector(3,[1,2,3]);w:=vector(3,[5,6,7]);
v := [1, 2, 3]
w := [5, 6, 7]
Possiamo ora effettuare tutte le operazioni tra vettori.
Partiamo con la somma:
> v+w;
v+w
Per vedere esplicitamente il risultato dobbiamo valutare l’espressione con
l’istruzione evalm:
>
evalm(%);
[6, 8, 10]
Ecco invece la moltiplicazione per uno scalare
> evalm(3*w);
[15, 18, 21]
Il prodotto scalare, o prodotto interno, si scrive invece
> innerprod(v,w);
38
Passiamo alle matrici. Definiamo una matrice 2 per 3 e chiamiamola A:
>
A := matrix( 2, 3, [[1,3,2], [-5,14,9]]);
1 3 2
A :=
−5 14 9
una matrice 3 per 3 di nome B:
>
B := matrix( 3, 3, [[1,-1,1], [0,20,-8], [2,0,2]]);


1 −1
1
B :=  0 20 −8 
2
0
2
e una seconda matrice 2 per 3 di nome C:
> C := matrix( 2, 3, [[0,5,2], [1,2,-1]]);
0 5
2
C :=
1 2 −1
e facciamo un po’ di operazioni tra matrici:
>
>
evalm(A+C);
evalm(2*A);
1 8 4
−4 16 8
2 6 4
−10 28 18
Per l’operazione di moltiplicazione tra matrici (righe per colonne) dobbiamo
usare il simbolo &* :
>
evalm(A&*B);
5 59 −19
13 285 −99
La trasposta e l’inversa richiedono solo l’utilizzo dei corrispondenti termini
inglesi:
> transpose(A);


1 −5
 3 14 
2
9
>
BINV:=inverse(B);
 5
 2


BINV :=  −1


−5
2
1
8
0
−1
8
−3
4
1
2
5
4







Ha fatto bene i conti? Controlliamo:
> evalm(B&*BINV);


1 0 0
 0 1 0 
0 0 1
OK!
Ancora, possiamo calcolare determinante e rango:
> det(B);
16
> rank(A);
2
Naturalmente ci vengono segnalati gli errori nell’impostare i calcoli. Ad
esempio non è possibile moltiplicare B per A:
> evalm(B&*A);
Error, (in linalg[multiply]) non matching dimensions for vector/matrix
product
Data la matrice A ed il vettore dei termini noti
> b:= vector(2,[1,2]);
b := [1, 2]
il sistema Ax=b si risolve con l’istruzione linsolve:
>
x:=linsolve(A,b);
5
1
29
7
x :=
+
−
t 1 , t 1,
t1
19 19
19 19
(in questo caso i vettori soluzione sono infiniti e si ottengono al variare di t1
nell’insieme dei numeri reali).
Possiamo ottenere il valore della prima componente del vettore soluzione
> x[1];
1
5
+
t1
19 19
che in corrispondenza di un fissato valore del parametro
> _t[1]:=1;
t 1 := 1
diviene
>
x[1];
6
19
Vediamo ora come ottenere la matrice inversa della matrice B in base alla
definizione: l’inversa, se esiste, risolve l’equazione tra matrici BX=I dove I è la
matrice identità. Definiamo la matrice identità come una matrice diagonale :
> ID:=diag(1,1,1);


1 0 0
ID :=  0 1 0 
0 0 1
Quindi risolviamo il sistema
>
BINVbis:=linsolve(B,ID);

5
2



BINVbis :=  −1


−5
2
1
8
0
−1
8
−3
4
1
2
5
4







che è l’inversa trovata prima da Maple, possiamo farlo controllare a Maple:
> evalm(BINV-BINVbis);


0 0 0
 0 0 0 
0 0 0
la loro differenza è zero quindi coincidono.
6
6. Introduzione alla programmazione
6.1
6.1.1
6.1 Procedure
6.1.1 Introduzione
Spesso nella risoluzione dei problemi accade di dovere eseguire più volte una o
più istruzioni in un ordine stabilito. Si supponga ad esempio di dover calcolare
il montante di una operazione finanziaria oppure un tasso di interesse: per
risolvere diversi problemi del genere cambiano i dati, ma le formule rimangono
sempre le stesse. Sarebbe quindi opportuno evitare di dovere riscrivere le
formule ogni volta, ma memorizzarle una volta per tutte e riutilizzarle quando
servono (risparmiando tempo e fatica). Questa opportunità è offerta dalle
procedure, vediamo come realizzarla.
6.1.2
6.1.2 Un esempio di procedura
Supponiamo di dover calcolare il montante M di un capitale C in regime di
interesse semplice, dopo t anni e con tasso d’interesse annuo i :
M = C*(1 + i*t)
I dati da considerare in ingresso ed in uscita sono, rispettivamente:
• dati in ingresso: C (capitale iniziale), t (durata in anni) e i (tasso di
interesse annuo)
• dato in uscita: M (montante)
Dichiarazione di un procedura
Per poter riutilizzare più volte la formula senza avere la necessità di riscriverla
ogni volta, possiamo dichiarare una procedura che esegua la formula del calcolo
del montante comunque vengano specificati i dati in ingresso.
A tale scopo occorre specificare:
• un nome da assegnare alla sequenza di calcoli da effettuare (il nome della
procedura)
• un ”avviso” per MAPLE che si tratta di una procedura
• quali sono i dati in ingresso
• come calcolare il risultato
La dichiarazione di una procedura che consenta il calcolo del montante è:
>
>
>
montante_IntSempl := proc(C, t, i)
C*(1+i*t)
end proc;
montante IntSempl := proc(C, t, i) C ∗ (1 + i ∗ t) end proc
dove, nell’ordine, sono specificati:
• ”montante IntSempl”: è il nome che abbiamo scelto per la procedura ed
è seguito dall’operatore di assegnazione (:=); è buona regola utilizzare dei
nomi significativi che ci aiutino a ricordare ”cosa fa” la procedura;
• ”proc” è una parola chiave: serve ad avvisare MAPLE che si tratta di una
procedura;
• di seguito sono specificati (tra parentesi tonde e separati da virgole) i
dati in ingresso che sono detti parametri (o argomenti) formali della
procedura; nel nostro esempio gli argomenti sono 3: il capitale C, il tempo
t ed il tasso di interesse i. Anche in questo caso la scelta dei nomi e
dell’ordine degli argomenti è libera;
• la formula che calcola il montante.
Per indicare la fine della dichiarazione della procedura si scrive end oppure
end proc. Se nella dichiarazione non ci sono errori di sintassi, MAPLE
riscrive la procedura (in blu) dopo l’end finale.
Esecuzione di una procedura
Una volta dichiarata una procedura, per eseguirla basta scriverne il nome
seguito dai valori che intendiamo assegnare agli argomenti: un’istruzione di
questo genere è detta chiamata della procedura ed i valori passati sono
detti parametri (o argomenti) attuali della chiamata.
Ad esempio, per calcolare il montante corrispondente a C = 1000 euro, t = 10
anni e i = 4% annuo, è sufficiente scrivere la chiamata avente 1000, 10 e 0.04
come argomenti attuali:
> montante_IntSempl(1000, 10, 0.04);
1400.00
Il valore stampato 1400.00 è il montante richiesto e viene restituito dalla
procedura perchÈ è il risultato dell’ultima istruzione eseguita (quella prima
dell’end).
Per effettuare un’altra esecuzione, ad esempio con C = 3500 euro , t = 9 anni
e i = 5% annuo, basta scrivere un’altra chiamata:
> montante_IntSempl(3500, 9, 0.05);
5075.00
Alcune raccomandazioni: è vero che il numero e l’ordine degli argomenti
formali lo scegliamo noi arbitrariamente, ma una volta fatte, queste scelte
vanno rispettate quando si effettuano le chiamate. Ad esempio, la chiamata:
> montante_IntSempl(9, 3500, 0.05);
1584.00
dà un risultato diverso rispetto alla chiamata precedente: giustamente, perchÈ
pur trattandosi degli stessi valori, gli argomenti attuali sono scritti in ordine
diverso. Infatti, nella dichiarazione della procedura si era deciso che il primo
argomento fosse il capitale, e perciù nell’ultima chiamata viene calcolato il
montante di un capitale di 9 euro dopo 3500 anni al tasso del 5%. Dunque:
l’ordine degli argomenti attuali deve corrispondere all’ordine degli argomenti formali.
Vediamo ora cosa accade se togliamo un argomento attuale dalla chiamata:
> montante_IntSempl(1000, 10);
Error, (in montante_IntSempl) montante_IntSempl uses a 3rd argument,
i, which is missing
è data una segnalazione d’errore perchÈ nella dichiarazione della procedura gli
argomenti formali erano 3, mentre in questa chiamata gli argomenti attuali
sono solo 2 (in questo caso MAPLE non sa quanto vale il tasso i); dunque:
il numero degli argomenti attuali deve corrispondere al numero degli argomenti formali.
Una chiamata di procedura restituisce un risultato (lo stesso che viene
stampato) che puù essere assegnato ad una variabile: ad esempio la chiamata
> montante_IntSempl(1200,5,0.03);
1380.00
restituisce il valore 1380 che puù essere assegnato ad una variabile:
> montante := montante_IntSempl(1200,5,0.03);
montante := 1380.00
se quindi scriviamo il nome della variabile è visualizzato il dato in essa
memorizzato:
> montante;
1380.00
Una procedura puù anche essere priva di argomenti, ad esempio:
> prova := proc()
> print("ciao a tutti")
> end;
prova := proc() print(“ciao a tutti”) end proc
la procedura ha come unica istruzione una stampa (print) che si limita a
stampare la stringa (= sequenza di simboli alfanumerici) racchiusa tra doppi
apici.
Anche se non ci sono argomenti le parentesi nella chiamata vanno messe:
> prova();
“ciao a tutti”
6.1.3
6.1.3 Un altro esempio di procedura
Operiamo questa volta di operare in regime di interesse composto, in tal caso,
noti il capitale iniziale C, la durata t dell’impiego ed il tasso d’intersse annuo
i, il montante M è dato da:
M = C*(1 + i)ˆt
Supponiamo in particolare di volere determinare, dati il montante, la durata t
ed il tasso di interesse i, il capitale iniziale e l’interesse corrispondenti, ovvero:
C = M / (1 + i)ˆt
e
I=M-C.
In questo caso i dati del problema sono:
• dati in ingresso: M (montante), t (durata in anni) e i (tasso di interesse
annuo)
• dati in uscita: C (capitale iniziale) e I (interesse)
La differenza sostanziale rispetto al caso precedente è che in questo caso i dati
in uscita sono 2 e non uno solo.
Proviamo a dichiarare la procedura come segue:
> capInter_IntComp := proc(M, t, i)
> M/(1+i)^t;
> M-C
> end;
capInter IntComp := proc(M, t, i) M (i + 1)t ; M − C end proc
nessuna segnalazione d’errore, ma ciù non significa che la procedura (quando
eseguita) fornisca i risultati corretti; infatti, se proviamo ad effettuare una
chiamata della procedura (con M = 2000 euro, t = 3 anni e i = 11 %):
> capInter_IntComp(2000,3,0.11);
2000 − C
C’è qualcosa che non va: la procedura dovrebbe stampare 2 numeri e invece
stampa un’espressione con una C di troppo: come mai ? L’espressione
rappresenta il montante (che è 2000, giusto) e corrisponde all’ultima istruzione
(prima dell’end), ma al posto della variabile C dovrebbe esserci il valore del
capitale calcolato nell’istruzione che precede. Già, ma noi non abbiamo
specificato che il risultato di tale istruzione sia memorizzato in C; per farlo
aggiungiamo una assegnazione nella seconda riga e torniamo a dichiarare la
procedura:
> capInter_IntComp := proc(M, t, i)
> C := M/(1+i)^t;
> M-C;
> end;
Warning, ‘C‘ is implicitly declared local to procedure
‘capInter_IntComp‘
capInter IntComp := proc(M, t, i) local C; C := M (i + 1)t ; M − C end proc
La nuova dichiarazione dà una segnalazione di ”Warning” (è un tipo di errore
non grave che consente comunque l’esecuzione della procedura), tale
segnalazione ci avverte che la variabile C è definita implicitamente, ovvero è
utilizzata nella procedura senza essere variabile di input e senza che Maple sia
stato avvisato in altro modo del suo utilizzo. Evidentemente Maple preferisce
una dichiarazione esplicita delle variabili utilizzate. La riscrittura della
procedura fatta da Maple ci suggerisce infatti quali sono le sue preferenze;
d’accordo, copiamo e riproviamo:
> capInter_IntComp := proc(M, t, i) local C;
> C:=M/(1+i)^t;
> M-C
> end;
capInter IntComp := proc(M, t, i) local C; C := M (i + 1)t ; M − C end proc
La segnalazione di Warning è scomparsa; rifacciamo la chiamata:
> capInter_IntComp(2000,3,0.11);
537.617237
Benino: questo è il valore corretto dell’interesse, ma dov’è il valore del capitale
(la procedura nelle nostre intenzioni dovrebbe avere 2 valori in uscita ) ?
La risposta è: l’output fornito automaticamente è un solo numero; infatti in
Maple una procedura, salvo indicazioni diverse, restituisce un solo numero
ovvero il risultato dell’ultima istruzione.
Per visualizzare il valore del capitale è sufficiente aggiungere un’istruzione di
stampa:
> capInter_IntComp := proc(M, t, i) local C;
> C:=M/(1+i)^t;
> print(C);
> M-C
> end;
capInter IntComp := proc(M, t, i) local C; C := M (i + 1)t ; print(C) ; M − C end proc
nessuna segnalazione d’errore; proviamo ad eseguire:
> capInter_IntComp(2000,3,0.11);
1462.382763
537.617237
ora ci siamo, i due valori sono rispettivamente il capitale (stampato con
l’istruzione print) e l’interesse (fornito come output automaticamente essendo
stato calcolato con l’ultima istruzione della procedura).
Un modo, di più chiara leggibilità, per ottenere entrambi i valori è quello di
stabilire esplicitamente qual’è l’output della procedura utilizzando l’istruzione
return, la cui sintassi è return(espressione). Cambiamo allora nuovamente la
dichiarazione della procedura:
> capInter_IntComp := proc(M, t, i) local C;
> C:=M/(1+i)^t;
> return(C,M-C);
> end;
capInter IntComp :=
proc(M, t, i) local C; C := M (i + 1)t ; return C, M − C end proc
Cosı̀ la chiamata della procedura fornisce come output un vettore di due
numeri:
> capInter_IntComp(2000,3,0.11);
1462.382763, 537.617237
A tale output possiamo anche assegnare un nome, ad esempio
> risultato:=capInter_IntComp(2000,3,0.11);
risultato := 1462.382763, 537.617237
In questo modo i numeri ottenuti all’interno della procedura possono essere
utilizzati come fossero due variabili qualsiasi all’esterno della procedura stessa;
ad esempio con
> risultato[1]+risultato[2];
2000.000000
ricalcoliamo il montante M.
Ancora un’osservazione circa i ”;” (”punti e virgola”): nell’ultima versione la
procedura ha 3 istruzioni (calcolo di C, stampa di C e calcolo dell’interesse)
ed il ”;” c’è dopo le prime due istruzioni ma non dopo la terza. La ragione sta
nel fatto che il ”;” serve a separare 2 istruzioni successive; quindi è facoltativo
metterlo dopo l’ultima istruzione (quella prima dell’end) perchÈ l’end non è
considerata un’istruzione, mentre è obbligatorio negli altri casi e se manca è
segnalato un errore. Se ad esempio togliamo il ”;” dopo la prima istruzione:
> capInter_IntComp := proc(M, t, i) local C;
> C:=M/(1+i)^t;
> print(C);
> M-C
> end;
capInter IntComp := proc(M, t, i) local C; C := M (i + 1)t ; print(C) ; M − C end proc
è restituito un errore di sintassi e la dichiarazione della procedura non è stata
accettata da MAPLE.
6.2
6.2.1
6.2 Istruzioni condizionali
6.2.1 L’istruzione if-then-else
Supponiamo di voler calcolare il periodo di tempo per cui deve essere investito
un capitale C in modo da ottenere il montante M se gli interesse sono
calcolati al tasso annuo i con capitalizzazione composta. Una procedura che
calcola la durata dell’investimento è ad esempio:
> tempo_IntComp := proc(M, C, i)
> log[1+i](M/C);
> end proc;
tempo IntComp := proc(M, C, i) logi+1 (M C) end proc
([i+1] indica la base del logaritmo).
Quindi, se M = 8000 Ä, C = 5000 Ä e i = 4 %, la durata dell’impiego, in
anni, deve essere:
>
tempo_IntComp(8000,5000,0.03);
15.90063174
Proviamo un’altra chiamata della procedura:
> tempo_IntComp(8000,0,0.03);
Error, (in tempo_IntComp) numeric exception: division by zero
Errore: cos’è successo? Rispetto alla chiamata precedente il capitale (che è il
secondo argomento attuale) è uguale a zero, ma nella procedura si divide per il
capitale C e quindi occorrerebbe controllare che C non sia nullo. Meglio
ancora sarebbe controllare che il capitale utilizzato nei calcoli sia positivo: per
C minore o uguale a zero sarebbe simpatico stampare un messaggio di errore.
Per effettuare questo controllo si puù utilizzare un’istruzione condizionale che
lavori nel seguente modo:
- se C <= 0 allora:
- stampa un messaggio di errore
- altrimenti:
- calcola il tempo di impiego del capitale
Per realizzare questo controllo utilizziamo l’istruzione
if-then-else di Maple. Si tratta in realtà di un’istruzione presente con analoga sintassi in tutti i
> tempo_IntComp := proc(M, C, i)
> if C <= 0 then
> print("il capitale dev’essere positivo")
> else
> log[1+i](M/C)
> end if
> end proc;
tempo IntComp := proc(M, C, i)
if C ≤ 0 then print(“il capitale dev’essere positivo”) else logi+1 (M C) end if
end proc
Se ora rifacciamo la chiamata con capitale nullo otteniamo:
> tempo_IntComp(8000, 0, 0.03);
“il capitale dev’essere positivo”
altrimenti:
> tempo_IntComp(8000, 1500, 0.03);
56.63207933
In generale la sintassi dell’istruzione condizionale if-then-else è la seguente:
if espressione boolean then
sequenza 1 di istruzioni MAPLE
else
sequenza 2 di istruzioni MAPLE
end if
e lavora nel seguente modo:
- valuta l’espressione boolean (un’espressione boolean è un’espressione il cui
risultato puù essere soltanto VERO
oppure FALSO; nell’esempio l’espressione boolean era: C <= 0)
- se il risultato dell’espressione boolean è VERO allora:
- esegui la sequenza 1 di istruzioni MAPLE
- altrimenti:
- esegui la sequenza 2 di istruzioni MAPLE
Si sarebbe anche potuto scrivere la procedura nel seguente modo: se C è
maggiore di zero allora calcola la durata, altrimenti stampa un messaggio; in
tal caso la procedura diventa:
> tempo2_IntComp := proc(M, C, i)
> if C > 0 then
> log[1+i](M/C)
> else
> print("il capitale dev’essere positivo")
> end
> end ;
tempo2 IntComp := proc(M, C, i)
if 0 < C then logi+1 (M C) else print(“il capitale dev’essere positivo”) end if
end proc
e lavora sostanzialmente nello stesso modo della procedura precedente
tempo IntComp; proviamo come esempio le chiamate con gli stessi argomenti:
> tempo2_IntComp(8000, 1500, 0.03);
56.63207933
> tempo2_IntComp(8000, 0, 0.03);
“il capitale dev’essere positivo”
Si osservi che nell’ultima procedura la fine dell’istruzione if-then-else è stata
segnalata da end (il primo) anzichÈ da end if e, analogamente, end proc è
stata sostituita da end (quello dell’ultima riga). In realtà infatti per Maple le
parole proc ed if (e altre che utilizzeremo tra poco) indicano a Maple l’inizio
di un particolare gruppo di istruzioni e sono come delle ”parentesi aperte”:
ciascun end è come una ”parentesi chiusa” corrispondente all’ultima
”parentesi aperta”.
In altri linguaggi di programmazione la ”parentesi aperta” è indicata
esplicitamente dalla parola begin, in altri ancora anzichÈ le parole begin - end
si utilizzano proprio una parentesi graffa aperta e una parentesi graffa chiusa.
6.2.2
6.2.2 L’istruzione if
Riprendiamo l’esempio della sezione precedente: se si desidera fare il controllo
che C sia non nullo, ma non interessa la stampa del messaggio, allora si puù
utilizzare l’istruzione if:
> tempo3_IntComp := proc(M, C, i)
> if C > 0 then
> log[1+i](M/C)
> end if
> end;
tempo3 IntComp := proc(M, C, i) if 0 < C then logi+1 (M C) end if end proc
in questo caso se C <= 0 non viene stampato alcun messaggio:
> tempo3_IntComp(8000,0,3.5);
In generale la sintassi dell’istruzione condizionale if è la seguente:
if espressione boolean then
sequenza di istruzioni MAPLE
end if
e lavora nel seguente modo:
- valuta l’espressione boolean (nell’esempio è C <> 0)
- se il risultato dell’espressione boolean è VERO allora:
- esegui la sequenza di istruzioni MAPLE
6.2.3
6.2.3 Espressioni boolean
Nell’esempio della sezione 2.2 (calcolo del tempo di impiego di un capitale C al
tasso di interesse composto i per ottenere un montante M) si è inserito un
controllo che C sia maggiore di zero:
> tempo3_IntComp := proc(M, C, i)
> if C > 0 then
> log[1+i](M/C)
> end if
> end;
tempo3 IntComp := proc(M, C, i) if 0 < C then logi+1 (M C) end if end proc
ma se esaminiamo il campo di esistenza dell’espressione log[i+1](M/C)
(supponendo il tasso di interesse i costante) ci rendiamo conto che il controllo
C > 0 non è sufficiente, occorre anche che M/C (l’argomento del logaritmo)
sia > 0 ovvero, in altri termini, il montante M dev’essere positivo, cioÈ si deve
avere:
M>0eC>0
evidentemente questa è un’espressione boolean (puù solo essere o VERA o
FALSA) quindi, per realizzare il controllo, basta inserire l’espressione in
un’istruzione if salvo sostituire la congiunzione e con la corrispondente and
dell’inglese (l’oppure si sostituisce invece con or):
> tempo4_IntComp := proc(M, C, i)
> if M > 0 and C > 0 then
> log[1+i](M/C)
> end if
> end;
tempo4 IntComp :=
proc(M, C, i) if 0 < M and 0 < C then logi+1(M C) end if end proc
Gli operatori or (somma logica) e and (prodotto logico) sono detti
operatori boolean e lavorano in modo simile agli usuali operatori aritmetici
+ e *, salvo che se scriviamo x + y, le variabili x ed y devono rappresentare
dei numeri, mentre se scriviamo x or y, le variabili x ed y devono assumere
solo i valori true (VERO) oppure false (FALSO).
La descrizione completa degli operatori or e and è data dalle loro tabelle di
verità in cui sono forniti i risultati per tutti i possibili valori degli operandi.
L’opertore or dà come risultato FALSO solo se entrambi gli operandi sono
falsi, quindi la sua tabella di verità è:
x y x or y
false false false
false true true
true false true
true true true
invece l’opertore and dà come risultato VERO solo se entrambi gli operandi
sono veri e la sua tabella di verità è:
x y x and y
false false false
false true false
true false false
true true true
un altro operatore boolean è il not (negazione logica), la cui tabella di
verità è:
x not x
false true
true false
Gli operatori boolean or, and e not insieme con i valori false e true
costituiscono un’algebra (analoga a quella usuale sui numeri reali) detta
algebra di Boole.
Nell’algebra di Boole si possiamo valutare espressioni in cui si utilizzano solo
gli operatori boolean e le variabili possono assumere solo i valori true e false;
espressioni di questo tipo sono dette espressioni boolean. Un esempio di
espressione boolean è:
x or y and (x or y and not x)
Naturalmente per valutare un’espressione boolean dobbiamo sapere in quale
ordine si devono valutare gli operatori; ad esempio per valutare l’espressione
x or y and not x
qual’operatore da valutare per primo ? E’ l’or, l’and o il not ? L’ordine di
valutazione (o più precisamente la priorità degli operatori bobolean) è:
not, and, or (cosı̀ come nell’algebra sui numeri reali la priorità è: elevamento
a potenza, prodotto, somma).
6.3
6.3.1
6.3 Istruzioni iterative
6.3.1 L’istruzione while
Nella risoluzione di un problema puù accadere che un gruppo di istruzioni
debba essere ripetuto finchÈ continua a verificarsi una determinata condizione.
Consideriamo ad esempio il seguente problema: si investe un capitale C in
un’operazione finanziaria al tasso di interesse annuo i (capitalizzazione
composta) e si desiderano calcolare tutti i montanti dopo 1 anno, dopo 2 anni,
eccetera, finchÈ il montante non abbia raggiunto un certo valore Mfin.
Desideriamo cioè calcolare e stampare gli importi dei montanti:
C*(1+i)ˆ1, C*(1+i)ˆ2, C*(1+i)ˆ3, . . .C*(1+i)ˆn
continuando finchÈ continua a valere la condizione C*(1+i)ˆn < Mfin .
I dati del problema sono in questo caso:
• dati in ingresso: C (capitale iniziale), i (tasso di interesse annuo) e Mfin
(importo finale del montante)
• dati in uscita: i montanti dopo 1 anno, 2 anni, 3 anni, ... finchÈ C*(1+i)ˆn
< Mfin
Per effettuare la stampa dobbiamo perciù riutilizzare sempre la stessa formula
ma per valori diversi del numero di anni, che indicheremo con n; i passi da
eseguire sono perciù:
- inizializza n (numero di anni) ad 1
- finchÈ C*(1+i)ˆn < Mfin esegui:
- stampa C*(1+i)ˆn
- aumenta n di 1
Si tratta ora di tradurre questi passi in istruzioni MAPLE tenendo conto che:
• inizializzare n ad 1 significa assegnare ad n il valore 1;
• il comando ”finchÈ ... esegui” si traduce con l’istruzione ”while ... do”
di Maple; anche con questa istruzione sarà necessario indicare la fine delle
istruzioni da ripetere con end do (oppure anche semplicemente con end);
• per aumentare n di 1 si utilizza l’operazione di assegnazione ”n := n + 1”
il cui effetto è quello di assegnare alla variabile n un nuovo valore uguale
a quello che aveva prima aumentato di 1.
Ecco dunque la procedura che esegue quanto richiesto:
>
>
>
>
>
>
>
montanti_IntComp := proc(C, i, Mfin) local n;
n:=1;
while C*(1+i)^n < Mfin do
print(C*(1+i)^n);
n:= n + 1;
end do
end;
montanti IntComp := proc(C, i, Mfin)
local n;
n := 1 ; while C ∗ (i + 1)n < Mfin do print(C ∗ (i + 1)n ) ; n := n + 1 end do
end proc
Si osservi che la variabile n è dichiarata locale per evitare la solita fastidiosa
segnalazione di Warning descritta in sez. 6.1.3.
Vediamo ad esempio quali sono i montanti anno dopo anno che consentono ad
un capitale C = 1000 Ä di raddoppiare al tasso annuo del 4 %:
> montanti_IntComp(1000, 0.04, 2000);
1040.00
1081.6000
1124.864000
1169.858560
1216.652902
1265.319018
1315.931779
1368.569050
1423.311812
1480.244285
1539.454056
1601.032219
1665.073507
1731.676448
1800.943506
1872.981246
1947.900496
18
L’ultimo numero stampato è il valore di n al termine della procedura (infatti
l’istruzione che aumenta n è l’ultima della procedura) e rappresenta il numero
di anni necessari per raggiungere Mfin = 2000 Ä. Si osservi che le istruzioni
all’interno del while sono state ripetute 17 volte, ciù si esprime dicendo che ci
sono state 17 iterazioni del ciclo while.
In generale l’istruzione while ha la seguente sintassi:
while espressione boolean do
sequenza di istruzioni MAPLE
end do
e impone a Maple le seguenti operazioni:
- valuta l’espressione boolean (nell’esempio è ”C*(1+i)ˆn < Mfin”)
- se il risultato dell’espressione boolean è VERO allora:
- esegui la sequenza di istruzioni MAPLE (detta ”corpo” dell’istruzione while)
- ripeti dall’inizio (valuta ..eccetera)
- altrimenti termina
Attenzione! Puù accadere, per errore, di scrivere come condizione per
l’esecuzione del corpo dell’istruzione while una espressione boolean sempre
vera: questo avviene ad esempio, se nella condizione dell’istruzione while della
procedura montanti IntComp si sostituisce
C*(1+i)ˆn < Mfin con - C*(1+i)ˆn < Mfin
In tal caso ripetendo la chiamata
montanti IntComp(1000, 0.04, 2000);
l’espressione boolean dell’istruzione while è sempre vera e il corpo
dell’istruzione viene continuamente ripetuto (per arrestare l’elaborazione
cliccare sul pulsante con l’indicazione di STOP). In questi casi si dice che si ha
un ciclo infinito o loop infinito.
6.3.2
6.3.2 L’istruzione for
Spesso quando alcune istruzioni devono essere ripetute più volte si conosce
preventivamente il numero esatto di ripetizioni richieste. In questi casi
conviene utilizzare l’istruzione for anzichè la while.
Partiamo anche in questo caso da un problema: supponiamo di voler calcolare
il fattoriale del numero n (ci dimentichiamo per un attimo che in Maple il
fattoriale di un numero n si calcola semplicemente scrivendo n!).
Costruiamo anche qui una procedura che contenga la nuova istruzione.
Abbiamo in questo caso:
• dato in ingresso: n
• dato in uscita: il fattoriale di n
Una procedura che calcola il fattoriale di un numero è:
>
>
>
>
>
>
>
>
>
fattoriale := proc(n) local m, fatt;
fatt:=1;
if n>0 then
for m from 1 to n do
fatt := fatt*m;
end do;
end if;
return(fatt)
end;
fattoriale := proc(n)
local m, fatt;
fatt := 1 ; if 0 < n then for m to n do fatt := m ∗ fatt end do end if ; return fatt
end proc
Che possiamo richiamare per calcolare il fattoriale di 6:
>
fattoriale(6);
720
La sintassi dell’istruzione for (nella sua forma più semplice) è:
for variabile indice from valore iniziale to valore finale do
sequenza di istruzioni MAPLE
end do
e lavora nel seguente modo:
- variabile indice := valore iniziale
- finchÈ variabile indice <= valore finale esegui:
- esegui la sequenza di istruzioni MAPLE
- variabile indice := variabile indice + 1
Un esempio più elaborato di utilizzo dell’istruzione for
Supponiamo di voler calcolare l’integrale definito:
noti gli estremi di integrazione a e b. Non esiste in questo caso alcun modo di
scrivere una primitiva della funzione integranda utilizzando composizioni di
funzioni elementari, possiamo perù calcolare un valore approssimato
dell’integrale applicando la definizione: approssimiamo cioè l’area
rappresentata dall’integrale con l’area di un plurirettangolo.
I dati del problema sono perciù:
• dati in ingresso: a e b (estremi dell’intervallo di integrazione) e m (numero
delle parti uguali in cui si intende suddividere l’intervallo di integrazione)
• dato in uscita: valore dell’integrale
Di seguito sono descritti i passi da eseguire per ottenere una soluzione del
problema:
- inizializzare a zero una variabile (area) in cui memorizzare la somma delle
aree dei rettangoli che costituiscono il
plurirettangolo
- inizializzare una variabile (delta) con il valore (b - a)/m (è la lunghezza di
ciascuna delle m parti in cui viene suddiviso l’intervallo [a,b])
- inizializzare con a (estremo sin. dell’intervallo di integrazione) una variabile
(x) che rappresenti il generico punto
x = a + n * delta al variare di n = 1, 2, , ..., m
- per n = 1, 2, , ..., m esegui:
- calcola il nuovo valore di x aggiungendo delta al valore attuale di x
- calcola il nuovo valore di area aggiungendo l’area del rettangolo di base
delta ed altezza exp(xˆ2), al valore
attuale di area
- restituisci il valore di area (area del plurirettangolo) calcolato
che tradotto in istruzioni MAPLE diventa:
> integrale := proc(a,b,m) local n, area, x, delta;
> area := 0;
> delta := (b - a) / m;
> x := a;
> for n from 1 to m do
> x := x + delta;
> area := area + delta*exp(x^2);
> od;
> return(evalf(area))
> end;
integrale := proc(a, b, m)
local n, area, x, δ;
area := 0 ;
δ := (b − a) m ;
x := a ;
for n to m do x := δ + x ; area := area + δ ∗ exp(x2 ) end do ;
return evalf(area)
end proc
Osservazioni:
• il comando evalf (espressione) restituisce il valore di un’espressione (reale)
• al solito le variabili che non sono argomenti formali della procedura (n,
area, x e delta) sono dichiarate come variabili locali per evitare la segnalazione di Warning
• l’istruzione for incrementa automaticamente la variabile n
Proviamo a fare qualche chiamata: integrando nell’intervallo [1,3] con m =
1000:
> integrale(1,3,1000);
1451.199053
... con m = 2000:
> integrale(1,3,2000);
1447.136700
... e con m = 3000:
> integrale(1,3,3000);
1445.784397
si osservi che il valore approssimato dell’integrale diminuisce, infatti
l’approsimazione è fatta per eccesso dato che il valore x è l’estremo destro dei
sotto-intervalli e che la funzione integranda è strettamente crescente. Per
ottenere un’approsimazione per difetto basta far variare n da 0 ad m - 1.
Naturalmente a Maple di calcolare l’integrale utilizzando il comando int di
Maple:
> evalf(int(exp(x^2),x=1..3));
1443.082472
Scarica

Introduzione a ”MAPLE”