Paithon Book Paithon Book

Python e le sue applicazioni nell’AI

Python e le sue applicazioni nell’AI#

Python ha una qualità che conta più della sua fama: si legge quasi come una lista di istruzioni scritta in inglese, e per chi non ha mai programmato è la differenza fra seguire un procedimento e decifrarlo. Attorno a quella qualità è cresciuto tutto il resto. Tre raccolte di codice già pronto le incontreremo di continuo: NumPy, che fa i conti su lunghe file di numeri tutte insieme e in fretta; scikit-learn, dove stanno i metodi classici del machine learning; e PyTorch, con cui si costruiscono e si addestrano le reti neurali.

Cominciamo dal massimo comune divisore, perché il procedimento lo conosciamo già per intero e resta solo da dirlo in una lingua che la macchina esegue.

Calcolare il massimo comune divisore#

Il trucco di Euclide lo abbiamo già visto: dividere, tenere il resto e ripetere, finché il resto non diventa zero. Qui lo traduciamo in Python e, per metterlo alla prova, lo applichiamo a una coppia di numeri nuova, \(60\) e \(48\).

# La ricetta: riceve due numeri interi positivi e ne restituisce il MCD

def mcd(a, b):
    while b:
        a, b = b, a % b
    return a

# Proviamola

print(mcd(60, 48))
12

Prima di leggerlo, una cosa da sapere subito: in un programma il segno = non vuol dire «è uguale a», come in matematica. Vuol dire «metti dentro». a = 5 non è un’affermazione da verificare, è un ordine: da adesso in poi, dove c’è scritto a c’è il numero cinque. Si chiama assegnazione, e le assegnazioni sono la cosa che un programma fa più spesso di ogni altra.

Ora leggiamolo riga per riga, come una ricetta:

  • def mcd(a, b): dà un nome alla ricetta e ai suoi due ingredienti, i numeri a e b. In programmazione una ricetta con un nome si chiama funzione; è un parente lontano della funzione dei grafici (anche lei riceve dei numeri e ne restituisce uno), ma qui è soprattutto un modo per scrivere una volta un procedimento e poi riusarlo quando serve;

  • while b: significa «ripeti finché b non è diventato zero». Lo zero non è scritto perché Python, quando gli si dà un numero al posto di una condizione, considera vero qualunque numero diverso da zero: while b si legge quindi «finché b c’è ancora»;

  • a % b è il simbolo Python per il resto della divisione tra a e b: proprio il resto del trucco di Euclide;

  • a, b = b, a % b fa scorrere i numeri di un posto: il divisore prende il posto del numero grande, e il resto prende il posto del divisore. Qui c’è un dettaglio che conta davvero: Python calcola tutti e due i valori a destra dell’uguale usando i numeri vecchi, e solo dopo li scrive a sinistra. Le due assegnazioni avvengono insieme, non una dopo l’altra;

  • return a consegna il risultato a chi ha chiesto il calcolo: quando il resto è zero, l’ultimo numero per cui si è diviso è il MCD;

  • print(mcd(60, 48)) è la riga che mette in moto la ricetta con i due numeri e ne stampa a schermo il risultato. È l’unica che produce qualcosa da vedere.

Seguiamolo con \(60\) e \(48\): \(60\) diviso \(48\) dà resto \(12\); poi \(48\) diviso \(12\) dà resto \(0\). Resto zero: l’ultimo numero per cui abbiamo diviso è \(12\), ed è proprio quello che print ha stampato sopra.

Se quelle due assegnazioni fossero davvero una dopo l’altra il risultato cambierebbe, ed è l’errore in cui si cade seguendo il codice con carta e penna. Scritte così:

a = b          # a diventa 48
b = a % b      # ma qui «a» vale già 48, non più 60!

la seconda riga calcolerebbe \(48\) diviso \(48\), cioè resto \(0\), il ciclo si fermerebbe subito e la funzione restituirebbe \(48\) invece di \(12\). Un numero che non è nemmeno un divisore comune di \(60\) e \(48\): sbagliato di netto, non di poco.

Il commento sopra la funzione dice «due numeri interi positivi», e non è una formalità: con i numeri negativi il risultato può uscire negativo (mcd(12, -8) restituisce \(-4\)), il che non è sbagliato di suo, ma un massimo comune divisore lo si vuole positivo. Le funzioni già pronte delle librerie se ne occupano (in Python è math.gcd, che restituisce sempre un numero positivo); questa, che è la versione minima, no. Ed è una cosa che tornerà spesso: quattro righe che funzionano non sono ancora quattro righe robuste.

Questo, per essere chiari, non è ancora intelligenza artificiale: è un algoritmo, e per giunta uno dei più antichi che esistano. Il massimo comune divisore di \(60\) e \(48\) lo si trova anche a mente; il pregio dell’idea di Euclide è che continua a funzionare quando i numeri diventano lunghi cento cifre e a mente non ci si prova nemmeno, ed è per questo che dopo duemilatrecento anni sta ancora dentro le librerie di ogni linguaggio.

Serviva a far vedere che cos’è una ricetta scritta da qualcuno, riga per riga, perché è il termine di paragone di tutto il resto: i programmi di cui parleremo quella ricetta non ce l’hanno scritta dentro, e se la costruiscono guardando migliaia di esempi. Per arrivare a scriverne uno servono ancora un po’ più di Python e un po’ di matematica, e sono i due capitoli che vengono subito dopo.