Blog — Principi matematiciDante Noguez |
Versione | 0.5.0 | |
|---|---|---|---|
| Lingua | ES/EN | ||
Il maestro Corominas ha osservato che, etimologicamente, la parola «funzione» rimanda all’‘adempimento’ o all’‘esecuzione (di qualcosa)’. Nel linguaggio comune, utilizziamo questa idea per riferirci al compito che spetta svolgere a uno strumento, entità o persona. Sebbene con sottili differenze, questa idea di «funzione» soggiace anche alla matematica, alle scienze computazionali, ai linguaggi di programmazione e all’intelligenza artificiale. «Funzione» è un termine così elementare per tutte queste discipline e così intuitivo per tutti noi nel linguaggio quotidiano, che lo utilizzeremo come base in questo testo per comprendere i rudimenti stessi di tali discipline.
Quando nel linguaggio quotidiano parliamo della «funzione (di qualcosa)», possiamo dire che ci stiamo riferendo all’operazione che si realizza su un oggetto per ottenere un risultato desiderato. La funzione del frigorifero, per esempio, consiste nel mantenere una determinata temperatura al suo interno, ovvero refrigerare. Come risultato, gli alimenti che vi conserviamo si mantengono in buono stato. Seguendo questa linea, possiamo schematizzare la funzione del frigorifero («refrigerazione») nel modo seguente:
Alimento → Refrigerazione → Conservazione dell'alimento
A titolo illustrativo, possiamo anche rappresentare questo schema nella forma seguente:
Dalla generalizzazione dello schema iniziale, si evince che la funzione denota l’operazione o serie di operazioni che si realizzano su un valore di «ingresso» (nel nostro esempio, gli alimenti) per dare come risultato un valore di «uscita» (la conservazione dell’alimento a bassa temperatura). In altre parole, il nucleo di una funzione sta nel definire una o più operazioni che si realizzano con il valore di ingresso per trasformarlo in qualche modo e dare come risultato un valore di uscita. Proponiamo diversi esempi per assimilare meglio questo concetto:
Ingresso («input») → Funzione («computazione», «algoritmo») → Uscita («output»)
Ingredienti → Ricetta (preparazione) → Pizza
Libro → Lettura (interpretazione) → Apprendimento
Investimento → Produzione → Impresa
Ipotesi → Sperimentazione → Scoperta scientifica
Legge (Potere Legislativo) → Interpretazione (P. Giudiziario) → Esecuzione (P. Esecutivo)
A sua volta, ciascuna di queste funzioni può essere scomposta in molte altre. Per esempio, possiamo scomporre ogni parte del paradigma statale precedente secondo il funzionamento interno di ciascun Potere:
Proposta di legge → Revisione (votazione, approvazione) → Nuova legge
Legge → Interpretazione → Sentenza, giurisprudenza
Sentenza → Esecuzione della sentenza → Giustizia
Come dicevo, questa forma paradigmatica (modellica, di base) di intendere la funzione ci darà la chiave per comprendere un’infinità di concetti e fenomeni in un numero di discipline. In realtà, la genesi di questa idea di funzione sta nella matematica. Più in particolare, in un manoscritto del 1673 elaborato dal grande Gottfried Leibniz, intitolato Methodus tangentium inversa seu de functionibus («il metodo inverso delle tangenti, ovvero sulle funzioni»).1 2
Sviluppato ulteriormente da un altro colosso (il nostro maestro Leonhard Euler), questo modo di intendere la funzione ha precisamente lo stesso senso che gli abbiamo attribuito finora. Per non perdere il rigore, specifichiamo brevemente a cosa ci riferiamo. Sebbene vi siano diversi modi di definire una funzione matematica, utilizziamo il più comune: la funzione è una relazione univoca di dipendenza tra un elemento di un insieme e un altro elemento di un altro insieme.
Procediamo per parti: gli insiemi sono gruppi o collezioni di numeri, elementi od oggetti con caratteristiche in comune. I numeri pari, per esempio, possono essere intesi come un insieme: \{2, 4, 6, ...\}. La definizione dell’insieme «numeri pari» esprime chiaramente che detto insieme può contenere solo numeri che hanno come multiplo il numero due.
Inoltre, si dice che esiste una relazione tra elementi di un insieme e elementi di un altro. Seguendo il nostro esempio, posso dire che l’insieme \{6, 12, 18, ...\} è in relazione con l’insieme dei numeri pari in funzione di una moltiplicazione per il numero 3. Così, l’insieme dei numeri pari sarebbe l’ingresso, e questo secondo insieme sarebbe l’uscita. In questo senso, la relazione tra gli elementi di entrambi gli insiemi si dà secondo le regole o operazioni che una funzione definisce (in questo caso, una moltiplicazione per 3). Ogni valore di uscita dipende interamente dalle operazioni che si realizzano con il valore di ingresso, e questa dipendenza è univoca perché a ogni valore di ingresso può corrispondere un solo valore di uscita. È impossibile, per esempio, che al numero 2 come valore di ingresso corrispondano i numeri 7, 10 e 24 dell’insieme di uscita, poiché 2 \times 3 = 6, cosicché esclusivamente il numero 6 dell’insieme di uscita corrisponde al 2. In una parola: la moltiplicazione è una funzione, e quando moltiplichiamo possiamo avere un solo risultato valido come risposta.
In matematica, vedremo inoltre che la notazione e la rappresentazione di una funzione hanno una forma particolare. La notazione matematica più comune per denotare una funzione è f(x); la sua rappresentazione più comune è un grafico con coordinate cartesiane. Esemplifichiamo con il caso f(x) = x^{2}. Secondo il nostro schema, l’ingresso è un qualsiasi numero (che rappresenteremo con una x), l’operazione (funzione) che realizzeremo su di esso è elevarlo al quadrato, il che avrà come risultato (uscita) un altro numero, che possiamo denominare con la lettera y3:
\ x \to f(x) \to y \\ \ x \to x^{2} \to y \\
Il fatto che questa notazione matematica possa sembrarci confusa si deve al suo livello di astrazione. In realtà, l’uso di lettere (x, y, eccetera) al posto di numeri è una convenzione accademica utilizzata per facilitare la definizione di regole generali. Tali lettere comunicano al lettore che sono variabili, ovvero che il loro valore può variare a seconda del contesto. La matematica esige sempre la generalizzazione delle formulazioni ed espressioni proprie del suo campo, cosicché questo tipo di «tecnicismi» sono indispensabili. Per il resto, sarebbe incredibilmente laborioso (e perfino confuso) scrivere espressioni matematiche complesse con uno stile diverso (ad es., «se abbiamo una funzione matematica di un qualsiasi numero uguale a detto numero elevato al quadrato») rispetto al tradizionale f(x) = x^{2} (si legge: «effe di x uguale a x al quadrato»).
A volte, la funzione si accompagna dalla definizione del suo dominio, ovvero si può indicare che tipo di oggetto è l’ingresso che introdurremo nella funzione. Per esempio:
\ f(x) = x^{2}, \ \ \ x\in\mathbb{R}\
Dove \in significa «appartiene a» e \mathbb{R} si utilizza per rappresentare i numeri reali. Allora l’ultima espressione la leggeremmo come «dove x appartiene all’insieme dei numeri reali».
Un principio elementare della comunicazione è l’economia del linguaggio: il linguaggio deve sempre essere il più chiaro e semplice possibile. Paradossalmente, questa notazione è la più semplice che esista, e qualsiasi altra forma di comunicare formule matematiche sarebbe inefficiente e confusa. In realtà, la notazione matematica ci risulta estranea solo per mancanza di abitudine, e questo è del tutto normale.
Tornando al nostro argomento, vediamo finalmente la rappresentazione grafica di una funzione matematica.
Nel grafico di f(x) = x^2, l’asse orizzontale rappresenta i valori di x; quello verticale, quelli di y. Ciò che ogni punto ci dice è che, per esempio, quando x vale 2, allora y vale 4; quando x vale 4, allora y vale 16; e così via. Questo è così perché la nostra funzione indica che y è il risultato dell’elevare x al quadrato. Infine, tracciamo una linea blu che unisce tutti i punti per rappresentare la tendenza che seguono; allo stesso modo, tracciamo una linea bianca tratteggiata per indicare a caso un’intersezione delle x con le y.
Possiamo anche rappresentare tutto ciò come una tabella:
| Valori di x | Valori di y |
|---|---|
| 0 | 0 |
| 1 | 1 |
| 2 | 4 |
| 3 | 9 |
| 4 | 16 |
| 5 | 25 |
| 6 | 36 |
| 7 | 49 |
| 8 | 64 |
| 9 | 81 |
| 10 | 100 |
Come si vede, i nostri valori di y sono il risultato della funzione f(x) = x^2. La x assume il valore di ogni numero che va da 0 a 10 e, di conseguenza, su di esso si realizza l’operazione che definisce la funzione per dare infine un risultato. Mostriamo alcune equivalenze nuovamente:
\ x \to f(x) \to y \\ \ x \to x^2 \to y \\ \ 2 \to 2^2 \to 4 \\ \ 4 \to 4^2 \to 16 \\ \ 5 \to 5^2 \to 25 \\
La funzione è esattamente la stessa sia nella notazione matematica, sia nella notazione programmatica, sia nel grafico che abbiamo fatto, sia nella tabellina sopra, sia in quest’ultimo schema. Alla fine, stiamo solo utilizzando segni diversi propri dei nostri linguaggi (numeri, lettere, linee, frecce) per rappresentare la stessa cosa in modi diversi. Tutte queste forme di rappresentazione sono valide, ma ne useremo una o un’altra a seconda del contesto e del nostro gusto personale.
A questo punto, vale la pena notare che «astratto» è, per definizione, una forma di rappresentare simbolicamente idee od oggetti della realtà. Etimologicamente, la parola «astrarre» si compone di ab- («allontanare», «lontano (da qualcosa)») e trahēre («trascinare», «tirare (qualcosa)»), cosicché «astrarre» significa trascinare qualcosa fuori dalla sua forma primigenia. Astrarre, dunque, implica separare qualcosa dalle sue altre parti o dalla sua situazione iniziale per rappresentarlo in un’altra forma. In aritmetica, per esempio, si separano le proprietà numeriche dalle proprietà cromatiche, sensitive, chimiche, nutrizionali di due paia di mele per rappresentare (e perfino generalizzare) in modo astratto (cioè, mediante segni «2, +, 4») le proprietà strettamente matematiche di quelle due paia, come 2 + 2 = 4.
A un certo punto, le proprietà numeriche di diversi oggetti si sono consolidate in modo tale che una nuova scienza si è costituita, dissociata dal nostro criterio personale e dalle forme fisiche concrete del nostro ambiente. Questa nuova scienza, sovrana sul proprio campo, è stata e continua ad essere capace di scoprire nuove configurazioni matematiche attraverso le relazioni e operazioni che possono darsi al suo interno, senza attenzione esplicita alla realtà che la circonda; ma questo processo «artificioso» o formale non deve mai portarci a credere che le operazioni matematiche riguardino realtà diverse dalla nostra. Ogni pensiero possibile, sia scientifico, delirante o fantasioso, ha sempre origine a fior di terra, per quanto i nostri appariscenti sistemi di pensiero sembrino indicare il contrario.
Per approfondire il tema delle forme di rappresentazione, si legga Poetizar di Gustavo Bueno.
Graficazione di funzioni
Infine, mi piacerebbe utilizzare questo nuovo apprendimento per rafforzare la nostra intuizione sulla graficazione delle funzioni. Ovvero, vedremo perché la funzione quadratica è una parabola, perché la sigmoide sembra una lettera S, eccetera. Questo ha a che fare con il comportamento della funzione: il grafico rappresenta il pattern che (grazie alla funzione) risulta dalle intersezioni tra i valori di x e i valori di y.
Una funzione cubica:
\ \operatorname{Cúbica}(x) = x^3
La funzione di attivazione ReLU (Rectified Linear Unit, «unità lineare rettificata»), ampiamente utilizzata nel deep learning, si definisce come:
\ ReLU(x) = max(0, x)
La sua funzione è convertire in 0 tutti i numeri negativi e conservare il valore originale dei positivi. Nella formula possiamo apprezzare che per ogni numero che entra nella funzione, il risultato è il valore massimo tra 0 e il valore di ingresso: se il valore di ingresso è -1, allora il risultato è 0 (perché 0 è maggiore di -1); allo stesso modo, se il valore di ingresso è 1, allora il risultato sarà 1 (perché 1 > 0).
Possiamo perfino graficare funzioni che matematicamente potrebbero sembrarci dei geroglifici, ma che programmaticamente sono piuttosto semplici. La funzione softmax è data da:
\ Softmax(x_i) = \frac{e^{x_i}}{\sum_{j=1}^{K} e^{x_j}}\
Per comprenderla meglio, possiamo fare una scomposizione dei suoi termini:
| Notazione | Interpretazione |
|---|---|
| x_i | Ogni valore corrispondente di x, «l’i-esimo valore di x» |
| e | Numero di Eulero, è uguale a 2.71828 |
| e^{x_i} | Il numero di Eulero elevato al valore corrispondente di x |
| \sum | Sommatoria: indica che si devono sommare tra loro tutti i valori che accompagna; questo simbolo è una lettera S («sigma») nell’alfabeto greco |
| _{j=1} | Indica il valore a partire dal quale inizierà la sommatoria |
| K | Numero totale di valori di x |
| \sum_{j=1}^{K} e^{x_j} | La sommatoria deve essere realizzata su tutti i valori di e^{x_j} che abbiamo, cioè a partire dal primo valore di x (ovvero x_j) fino al valore x_K (ovvero l’ultimo valore di x). |
In termini semplici, questa formula dice che la funzione softmax consiste nel dividere il numero e elevato alla potenza x_i (cioè alla potenza del valore corrispondente di x) per la sommatoria di tutti i valori di x come potenze del numero e. Se lo scrivessimo passo per passo, risulterebbe:
\ Softmax(x_i) = \frac{2.71828^{x_i}}{2.71828^{x_j} + 2.71828^{x_2} + 2.71828^{x_3}, ... 2.71828^{x_K}}\
Supponiamo che le nostre x siano i numeri da -2 a 1, allora sostituiamo i valori e applichiamo la formula:
\ Softmax(x_1) = Softmax(-2) = \frac{2.71828^{-2}}{2.71828^{-2} + 2.71828^{-1} + 2.71828^{0} + 2.71828^{1}}\
\ Softmax(x_2) = Softmax(-1) = \frac{2.71828^{-1}}{2.71828^{-2} + 2.71828^{-1} + 2.71828^{0} + 2.71828^{1}}\
\ Softmax(x_3) = Softmax(0) = \frac{2.71828^{0}}{2.71828^{-2} + 2.71828^{-1} + 2.71828^{0} + 2.71828^{1}}\
\ Softmax(x_4) = Softmax(1) = \frac{2.71828^{1}}{2.71828^{-2} + 2.71828^{-1} + 2.71828^{0} + 2.71828^{1}}\
I nostri risultati (o valori di y, se si vuole) sarebbero 0.032, 0.087, 0.236 e 0.643, rispettivamente. I valori sommati tra loro danno sempre 1 quando applichiamo la funzione softmax.
Ora possiamo apprezzare meglio la semplicità della formula iniziale, rispetto alla laboriosa scomposizione che dobbiamo fare al momento di calcolare valore per valore. Questa è una delle ragioni per cui la notazione matematica è così astratta.
Fortunatamente, Python ci evita tutto questo trambusto. Possiamo delegare al nostro programmino il calcolo di questa formula intricata e logorante:
def Softmax(x):
return np.exp(x) / sum(np.exp(x))
In primo luogo, concentriamoci nel comprendere che queste funzioni implicano un pattern, il quale definisce il modo in cui otterremo y.
Ora, da dove viene tutto questo e cosa significa? La risposta dipende anche da ciò che vogliamo fare con il nostro apprendimento. Innanzitutto, sottolineiamo nuovamente che tutti questi segni sono modi di rappresentare. Scoprire qualcosa o parlare di qualcosa di nuovo implica anche la necessità di cercare una forma di comunicare quella novità. Naturalmente, comunicare tale novità comporta formulare nuovi concetti, idee ed espressioni per poterla comunicare; altrimenti, sarebbe impossibile differenziarla da ciò che già conosciamo.
In tal senso, i primi matematici che cercarono di comprendere il concetto di funzione cercarono anche una forma per esprimerlo. Forse per caso, per comodità, per l’autorità del grandioso Leonhard Euler o per centinaia di altre possibili ragioni, la notazione che infine tutti convennero di utilizzare fu f(x). Allo stesso modo, le forme geometriche, testuali o programmatiche di rappresentarla sono anch’esse una convenzione, così come queste lettere, accenti e virgole che scrivo lo sono. Sta sempre al di fuori di noi la «decisione» oggettiva su quali convenzioni permangano e quali altre scompaiano. In ogni caso, ogni persona comprende e rappresenta in modo diverso tutte queste cose dentro di sé.
D’altra parte, come dico, il loro uso dipenderà da noi. Nel caso di una funzione, per esempio, a un matematico interessano le proprietà logiche, numeriche, geometriche e astratte della funzione; a un informatico, sapere cos’è una funzione e come si relaziona ciò con il funzionamento di un computer; a un programmatore, sapere quali proprietà hanno le funzioni di un certo linguaggio. Noi possiamo dare loro quella o qualsiasi altra utilità che ci venga in mente.
Diciamo, per esempio, che abbiamo appena scoperto che le lingue cambiano a un ritmo che si può rappresentare con una funzione sigmoide: prima, nessuno utilizza l’espressione che vogliamo studiare; a un certo punto, una nuova espressione appare e comincia a essere utilizzata da alcune persone; poi, arriva un momento in cui si estende rapidamente tra la popolazione; e infine, adottata universalmente, il suo uso cambia molto poco, poiché ormai nessuno smette di usarla, né tantomeno qualcuno comincia a usarla per la prima volta.
La formula per generare un grafico di questa natura è:
\ Sigmoide(x) = \sigma(x) = \frac{1}{1 + e^{-x}}\
Come dicevamo: prima nessuno la usa, poi poche persone, quindi cresce rapidamente e alla fine si stabilizza. In questo caso, x rappresenta il passare del tempo e y il numero di persone che utilizzano una parola.
Questa funzione può essere utilizzata per rappresentare il numero di contagiati da COVID-19, lo sviluppo di un embrione, la crescita di una nuova industria e migliaia di altre cose. Allo stesso modo, le si possono dare usi più complessi: per esempio, può essere utilizzata per diagnosticare malattie con modelli matematici (modelli di intelligenza artificiale). La sua utilità dipende interamente da noi.
Per parlare di algoritmi ci atterremo a ciò che Donald Knuth (prominente programmatore) ha sentenziato a riguardo. In essenza, un algoritmo consiste in un insieme finito di regole o istruzioni ordinate il cui fine è risolvere un tipo specifico di problema.
Secondo Knuth, ogni algoritmo deve offrire una soluzione concreta al problema che si propone di risolvere; inoltre, deve essere definito, ovvero preciso, non ambiguo; deve anche essere effettivo4, nel senso che le operazioni che contiene siano sufficientemente basilari da poter essere realizzate in un periodo finito di tempo da qualsiasi persona; infine, deve contenere un «ingresso» e un’«uscita», termini con i quali siamo già familiari.
Per illustrare questo concetto, possiamo cominciare con un esempio semplice di algoritmo. Diciamo che ci interessa creare un algoritmo per infilarci una scarpa. Potremmo abbozzare il seguente schema:
1. Prendere la scarpa con entrambe le mani.
2. Puntare il tallone della scarpa verso di noi.
3. Inserire la punta del piede dentro la scarpa.
4. Cambiare la presa per sostenere la scarpa con i pollici al suo interno.
5. Inserire il piede fino in fondo nella scarpa estraendo contemporaneamente i pollici.
Come vediamo, la natura dell’algoritmo è piuttosto semplice, sebbene la sua formulazione non lo sia affatto. A rigore, un algoritmo funziona solo quando soddisfiamo veramente i requisiti che menzionavamo prima. L’algoritmo per infilare le scarpe che abbiamo appena proposto potrebbe fallire in mille modi: come distinguo la scarpa sinistra dalla destra? Come scelgo quale paio di scarpe usare? Cosa succede se mi cade la scarpa dalle mani durante il passo 2? Sebbene umanamente possiamo improvvisare o ragionare su ogni passo, un computer non può farlo, cosicché il nostro algoritmo risulterebbe inutilizzato di fronte a qualsiasi imprevisto.
Potremmo dire anche che, nonostante alcune sottili differenze, un algoritmo e una funzione sono la stessa cosa: un’operazione realizzata con un ingresso per dare come risultato un’uscita.
Detto ciò, mi pare che questo abbozzo sia sufficiente per il momento per comprendere l’algoritmo. Sebbene l’algoritmo sia una specie di ricetta o manuale semplice per risolvere un problema concreto, la sua complessità risiede nel livello di dettaglio minuzioso che richiede per essere effettivo. A mio avviso, l’algoritmo è l’uso meccanico della forza bruta per conseguire un fine; ma quella forza bruta deve essere calibrata meticolosamente.
Infine, esemplifichiamo con un algoritmo vero e proprio. Formuleremo il famoso algoritmo di Euclide, utilizzato per trovare il massimo comun divisore di due numeri.
Sebbene la notazione algoritmica ci possa risultare più familiare, è necessario avvertire che il segno «←» può risultare confuso perché indica una sequenza da destra a sinistra. Questo segno indica che il valore a sinistra acquisisce il valore a destra: «n ← r» significa «n acquisisce il valore di r»; il segno «↔︎», d’altra parte, significa che dobbiamo scambiare il valore delle variabili tra loro.
Ora sì, l’algoritmo: Dati due numeri positivi interi m e n, l’Algoritmo E (di Euclide) troverà il massimo comun divisore tra essi, cioè troverà l’intero positivo più grande che può dividere entrambi senza alcun resto.
Algoritmo E:
E0. Verificare che m ≥ n. Se m < n, scambiare m ↔ n.
E1. Trovare il resto. Dividere m per n; il resto sarà r. (Avremo che 0 ≤ r < n).
E2. È zero? Se r = 0, l’algoritmo termina; n è la risposta.
E3. Ridurre. Ora m ← n, n ← r, e torneremo al passo E1.
In pseudocodice:
Funzione AlgoritmoE(m, n):
Se m è minore di n:
scambiare i valori
r = m modulo n
se r è uguale a 0:
il risultato è n
in caso contrario:
finché r non è uguale a zero:
scambiare i valori di m, n con quelli di n, r
r = m modulo n
se r è uguale a 0:
il risultato è n
L’algoritmo impone condizioni, regole e si anticipa a determinate situazioni per funzionare. Sebbene l’«ambiente matematico» sia molto più prevedibile di quello «vitale» (cioè, nell’algoritmo precedente ci poteva cadere la scarpa, ma in questo algoritmo non scomparirà alcun numero), è certo che anche formulare algoritmi di questa natura è un’arte minore che richiede una certa pratica.
In Python, potremmo programmarlo nella forma seguente:
def AlgoritmoE(m, n):
if m < n:
m, n = n, m
r = m%n
if r == 0:
return n
else:
while r != 0:
m, n = n, r
r = m%n
if r == 0:
return n
# Una versión simplificada del algoritmo:
def Euclides(a, b):
while b != 0:
a, b = b, a % b
return a
# Ponemos a prueba los algoritmos:
AlgoritmoE(544, 119) # → 17
AlgoritmoE(2166, 6099) # → 57
Euclides(544, 119) # → 17
Euclides(2166, 6099) # → 57A mio giudizio, l’algoritmo più importante della storia è la macchina di Turing. Questo algoritmo definì un modello per i computer dei nostri giorni, e la sua semplicità è altrettanto impressionante quanto la sua capacità.
Per sapere in cosa consiste quell’algoritmo, si legga: The Annotated Turing di Petzold.
Tra le altre cose, per noi l’interesse dell’algoritmo o della funzione risiede precisamente nel fatto che ci permettono di comprendere il potenziale della macchina più avanzata dei nostri giorni. La programmazione, da parte sua, ci permette di materializzare o sfruttare tale comprensione.
In essenza, programmare implica comunicare istruzioni (algoritmi, funzioni) a un computer. Le istruzioni formulate in un linguaggio di programmazione vengono tradotte mediante un compilatore (cioè un altro programma) nel «linguaggio macchina», ovvero in un sistema binario che poi può essere «interpretato» dai transistor del microprocessore.
Come è noto, il sistema binario che utilizzano i computer consiste di zeri e uni. Per esempio, i numeri da 0 a 8 sarebbero5:
0. 0 0 0
1. 0 0 1
2. 0 1 0
3. 0 1 1
4. 1 0 0
5. 1 0 1
6. 1 1 0
7. 1 1 1
8. 1 0 0 0
Prendiamo il seguente numero del sistema decimale:
1 \ 2 \ 3
Qui, il 3 è al posto delle unità; il due, nelle decine; e l’uno, nelle centinaia. Allora,
(100 \times 1) + (10 \times 2) + (1 \times 3) = 100 + 20 + 3 = 123
Ogni posto di ogni cifra rappresenta una potenza di 10, per cui ci sono dieci possibili cifre in ogni posto (ora comincia ad avere senso il termine «sistema decimale»). Il primo posto da destra rappresenta un 10^0; quello di mezzo, 10^1; il terzo, 10^2, e così via:
\ 10^2\ 10^1\ 10^0\ \\ 1 \ \ \ \ \ \ \ 2 \ \ \ \ \ \ \ 3
In binario, d’altra parte, abbiamo solo due cifre e gli esponenti del due in ogni posto:
\ ... 2^2\ 2^1\ 2^0\
Che è equivalente a:
\ ... 4\ 2\ 1\
Allora, se volessimo rappresentare il valore decimale 3, dovremmo sommare 2 e 1 in sistema binario così:
\ 2^2\ 2^1\ 2^0\ \\ 0 \ \ \ \ 1 \ \ \ \ 1
E il valore decimale 123:
\ 2^6 \ 2^5\ 2^4\ 2^3 \ 2^2\ 2^1\ 2^0 \ \\ 1 \ \ 1 \ \ 1 \ \ 1 \ \ 0 \ \ 1 \ \ 1
La maggior parte dei computer utilizza 8 cifre o bit (che significa «cifra binaria», in inglese «binary digit») alla volta, cosicché il numero 3 sarebbe 00000011. Ogni insieme di 8 bit si denomina byte.
Sebbene negli esseri umani il sistema decimale prevalga per esserci più intuitivo, è certo che ancora una volta stiamo parlando di una convenzione. Xul Solar, per esempio, fu un uomo singolare che sostenne per molto tempo che il sistema duodecimale di numerazione è superiore al decimale. Nella sua vita personale e professionale, Xul Solar utilizzava quel sistema.
Il sistema binario, d’altra parte, è particolarmente conveniente per i computer per la semplice ragione che facilita la traduzione di zeri e uni in segnali elettrici: lo zero indica spento; l’uno, acceso. Il transistor adempie alla funzione di rappresentare elettricamente gli uni e gli zeri del computer, realizzando operazioni con essi accendendosi e spegnendosi continuamente. Un processore di computer è composto da milioni di transistor che rappresentano uni e zeri. Un transistor può rappresentare centinaia di migliaia di miliardi di zeri e uni al secondo; il transistor più piccolo dei nostri giorni è più o meno 50.000 volte più sottile di un capello.
Il sistema binario fu formulato nientemeno che dal nostro vecchio amico Leibniz, il quale probabilmente utilizzò anche il termine «algoritmo» in senso moderno per la prima volta. Leibniz fu collega di Johann Bernoulli, il quale a sua volta fu mentore del nostro altro amico Leonhard Euler.
Naturalmente, il sistema binario, oltre a rappresentare numeri, può anche rappresentare lettere, immagini, video, musica e tutto ciò che vediamo sullo schermo di un computer. Per questo, sono state convenute diverse combinazioni di codice binario per rappresentare certi caratteri. La lettera «A», per esempio, si rappresenta con il numero 65, che in binario è 01000001; ASCII e Unicode sono codici standard per rappresentare l’alfabeto e alcuni caratteri speciali.
D’altra parte, lo standard per rappresentare i colori è RGB, il quale indica la quantità di rosso, verde e blu che contiene il colore da rappresentare. Ogni pixel degli schermi dei computer utilizza tre byte per rappresentare un colore.
Più ci avviciniamo a un’immagine, meglio vediamo i pixel (i piccoli quadrati colorati) che la rappresentano. Così, sappiamo ormai che la macchina più potente creata dall’essere umano consiste in enormi sequenze di numeri.

