Introduzione a Mathematica

Gianluca Gorni
Università di Udine, Dipartimento di Matematica e Informatica

11. Operazioni concise sulle liste

Ultima modifica: 7 aprile 2003

Pagina internet utile!

Prodotto e somma di una lista con un numero

Se ho un vettore v = ((Subscript[v, 1]), (Subscript[v, 2]), …, (Subscript[v, n])) è usuale in matematica scrivere 2  v per il vettore (2  (Subscript[v, 1]), 2  (Subscript[v, 2]), …, 2  (Subscript[v, n])). Con Mathematica si può usare la stessa notazione compatta:

vett = Table[(Subscript[v, i]), {i, 5}]

2  vett

3  vett

-vett

Se vogliamo aumentare di 1 tutte coordinate del vettore v = ((Subscript[v, 1]), (Subscript[v, 2]), …, (Subscript[v, n])) nella notazione matematica tradizionale dobbiamo scrivere ((Subscript[v, 1]), (Subscript[v, 2]), …, (Subscript[v, n])) + (1, 
          1, …, 1). Con Mathematica basta scrivere

vett + 1

Se abbiamo una lista e le sommiamo un numero (o una variabile, o in generale un'espressione che non sia una lista), quel numero verrà automaticamente sommato a tutti gli elementi della lista:

vett - 4

vett + a

4  vett - 1

A dire il vero un modo migliore di interpretare la somma di una lista con un numero è di vedere la lista non come un vettore, ma come un insieme di numeri e la somma come la traslazione dell'insieme. Per esempio, se E = {2, 3, 5}, allora 1 + E sarà il traslato di E di una unità verso destra.

Somme e prodotti di liste termine a termine

Se abbiamo due liste della stessa dimensione, per esempio

lista1 = Table[(Subscript[a, i]), {i, 5}];
lista2 = Table[(Subscript[b, i]), {i, 5}];

possiamo sommarle semplicemente scrivendo lista1+lista2:

lista1 + lista2

Cosa succede per il prodotto? Vediamo:

lista1*lista2

È venuto il prodotto termine a termine delle due liste. Partiamo da due matrici della stessa dimensione:

mat1 = 
        Table[(Subscript[a, i, j]), {i, 3}, {j, 4}];
mat2 = 
        Table[(Subscript[b, i, j]), {i, 3}, {j, 4}];
mat1*mat2 // MatrixForm

Anche qui è venuto il prodotto termine a termine. Non confondere questo col prodotto matriciale, che si scrive col puntino. Il prodotto matriciale di due matrici non quadrate con le stesse dimensioni non ha senso:

mat1 . mat2 // MatrixForm

Per matrici quadrate confrontare

mat1 = 
        Table[(Subscript[a, i, j]), {i, 3}, {j, 3}];
mat2 = 
        Table[(Subscript[b, i, j]), {i, 3}, {j, 3}];
mat1*mat2 // MatrixForm

mat1 . mat2 // MatrixForm

(({{(Subscript[a, 1, 1]) (Subscript[b, 1, 1]) + (Subscript[a, 1, 2]) (Subscript[b, 2, 1]) + 
                (Subscript[a, 1, 3]) (Subscript[b, 3, 1]), (Subscript[a, 1, 1]) (Subscript[b, 1, 2]) + 
                (Subscript[a, 1, 2]) (Subscript[b, 2, 2]) + 
                (Subscript[a, 1, 3]) (Subscript[b, 3, 2]), (Subscript[a, 1, 1]) (Subscript[b, 1, 3]) + 
                (Subscript[a, 1, 2]) (Subscript[b, 2, 3]) + (Subscript[a, 1, 3]) (Subscript[b, 3, 3])}, {(Subscript[a, 2, 1]) (Subscript[b, 1, 1]) + (Subscript[a, 2, 2]) (Subscript[b, 2, 1]) + 
                (Subscript[a, 2, 3]) (Subscript[b, 3, 1]), (Subscript[a, 2, 1]) (Subscript[b, 1, 2]) + 
                (Subscript[a, 2, 2]) (Subscript[b, 2, 2]) + 
                (Subscript[a, 2, 3]) (Subscript[b, 3, 2]), (Subscript[a, 2, 1]) (Subscript[b, 1, 3]) + 
                (Subscript[a, 2, 2]) (Subscript[b, 2, 3]) + (Subscript[a, 2, 3]) (Subscript[b, 3, 3])}, {(Subscript[a, 3, 1]) (Subscript[b, 1, 1]) + (Subscript[a, 3, 2]) (Subscript[b, 2, 1]) + 
                (Subscript[a, 3, 3]) (Subscript[b, 3, 1]), (Subscript[a, 3, 1]) (Subscript[b, 1, 2]) + 
                (Subscript[a, 3, 2]) (Subscript[b, 2, 2]) + 
                (Subscript[a, 3, 3]) (Subscript[b, 3, 2]), (Subscript[a, 3, 1]) (Subscript[b, 1, 3]) + 
                (Subscript[a, 3, 2]) (Subscript[b, 2, 3]) + (Subscript[a, 3, 3]) (Subscript[b, 3, 3])}}))

Prodotto scalare ( . )

Il prodotto scalare (canonico) di due vettori (liste) della stessa dimensione è la somma dei prodotti termine a termine. In formule prodotto scalare di {(Subscript[x, 1]), (Subscript[x, 2]), …, (Subscript[x, n])} con {(Subscript[y, 1]), (Subscript[y, 2]), …, (Subscript[y, n])} è (∑_(i = 1)^n) (Subscript[x, i]) (Subscript[y, i]). In Mathematica il prodotto scalare di due vettori si ottiene inserendo fra di loro un puntino, proprio come il prodotto fra matrici:

(Subscript[v, 1]) = {(Subscript[x, 1]), (Subscript[x, 2]), (Subscript[x, 3])};
(Subscript[v, 2]) = {(Subscript[y, 1]), (Subscript[y, 2]), (Subscript[y, 3])};
(Subscript[v, 1]) . (Subscript[v, 2])

La lunghezza (norma euclidea) di un vettore v = {(Subscript[x, 1]), (Subscript[x, 2]), …, (Subscript[x, n])} è la radice della somma dei quadrati delle componenti. Un modo conciso di ottenerla è col puntino:

v = {(Subscript[x, 1]), (Subscript[x, 2]), (Subscript[x, 3])};
Sqrt[v . v]

Funzioni "listabili" di una variabile

Supponiamo di avere una lista di numeri e di volere la lista dei quadrati di quei numeri.

lista = Range[6]

Si potrebbe usare Table:

Table[lista[[n]]^2, {n, 6}]

Questo metodo richiede di usare una variabile ausiliaria n e di conoscere a priori la lunghezza della lista. C'è un modo più pratico: elevare al quadrato la lista:

lista^2

Possiamo anche elevare un numero a una lista, ottenendo la lista delle potenze:

2^{a, b, c}

Similmente, se voglio le radici quadrate degli elementi della lista basta che scriva

Sqrt[lista]

Così anche i logaritmi, gli esponenziali, ecc.:

Log[lista]

Exp[lista]

Questi trucchi valgono anche per liste di liste e per matrici:

Sqrt[(({{1, 2, 3}, {4, 5, 6}}))]//MatrixForm

Questo comportamento di Mathematica corrisponde bene alla notazione matematica tradizionale f(A) per indicare l'immagine dell'insieme A tramite la funzione f, ossia l'insieme {f(x) : x ∈ A}.

Oltre che con funzioni numeriche, anche altre funzioni "entrano dentro" automaticamente alle liste. Per esempio Factor:

Factor[{1 - x^2, 1 - x^3, 1 - x^4}]

Un modo per confermare se una funzione ha questa proprietà, che non sia per tentativi, è di vedere se ha l'attributo Listable:

Attributes[Factor]

Attributes[Sqrt]

Tutte o quasi le funzioni numeriche sono Listable. Una funzione che non è listable è MatrixForm:

Attributes[MatrixForm]

Map (/@)

Supponiamo di avere la funzione f[x_] = (x^2) - 3  x + 1, e di volere la lista dei valori di f sugli interi 1,2,3,…,10. Possiamo farlo in modo conciso definendo prima la fe la lista dei valori di partenza

f[x_] := x^2 - 3  x + 1;
lista = Range[10];

e poi usando il comando Map in questo modo:

Map[f, lista]

In questo esempio si faceva più in fretta con Table[x^2-3x+1,{x,10}]. Map viene comodo quando la lista dei valori su cui calcolare f è irregolare:

Map[f, {3, 2, 12}]

Possiamo usare Map anche con una funzione che non abbiamo definito. In tal caso il nome della funzione resta indicato:

Clear[g];
Map[g, lista]

Per visualizzare una lista di matrici, ciascuna con MatrixForm, mettere MatrixForm davanti alla lista non dà il risultato sperato:

mat1 = Table[(Subscript[a, i, j]), {i, 3}, {j, 4}];
mat2 = 
        Table[(Subscript[b, i, j]), {i, 3}, {j, 4}];
MatrixForm[{mat1, mat2}]

Bisogna invece Mappare MatrixForm sulla lista:

Map[MatrixForm, {mat1, mat2}]

Map ha anche una forma abbreviata: invece di Map[f,lista] si può scrivere f/@lista. La combinazione /@ in inglese si legge "slash at".

g /@ {1, 2, 3}

Apply (@@)

Supponiamo di avere una lista di numeri, e volessimo la loro somma. C'è un modo conciso di farlo, che però richiede un po' per essere digerito. Cominciamo con la lista

lista = {3, 2, 6, 9}

Vorremmo trasformare questa lista nella somma dei suoi componenti, cioè la seguente espressione:

3 + 2 + 6 + 9

Il modo elegante di farlo sfrutta la rappresentazione interna delle due espressioni in Mathematica: lista è rappresentata come List[3,2,6,9]:

lista // FullForm

mentre 3+2+6+9 è Plus[3,2,6,9]:

3 + 2 + 6 + 9 // Hold // FullForm

(si deve usare Hold per impedire che la somma venga eseguita). Ora, qual'è la differenza fra le due espressioni List[3,2,6,9] e Plus[3,2,6,9]? La differenza è soltanto la testa dell'espressione: nel primo caso è List, nel secondo è Plus. Per trasformare la prima espressione nella seconda basta fare un trapianto di testa. C'è un comando fatto proprio per questo scopo: Apply[NuovaTesta,espressione]:

Apply[Plus, lista]

È molto comune abbreviare Apply[f,a] con f@@a ("@@" in inglese si legge "at-at"):

Plus @@ lista

Se si vuole il prodotto degli elementi della lista, invece della somma, basta scrivere

Times @@ lista

Usando Apply e il comando Length[] (che dà la lunghezza di una lista) abbiamo un modo conciso di trovare la media aritmetica di una lista di numeri:

lista = {3, 2, 6, 9};
Plus @@ lista/Length[lista]

Possiamo anche definire una funzione per la media artimetica:

media[v_] := Plus @@ v/Length[v]

media[lista]

Questa funzione è che accetta indifferentemente liste di qualsiasi lunghezza.
Per qualche motivo la media aritmetica non è una funzione predefinita in Mathematica. C'è comunque un comando Mean nel pacchetto aggiuntivo Statistics`DescriptiveStatistics`.

Apply su liste di liste (@@@)

Supponiamo ora di avere una lista di liste di numeri:

(listaDiListe = {{1, 2}, {3, 4, 5}, {6, 7, 8, 9}}) // 
      FullForm

e di volere le somme di ciascuna delle liste interne:

{Plus[1, 2], Plus[3, 4, 5], Plus[6, 7, 8, 9]}

Strutturalmente, si tratta di trapiantare la testa Plus su ciascuna delle liste interne. Questo si fa con Apply[NuovaTesta,listaDiListe,{1}], dove il numero 1 è il livello dell'espressione in cui si vuole fare il trapianto:

Apply[Plus, listaDiListe, {1}]

Il trapianto di testa distribuito sugli elementi di una lista è abbastanza comune da meritare un'abbreviazione: f@@@a ("at-at-at"):

Plus @@@ listaDiListe

Times @@@ listaDiListe

Ci si può sbizzarrire coi trapianti di testa. Come trasformare una somma in una lista:

List @@ (a + b)

o un prodotto in una somma:

Plus @@ (a*b)

La potenza del comando Apply emerge man mano che si impara come Mathematica rappresenta internamente le espressioni.

Confronto fra Plus/@a e Plus@@@a

Entrambi i comandi Map[Plus,lista] e Apply[Plus,lista,{1}] (o le loro abbreviazioni Plus/@lista, Plus@@@lista) in un certo senso"distribuiscono" il Plus sulla lista. Confrontiamo l'effetto: con Apply viene la lista delle somme:

listaDiListe = {{a, b}, {1, 2, 3}};
Apply[Plus, listaDiListe, {1}]

Invece con Map ritroviamo la listaDiListe invariata:

Map[Plus, listaDiListe]

Cosa c'è che non va? Il problema è il comportamento di Plus su una lista:

Plus[{a, b}]

Ma perché mai Plus di una lista deve restituire la lista stessa? In generale Plus (e non solo Plus, ma anche Times) di un singolo argomento restituisce l'argomento stesso: è come fare una somma in cui c'è un solo addendo. E una lista è, appunto, una lista. Insomma, c'è una netta distinzione fra Plus[a,b] e Plus[{a,b}]: nel primo caso abbiamo una somma di due addendi, nel secondo caso c'è un solo addendo, che per puro caso è una lista.

Esercizi

Il prodotto scalare fra due vettori si può anche descrivere geometricamente come il prodotto delle lunghezze dei due vettori per il coseno dell'angolo compreso. Scrivere una funzione che dati due vettori v e w dia l'angolo compreso.

Se non sapessimo che il prodotto scalare fra due vettori si fa col puntino, potremmo riottenerlo con una combinazione di ^2, Plus e @@?

Scrivere una funzione che, data una lista di numeri, ne dia la media geometrica (\(TraditionalForm\`\@\(\(x\_1\) \(x\_2\) ⋯\ x\_n\)\%n\)), o quella armonica (reciproco della somma dei reciproci). O la varianza statistica (media aritmetica dei quadrati degli scarti dalla media aritmetica). Fare in modo che queste funzioni accettino liste di qualsiasi dimensione.

Scrivere una funzione che, data una lista di liste di numeri, dia la somma di tutti quei numeri.

Abbiamo visto che Plus e Times di un solo addendo restituiscono l'addendo stesso. Che cosa succede di Plus e Times di zero addendi? Provare.