Il problema risiede precisamente in questo: queste sequenze sono gigantesche, cosicché comunicare con il computer in quel linguaggio sarebbe un martirio. Per questo, i linguaggi di programmazione facilitano tale comunicazione, poiché il computer sa tradurre nel suo linguaggio le istruzioni che gli diamo mediante un programma. Come vedremo più avanti, bastano appena poche righe di codice per creare un programma di intelligenza artificiale.
Cf. Herrera Castillo (2013) e Scriba (1964).↩︎
Leibniz non solo coniò il termine «funzione», ma anche «variabile», «costante», «coordinata», «equazione differenziale». Nelle parole di Dietrich Mahnke (che, peraltro, menzioniamo anche nel nostro testo a proposito di Borges), questo prezioso manoscritto di Leibniz conteneva già il germe di diversi concetti importanti della matematica moderna: la serie di Taylor, il calcolo infinitesimale e le funzioni.↩︎
Le funzioni si possono anche scrivere utilizzando una freccia di mappatura, come per esempio: x \mapsto x^2, che equivale al nostro esempio f(x) = x^{2}. Tuttavia, è preferibile abituarci a quest’ultimo modo di formularle, poiché è il più abituale.↩︎
L’«effettività» alla quale si riferisce Knuth è simile all’«indipendenza» alla quale si riferiva David Hilbert quando parlava del «pensiero assiomatico»: gli assiomi di un sistema devono essere tali che non vi sia alcuno inutile o superfluo, alcuno che si possa derivare da un altro. Lo stesso potrebbe dirsi degli algoritmi.↩︎
Gli zeri aggiuntivi a 0, 1, 2 e 3 non sono necessari, ma li utilizziamo per facilitare la lettura.↩︎