75  Entropia e informazione di Shannon

“L’entropia è una misura della nostra ignoranza.”

E.T. Jaynes, Fisico e Statistico, pioniere della probabilità bayesiana

Introduzione

In questo capitolo introdurremo il concetto di entropia, una misura centrale della teoria dell’informazione. L’entropia esprime, in termini quantitativi, l’incertezza media associata a una distribuzione di probabilità e, quindi, la quantità di informazione che ricaviamo quando un evento si verifica. Maggiore è l’imprevedibilità di un evento, maggiore è l’entropia; al contrario, quando l’esito è certo, l’entropia è minima.

L’entropia raggiunge il suo valore massimo quando la distribuzione è uniforme, cioè quando tutti gli eventi possibili hanno la stessa probabilità. In questo caso, nessun esito è più prevedibile di un altro. Quando invece le probabilità sono fortemente sbilanciate, alcuni eventi diventano molto più probabili di altri, e la nostra incertezza diminuisce.

Un esempio aiuta a chiarire. Immaginiamo un sacchetto di palline. Se contiene soltanto palline rosse, la probabilità di estrarre il rosso è pari a 1 e quella di qualsiasi altro colore è 0: la distribuzione non è uniforme e l’entropia è zero, perché il risultato è certo. Se invece il sacchetto contiene tre colori in eguale quantità, ciascun colore ha probabilità 1/3 di essere estratto: questa è una distribuzione uniforme, nella quale l’incertezza è massima e così anche l’entropia.

Panoramica del capitolo

  • Introdurre il concetto di informazione e la sua unità di misura (bit).
  • Definire l’entropia come media della sorpresa di Shannon.
  • Interpretare l’entropia in termini di incertezza e numero di alternative equiprobabili.
  • Stimare l’entropia da distribuzioni teoriche e da campioni osservati.
  • Collegare l’entropia alla codifica di Huffman e al limite teorico di compressione.

  • Per i concetti di base sulla teoria dell’informazione, si rimanda ai primi due capitoli di Information Theory: A Tutorial Introduction (Stone, 2022).
here::here("code", "_common.R") |> 
  source()

library(igraph)
library(ggraph)
library(tidygraph)

# Funzione per calcolare la lunghezza media del codice di Huffman
huffman_encoding <- function(probabilities) {
  # Crea la "coda con priorità" iniziale come lista di liste
  heap <- lapply(names(probabilities), function(sym) list(probabilities[[sym]], list(sym, "")))

  # Funzione per ordinare la heap per probabilità (peso)
  sort_heap <- function(heap) {
    heap[order(sapply(heap, function(x) x[[1]]))]
  }

  # Costruzione dell'albero di Huffman
  while (length(heap) > 1) {
    heap <- sort_heap(heap)
    lo <- heap[[1]]
    hi <- heap[[2]]
    heap <- heap[-c(1, 2)]

    # Aggiunge i prefissi "0" e "1" ai codici
    for (i in seq_along(lo)[-1]) {
      lo[[i]][[2]] <- paste0("0", lo[[i]][[2]])
    }
    for (i in seq_along(hi)[-1]) {
      hi[[i]][[2]] <- paste0("1", hi[[i]][[2]])
    }

    merged <- c(list(lo[[1]] + hi[[1]]), lo[-1], hi[-1])
    heap <- append(heap, list(merged))
  }

  # Estrai la lista finale dei simboli e codici
  final <- heap[[1]][-1]
  names(final) <- sapply(final, function(x) x[[1]])

  # Crea dizionario con codici
  huffman_dict <- lapply(final, function(x) x[[2]])

  # Calcolo della lunghezza media del codice
  avg_length <- sum(mapply(function(sym, code) {
    probabilities[[sym]] * nchar(code)
  }, names(huffman_dict), huffman_dict))

  return(list(avg_length = avg_length, codes = huffman_dict))
}

75.1 Che cos’è l’informazione?

Un bit è la quantità di informazione necessaria per distinguere tra due alternative ugualmente probabili. Ogni nuova decisione binaria raddoppia le possibilità. Il logaritmo in base 2 (\(\log_2\)) ci dice quante decisioni binarie servono per distinguere un certo numero di alternative.

75.1.1 Dalle scelte ai bit: un esempio visivo

Per capire come l’informazione possa essere misurata in bit, consideriamo il seguente esempio. Immaginiamo di trovarci a un incrocio e di dover scegliere una strada tra due possibilità. Ogni volta che ci troviamo di fronte a un incrocio, dobbiamo prendere una decisione: andare a destra o a sinistra. Ogni decisione può essere rappresentata da un bit di informazione: 0 per la sinistra e 1 per la destra.

Consideriamo il percorso con più incroci rappresentato nell’immagine seguente. Ogni percorso completo può essere codificato da una sequenza di bit, dove ogni bit corrisponde a una decisione (binaria) presa a un incrocio. Ad esempio, per raggiungere il punto D011, la sequenza di bit corretta è 011.

75.1.1.1 Quanti bit sono necessari per identificare una destinazione specifica?

Ogni decisione aggiunge un bit alla sequenza che descrive il percorso. Se ci sono \(m\) destinazioni possibili, servono

\[ n = \log_2 m \] bit per identificarne una in modo univoco.

Nel nostro esempio, abbiamo otto destinazioni finali. Pertanto, sono necessari 3 bit (3 decisioni binarie) per identificarne una in modo univoco.

75.1.1.2 Cosa rappresenta un bit in questo contesto?

Un bit rappresenta un’unità elementare di informazione. In questo caso, ogni bit risponde alla domanda: “Devo andare a destra o a sinistra?”.

75.1.1.3 Perché utilizziamo i logaritmi?

Il logaritmo in base 2 ci permette di calcolare l’esponente a cui elevare 2 per ottenere un dato numero. In altre parole, ci indica quanti bit sono necessari per rappresentare un certo numero di destinazioni. Per l’esempio considerato, per arrivare a \(D011\) partendo da \(A\), sono necessarie 3 domande la cui risposta è binaria (destra/sinistra).

Per riassumere:

  • per raggiungere il punto D011 partendo da A, abbiamo bisogno di prendere tre decisioni binarie (sinistra o destra) in corrispondenza di tre incroci;
  • ogni decisione binaria può essere rappresentata da un bit (0 o 1). Quindi, per l’intero percorso, abbiamo bisogno di una sequenza di tre bit: 011;
  • per rispondere alla domanda “Come si va da A a D011?”, abbiamo dunque bisogno di 3 bit di informazione.

In sintesi, esiste una relazione diretta tra il numero di bit di informazione e il numero di possibili destinazioni in un percorso decisionale binario. Ogni bit ci permette di scegliere tra due alternative, raddoppiando così il numero di possibili percorsi.

75.2 La sorpresa e l’informazione di Shannon

Introduciamo ora un elemento cruciale: la probabilità dell’evento. Quando due eventi hanno probabilità diverse, anche la quantità di informazione che trasmettono è diversa. Un evento molto probabile suscita poca sorpresa e, di conseguenza, veicola poca informazione. Al contrario, un evento raro produce una sorpresa maggiore e trasmette più informazione.

Shannon formalizzò questa intuizione definendo l’informazione – o “sorpresa” – di un evento \(x\) come

\[ h(x) = \log_2 \frac{1}{p(x)} = -\log_2 p(x) \ \text{bit}. \tag{75.1}\]

Questa espressione mostra chiaramente come l’informazione associata a un evento dipenda in modo inverso dalla sua probabilità: più l’evento è raro, maggiore sarà il valore di \(h(x)\).1

Per rendere l’idea, immaginiamo tre eventi con probabilità rispettivamente pari a 0.5, 0.25 e 0.10. Applicando la formula di Shannon, otteniamo che la sorpresa corrisponde rispettivamente a 1.00 bit, 2.00 bit e 3.32 bit. Si vede così che, man mano che la probabilità diminuisce, la quantità di informazione – misurata in bit – cresce. In altre parole, un’osservazione inattesa “pesa” di più, perché modifica in misura maggiore le nostre conoscenze sul sistema in esame.

75.2.1 Entropia come media dell’informazione di Shannon

Finora abbiamo considerato la sorpresa associata a un singolo evento. In molti casi, però, non ci interessa un esito isolato, ma vogliamo descrivere l’incertezza complessiva di un sistema che può produrre esiti diversi. Per farlo, occorre calcolare la sorpresa media tenendo conto di tutti i possibili risultati e delle rispettive probabilità. È proprio questo il significato dell’entropia.

Dal punto di vista matematico, l’entropia è la media pesata dell’informazione di Shannon di tutti i possibili esiti di una variabile casuale \(X\):

\[ H(X) \approx \frac{1}{n} \sum_{i=1}^{n} h(x_i). \tag{75.2}\]

In questa espressione, \(h(x_i)\) rappresenta la quantità di informazione trasmessa da un singolo esito \(x_i\), secondo la definizione di Shannon vista in precedenza. L’entropia non si riferisce dunque a un evento specifico, ma alla sorpresa media che ci aspettiamo di provare osservando ripetutamente la variabile.

Se la distribuzione delle probabilità è perfettamente equilibrata – ad esempio in una distribuzione uniforme, dove tutti i risultati sono ugualmente probabili – l’entropia è massima, poiché ogni osservazione fornisce una quantità simile e relativamente alta di informazione. Se invece la distribuzione è sbilanciata – per esempio nel caso di una moneta truccata che dà quasi sempre “testa” – l’entropia è più bassa, perché la prevedibilità aumenta e la quantità media di informazione fornita da ciascuna osservazione diminuisce.

Il grafico seguente illustra come la sorpresa di Shannon varia in funzione della probabilità di un evento: eventi rari producono un valore elevato di sorpresa, mentre eventi comuni producono un valore basso.

p_vals <- seq(0.001, 1, by = 0.001)
surprise <- -log2(p_vals)

ggplot(data.frame(p = p_vals, h = surprise), aes(x = p, y = h)) +
  geom_line(color = okabe_ito["sky"], size = 1) +
  labs(
    title = "Sorpresa di Shannon in funzione della probabilità",
    x = "Probabilità dell'evento p(x)",
    y = "Sorpresa h(x) [bit]"
  ) 

75.2.2 Interpretazione dell’entropia

Diamo ora un significato concreto al valore numerico dell’entropia. Poiché essa rappresenta la media della sorpresa attesa osservando la realizzazione di una variabile casuale, tenendo conto di tutti i possibili esiti e delle loro probabilità, può essere interpretata come il numero medio di bit necessari per descrivere un’osservazione della variabile \(X\).

Quando l’entropia è espressa in bit, possiamo tradurla in un numero equivalente di alternative equiprobabili utilizzando la relazione

\[ m = 2^{H(X)} . \tag{75.3}\]

Questo significa che un’entropia di \(H(X)\) bit corrisponde alla stessa incertezza che avremmo se dovessimo distinguere tra \(m\) esiti tutti ugualmente probabili. In questo senso, l’entropia misura la quantità di informazione contenuta in una variabile, esprimendola in termini del numero di scelte equiprobabili che la variabile potrebbe assumere.

1. Caso di riferimento: moneta equa.

Se una variabile casuale può assumere due valori ugualmente probabili, come una moneta equa con \(p(\text{testa}) = p(\text{croce}) = 0.5\), la sua entropia è:

\[ H(X) = 0.5 \log_2\frac{1}{0.5} + 0.5 \log_2\frac{1}{0.5} = 0.5 \times 1 + 0.5 \times 1 = 1 \ \text{bit}. \]

Questo è il valore massimo di entropia per una variabile con due soli esiti: 1 bit è l’informazione necessaria per distinguere tra due alternative equiprobabili.

2. Moneta sbilanciata: singolo lancio.

Quando la moneta è sbilanciata, l’informazione media diminuisce. Supponiamo \(p(\text{testa}) = 0.9\) e \(p(\text{croce}) = 0.1\).

La sorpresa associata a ciascun esito è:

\[ h(\text{testa}) = \log_2\frac{1}{0.9} \approx 0.15 \ \text{bit}, \]

\[ h(\text{croce}) = \log_2\frac{1}{0.1} \approx 3.32 \ \text{bit}. \]

Pesando queste sorprese con le rispettive probabilità otteniamo l’entropia media:

\[ H(X) = 0.9 \times 0.15 + 0.1 \times 3.32 \approx 0.469 \ \text{bit}. \]

Questa entropia è inferiore a 1 bit, nonostante l’esito raro (“croce”) sia molto più sorprendente di quello di una moneta equa. In generale, nessuna moneta sbilanciata può avere un’entropia media superiore a quella di una moneta equa.

3. Più lanci: interpretazione pratica.

Se lanciamo questa moneta 1000 volte, l’informazione totale prodotta sarà:

\[ 1000 \times 0.469 \approx 469 \ \text{bit}. \]

Quindi, rispetto alla moneta equa (1000 bit), otteniamo meno della metà dell’informazione.

4. Numero equivalente di alternative equiprobabili.

L’entropia può essere anche interpretata come il numero equivalente di alternative tutte equiprobabili:

\[ m = 2^{H(X)} = 2^{0.469} \approx 1.38. \]

Questo non significa che esista un dado fisico con 1.38 facce: è solo un modo per dire che la quantità di incertezza media di questa moneta è la stessa di una variabile che può assumere circa 1.38 valori tutti con la stessa probabilità.

# Funzione per calcolare l'entropia di una moneta
entropy_coin <- function(p) {
  ifelse(p == 0 | p == 1, 0,
         -p * log2(p) - (1 - p) * log2(1 - p))
}

# Sequenza di probabilità
p_values <- seq(0, 1, by = 0.01)
H_values <- entropy_coin(p_values)

# Dati per i punti di esempio
points_df <- data.frame(
  p = c(0.5, 0.9),
  H = entropy_coin(c(0.5, 0.9)),
  label = c("Moneta equa\nH=1 bit", "Moneta sbilanciata\nH=0.469 bit")
)

# Grafico
ggplot(data.frame(p = p_values, H = H_values), aes(x = p, y = H)) +
  geom_line(color = "blue", size = 1) +
  geom_point(data = points_df, aes(x = p, y = H), color = "red", size = 3) +
  geom_text(data = points_df, aes(label = label), vjust = -1, hjust = 0.5) +
  labs(
    title = "Entropia di una moneta in funzione di p(testa)",
    x = expression(paste("Probabilità di testa, ", p)),
    y = "Entropia H(X) [bit]"
  )

75.2.3 Caratteristiche dell’entropia

L’entropia raggiunge il suo valore massimo quando tutti gli esiti possibili hanno la stessa probabilità di verificarsi. In questa condizione, l’incertezza è totale: non esiste alcun indizio che permetta di prevedere il risultato meglio del puro caso, e il grado di imprevedibilità è al massimo.

All’opposto, l’entropia è minima quando l’esito è completamente certo, cioè quando un evento ha probabilità pari a 1 e tutti gli altri hanno probabilità pari a 0. In tali circostanze non vi è alcuna incertezza, nessuna sorpresa e quindi nessuna informazione aggiuntiva ottenibile dall’osservazione.

Un’ulteriore caratteristica fondamentale è l’additività per eventi indipendenti: quando due o più eventi sono indipendenti, l’entropia complessiva della loro combinazione è pari alla somma delle entropie dei singoli eventi. Questa proprietà deriva direttamente dall’additività dei logaritmi nella formula di Shannon e riflette il fatto che, nel caso di eventi indipendenti, l’incertezza complessiva si ottiene sommando le incertezze prodotte da ciascun evento considerato separatamente.

75.3 Stimare l’entropia

Nelle sezioni precedenti abbiamo visto che l’entropia esprime la sorpresa media attesa quando osserviamo una variabile casuale, ed è strettamente legata all’informazione di Shannon dei singoli eventi. Passiamo ora dal concetto alla sua applicazione pratica, illustrando come calcolare l’entropia sia a partire da una distribuzione di probabilità teorica, sia da un insieme di dati osservati.

75.3.1 L’entropia di una distribuzione di probabilità

Consideriamo una variabile casuale discreta \(X\), che può assumere un insieme di valori distinti \(x_1, x_2, \dots, x_n\), ciascuno con una probabilità associata \(p(x) = \Pr\{X = x\}\). L’entropia \(H(X)\) misura l’incertezza complessiva della distribuzione di probabilità e si calcola come

\[ H(X) = -\sum_{x \in X} p(x) \log_2 p(x) . \tag{75.4}\]

Questa formula combina in un’unica media pesata la sorpresa di ciascun esito, dove la sorpresa di un evento \(x\) è \(-\log_2 p(x)\). Il segno negativo è necessario perché i logaritmi di numeri minori di 1 sono negativi; in questo modo, l’informazione viene espressa come quantità positiva.

Ogni termine \(-p(x) \log_2 p(x)\) rappresenta dunque l’informazione media fornita da un singolo esito, pesata in base alla sua probabilità. Una distribuzione uniforme, in cui tutti gli esiti sono ugualmente probabili, massimizza questa media e quindi l’entropia. Al contrario, quando alcuni esiti sono molto più probabili di altri, l’entropia si riduce, riflettendo una minore incertezza complessiva.

In sintesi, \(H(X)\) fornisce il numero medio di bit necessari per descrivere un’osservazione di \(X\), equivalente all’incertezza media che ci si aspetta quando si estrae un esito a caso da questa distribuzione.

Supponiamo di avere un dado con otto facce. Ci sono \(m = 8\) esiti possibili:

\[ A_x = \{1,2,3,4,5,6,7,8\}. \]

Poiché il dado è equo, tutti gli otto esiti hanno la stessa probabilità di \(p(x) = 1/8\), definendo così una distribuzione di probabilità uniforme:

\[ p(X) = \left\{\frac{1}{8}, \frac{1}{8}, \frac{1}{8}, \frac{1}{8}, \frac{1}{8}, \frac{1}{8}, \frac{1}{8}, \frac{1}{8}\right\}. \]

L’entropia di questa distribuzione può essere calcolata come:

\[ H(X) = - \sum_{i=1}^{8} \frac{1}{8} \log_2 \frac{1}{8} = \log_2 8 = 3 \text{ bit}. \]

Poiché l’informazione associata a ciascun esito è esattamente 3 bit, anche l’entropia media è di 3 bit, che rappresenta l’incertezza complessiva della variabile \(X\).

Dato che \(X\) ha un’entropia di \(H(X) = 3\) bit, possiamo dire che \(X\) può rappresentare fino a:

\[ m = 2^{H(X)} = 2^3 = 8 \]

esiti equiprobabili.

Sia \(X\) una variabile casuale discreta che può assumere i valori \(a, b, c,\) e \(d\) con una distribuzione di probabilità di massa \(p(a) = \frac{1}{2}\), \(p(b) = \frac{1}{4}\), \(p(c) = \frac{1}{8}\), e \(p(d) = \frac{1}{8}\), rispettivamente. L’entropia di \(X\), che misura l’incertezza associata alla distribuzione di probabilità, è calcolata come:

\[ H(X) = -\left(\frac{1}{2} \log_2 \frac{1}{2} + \frac{1}{4} \log_2 \frac{1}{4} + \frac{1}{8} \log_2 \frac{1}{8} + \frac{1}{8} \log_2 \frac{1}{8}\right). \]

Calcolando i singoli termini, otteniamo:

\[ H(X) = -\left(\frac{1}{2} \cdot (-1) + \frac{1}{4} \cdot (-2) + \frac{1}{8} \cdot (-3) + \frac{1}{8} \cdot (-3)\right) = \frac{7}{4} \text{ bits}. \]

È importante notare che l’entropia \(H(X)\) dipende esclusivamente dalla distribuzione di probabilità dei valori di \(X\) e non dai valori stessi.

75.3.2 L’entropia in un campione di osservazioni

Finora abbiamo considerato il caso in cui la distribuzione di probabilità sia nota a priori. Nella pratica della ricerca psicologica, tuttavia, disponiamo spesso soltanto di un campione di osservazioni. In questo caso possiamo stimare l’entropia calcolando le frequenze relative di ciascun valore osservato e utilizzandole come stima empirica delle probabilità.

Il risultato misura quanto la distribuzione dei valori nel campione sia incerta o imprevedibile. Un campione in cui le frequenze siano simili per tutti i valori possibili mostrerà un’entropia stimata elevata; al contrario, se nel campione un valore domina nettamente sugli altri, l’entropia stimata sarà bassa, indicando una distribuzione più prevedibile.

Per comprendere meglio questo concetto, possiamo calcolare l’entropia associata a insiemi di osservazioni. Consideriamo i due vettori seguenti:

\[ \begin{align} x &= \{1, 2, 3, 3, 3, 3, 2, 1, 3, 3, 2, 1, 1, 4, 4, 3, 1, 2\}, \notag\\ y &= \{3, 4, 1, 1, 1, 1, 4, 3, 1, 1, 4, 3, 3, 2, 2, 1, 3, 4\}. \notag \end{align} \]

Troviamo l’entropia associata a ciascuno di essi.

# Vettori x e y
x <- c(1, 2, 3, 3, 3, 3, 2, 1, 3, 3, 2, 1, 1, 4, 4, 3, 1, 2)
y <- c(3, 4, 1, 1, 1, 1, 4, 3, 1, 1, 4, 3, 3, 2, 2, 1, 3, 4)

# Conta le frequenze
x_counts <- table(x)
y_counts <- table(y)

# Calcola le probabilità relative
x_probabilities <- as.numeric(x_counts) / length(x)
y_probabilities <- as.numeric(y_counts) / length(y)

# Funzione per calcolare l'entropia (log in base 2)
calculate_entropy <- function(probabilities) {
  -sum(probabilities * log2(probabilities))
}

# Calcolo dell'entropia
x_entropy <- calculate_entropy(x_probabilities)
y_entropy <- calculate_entropy(y_probabilities)

# Stampa i risultati
cat(sprintf("Entropia di x: %.4f bit\n", x_entropy))
#> Entropia di x: 1.8776 bit
cat(sprintf("Entropia di y: %.4f bit\n", y_entropy))
#> Entropia di y: 1.8776 bit

Entrambi i vettori hanno la stessa entropia di 1.8776 bit.

75.3.3 L’entropia di una variabile casuale continua

Per le variabili casuali continue, il concetto di entropia si estende naturalmente a partire dal caso discreto, sostituendo la somma con un integrale. Questa generalizzazione è necessaria perché una variabile continua può assumere un numero infinito di valori all’interno di un intervallo, e le probabilità puntuali diventano nulle; ciò che conta è la densità di probabilità nei vari punti del dominio.

Per una variabile casuale continua \(X\), con funzione di densità di probabilità \(p(x)\), l’entropia – in questo contesto detta entropia differenziale – è definita da

\[ H(X) = -\int p(x) \log_2 p(x) \, dx , \]

dove \(p(x)\) rappresenta la densità di probabilità di \(X\) e l’integrale è calcolato su tutto il dominio della variabile.

Come nel caso discreto, l’entropia differenziale fornisce una misura dell’incertezza media associata alla distribuzione di probabilità. Una densità molto concentrata – per esempio una distribuzione con picchi stretti – indica bassa entropia, perché la variabile è relativamente prevedibile. Al contrario, una densità più “sparsa” e distribuita uniformemente implica un’entropia più alta, segnalando maggiore imprevedibilità.

Il segno negativo nella formula deriva dal fatto che, per probabilità comprese tra 0 e 1, il logaritmo è negativo: in questo modo l’entropia assume valori positivi e può essere interpretata, come nel caso discreto, come il numero medio di bit necessari per descrivere un’osservazione di \(X\).

Per la distribuzione normale \(X \sim \mathcal N(\mu,\sigma^2)\) l’entropia differenziale ha una forma chiusa:

\[ H(X)=\tfrac12 \log_2\!\big(2\pi e\,\sigma^2\big)\ \text{bit}. \]

La dipendenza è tutta nella scala \(\sigma\): raddoppiare \(\sigma\) aggiunge esattamente 1 bit di entropia, perché la massa di probabilità si “spalma” su un intervallo più ampio. Numericamente, con \(\sigma=0{,}5\), \(H(X)\approx 1{,}047\) bit; con \(\sigma=1\), \(H(X)\approx 2{,}047\) bit; con \(\sigma=2\), \(H(X)\approx 3{,}047\) bit. L’aumento regolare di un bit per ogni raddoppio di \(\sigma\) rende molto trasparente l’idea che una densità più concentrata (piccola \(\sigma\)) produce minore incertezza, mentre una densità più diffusa (grande \(\sigma\)) produce maggiore incertezza.

Ecco un frammento R che replica il calcolo e mostra le tre densità normalizzate sulla stessa scala, così che la relazione tra forma della densità e entropia sia visibile a colpo d’occhio.

# Entropia differenziale (in bit) per N(mu, sigma^2)
h_norm_bits <- function(sigma) 0.5 * log2(2 * pi * exp(1) * sigma^2)

sigmas <- c(0.5, 1, 2)
entropie <- sapply(sigmas, h_norm_bits)
round(entropie, 3)
#> [1] 1.047 2.047 3.047
# atteso: 1.047, 2.047, 3.047

# Visualizzazione delle densità
df <- data.frame(
  x = rep(seq(-6, 6, length.out = 1000), times = length(sigmas)),
  sigma = factor(rep(sigmas, each = 1000))
)
df$dens <- mapply(function(x, s) dnorm(x, mean = 0, sd = s), df$x, as.numeric(as.character(df$sigma)))

ggplot(df, aes(x = x, y = dens, group = sigma)) +
  geom_line(aes(linetype = sigma), linewidth = 1) +
  labs(
    title = "Densità normali con diversa scala e relativa entropia",
    subtitle = paste0("H(σ=0.5)≈", round(entropie[1],3), " bit; ",
                      "H(σ=1)≈",   round(entropie[2],3), " bit; ",
                      "H(σ=2)≈",   round(entropie[3],3), " bit"),
    x = "x", y = "densità"
  ) 

Nell’analisi di dati psicologici, la stessa variabile misurata con una scala più “compressa” (varianza più piccola, punteggi concentrati) porta a una minore entropia differenziale rispetto alla stessa variabile osservata con maggiore dispersione. Questo legame diretto tra dispersione e entropia chiarisce perché, in presenza di eterogeneità individuale o situazionale, la “quantità di incertezza” da descrivere aumenti con la variabilità del fenomeno.

75.4 La codifica di Huffman

Nelle sezioni precedenti abbiamo visto che l’entropia di una variabile casuale \(X\) misura la sorpresa media attesa quando osserviamo un suo esito e che può essere interpretata come la lunghezza media più breve, in bit, di un codice binario ottimale per rappresentare tali esiti. L’algoritmo di Huffman fornisce un metodo pratico per costruire un codice che si avvicina a questo limite teorico.

Il principio è intuitivo: se dobbiamo trasmettere un messaggio composto da simboli con probabilità diverse, conviene assegnare codici più brevi ai simboli frequenti e codici più lunghi a quelli rari. In questo modo si riduce la lunghezza media complessiva del messaggio, in linea con ciò che la formula dell’entropia suggerisce.

Per costruire una codifica di Huffman, si parte dalle frequenze (o probabilità) dei simboli. Si crea un nodo per ciascun simbolo e, in modo iterativo, si uniscono i due nodi meno probabili in un nuovo nodo la cui frequenza è la somma delle due. Il procedimento continua finché tutti i simboli confluiscono in un’unica radice. A questo punto si assegna lo 0 ai rami sinistri e l’1 ai rami destri: il codice di un simbolo è la sequenza di bit dal nodo radice alla foglia corrispondente. Il risultato è un codice prefisso (nessun codice è l’inizio di un altro), senza perdita di informazione e ottimale tra tutti i codici che usano un numero intero di bit per simbolo.

75.4.1 Esempio di costruzione

Supponiamo di avere quattro simboli con le frequenze riportate in tabella:

Simbolo Frequenza
A 20
B 10
C 8
D 5

Il procedimento di Huffman procede così:

  1. (D:5) e (C:8) → uniti in (DC:13)
  2. (B:10) e (DC:13) → uniti in (BDC:23)
  3. (A:20) e (BDC:23) → uniti in (ABDC:43)

L’albero risultante è il seguente:

       (43)
      /    \
    (20)   (23)
     A     /   \
         (10)  (13)
          B    /  \
              D    C

Assegnando 0 a sinistra e 1 a destra, otteniamo:

Simbolo Codice Huffman
A 0
B 10
D 110
C 111

Come previsto, il simbolo più frequente (A) ha il codice più corto, mentre i meno frequenti (C e D) hanno codici più lunghi.

75.4.2 Collegamento con l’entropia

Consideriamo ora le probabilità \(p(A)=0.4\), \(p(B)=0.3\), \(p(C)=0.2\), \(p(D)=0.1\). Con la codifica sopra, la lunghezza media del codice è:

\[ (0.4 \times 1) + (0.3 \times 2) + (0.2 \times 3) + (0.1 \times 3) = 1.9 \ \text{bit}. \]

L’entropia teorica della variabile è:

\[ \begin{align} H(X) &= -[0.4 \log_2 0.4 + 0.3 \log_2 0.3 + 0.2 \log_2 0.2 + 0.1 \log_2 0.1] \notag\\ &\approx 1.8465 \ \text{bit}.\notag \end{align} \]

Il valore di Huffman (1.9 bit) è leggermente superiore a \(H(X)\) perché l’entropia può assumere valori decimali, mentre la codifica di Huffman utilizza lunghezze intere di bit per simbolo. Nonostante questa piccola differenza, la codifica di Huffman è quasi ottimale e si avvicina al limite teorico imposto dall’entropia.

In altre parole, l’entropia rappresenta la lunghezza media minima teorica per codificare senza perdita un insieme di simboli, mentre Huffman traduce questa teoria in un algoritmo concreto che raggiunge, nei limiti imposti dall’uso di bit interi, la massima efficienza possibile.

Supponiamo di avere una variabile casuale \(X\) che può assumere quattro valori: \(A\), \(B\), \(C\), e \(D\), con le seguenti probabilità:

  • \(p(A) = 0.4\)
  • \(p(B) = 0.3\)
  • \(p(C) = 0.2\)
  • \(p(D) = 0.1\)

Per rappresentare questi esiti con un codice binario efficiente possiamo usare la codifica di Huffman, che assegna codici più brevi ai simboli più probabili, e codici più lunghi a quelli meno probabili.

Supponiamo che Huffman produca la seguente codifica:

  • A = 0 (1 bit)
  • B = 10 (2 bit)
  • C = 110 (3 bit)
  • D = 111 (3 bit)

La lunghezza media del codice si ottiene moltiplicando la probabilità di ciascun simbolo per la lunghezza del suo codice binario, e poi sommando:

\[ \begin{align} \text{Lunghezza media} &= (0.4 \times 1) + (0.3 \times 2) + (0.2 \times 3) + (0.1 \times 3) \\ &= 0.4 + 0.6 + 0.6 + 0.3 = 1.9 \text{ bit}. \end{align} \]

Questo significa che, in media, servono 1.9 bit per rappresentare un’osservazione della variabile \(X\) usando la codifica di Huffman.

Confermiamo il risultato con il seguente codice R:

# Definizione delle probabilità
probabilities <- list(A = 0.4, B = 0.3, C = 0.2, D = 0.1)
# Funzione per la codifica di Huffman
huffman_encoding <- function(probabilities) {
  nodes <- lapply(names(probabilities), function(sym) {
    list(symbol = sym, prob = probabilities[[sym]], left = NULL, right = NULL)
  })

  while (length(nodes) > 1) {
    nodes <- nodes[order(sapply(nodes, function(n) n$prob))]
    left <- nodes[[1]]
    right <- nodes[[2]]
    merged <- list(symbol = NULL, prob = left$prob + right$prob, left = left, right = right)
    nodes <- c(nodes[-c(1, 2)], list(merged))
  }

  assign_codes <- function(node, prefix = "", code_map = list()) {
    if (!is.null(node$symbol)) {
      code_map[[node$symbol]] <- prefix
    } else {
      code_map <- assign_codes(node$left, paste0(prefix, "0"), code_map)
      code_map <- assign_codes(node$right, paste0(prefix, "1"), code_map)
    }
    return(code_map)
  }

  code_map <- assign_codes(nodes[[1]])

  avg_length <- sum(sapply(names(probabilities), function(sym) {
    probabilities[[sym]] * nchar(code_map[[sym]])
  }))

  return(list(avg_length = avg_length, huffman_dict = code_map))
}
# Applicazione e stampa dei risultati
result <- huffman_encoding(probabilities)

cat(sprintf("Lunghezza media del codice di Huffman: %.2f bit/simbolo\n", result$avg_length))
#> Lunghezza media del codice di Huffman: 1.90 bit/simbolo
cat("Codici di Huffman:\n")
#> Codici di Huffman:
for (sym in names(result$huffman_dict)) {
  cat(sprintf("%s: %s\n", sym, result$huffman_dict[[sym]]))
}
#> A: 0
#> B: 10
#> D: 110
#> C: 111

Ora calcoliamo l’entropia teorica della variabile \(X\), cioè la lunghezza media minima che qualsiasi codifica binaria può raggiungere:

\[ \begin{align} H(X) &= - \sum p(x) \log_2 p(x) \\ &= -[0.4 \log_2 0.4 + 0.3 \log_2 0.3 + 0.2 \log_2 0.2 + 0.1 \log_2 0.1] \\ &= 1.8465 \text{ bit}. \end{align} \]

Il valore dell’entropia è leggermente inferiore alla lunghezza media di Huffman (1.9 bit). Questo è normale: Huffman fornisce codici con lunghezza intera in bit, mentre l’entropia può assumere valori decimali. La codifica di Huffman è quindi quasi ottimale.

In sintesi:

  • l’entropia \(H(X)\) rappresenta la lunghezza media teorica minima (in bit) per codificare una variabile casuale;
  • la codifica di Huffman costruisce un codice binario che si avvicina molto a questo limite, usando più bit per i simboli rari e meno bit per quelli frequenti;
  • in questo modo, l’entropia ci offre un criterio per valutare quanto efficiente è una codifica: più la lunghezza media si avvicina all’entropia, più è efficiente.

75.5 Applicazioni psicologiche

Il concetto di entropia, inteso come misura della sorpresa media associata a un evento, trova applicazioni dirette anche nello studio di fenomeni psicologici. In particolare, la sorpresa — formalizzabile in termini di informazione di Shannon — è stata associata a cambiamenti emotivi, processi di apprendimento e modulazione della motivazione.

Un esempio classico è fornito da Spector (1956), che studiò l’effetto della probabilità a priori sulla soddisfazione dei soggetti in seguito a una promozione lavorativa. I risultati mostrarono che esiti inizialmente percepiti come poco probabili — e quindi più sorprendenti quando si verificano — producevano un impatto emotivo maggiore rispetto a esiti attesi. In altre parole, la sorpresa amplificava la risposta affettiva, confermando l’idea che l’entropia non sia solo una misura astratta, ma un indicatore della potenziale intensità della reazione emotiva.

Ricerche più recenti, in contesti sia sperimentali che ecologici, hanno confermato questo legame. Ad esempio, studi nell’ambito delle neuroscienze cognitive hanno mostrato che eventi ad alta sorpresa modulano l’attività di aree cerebrali legate all’elaborazione emotiva, come l’amigdala e la corteccia prefrontale ventromediale, influenzando sia l’umore immediato sia l’apprendimento successivo. Allo stesso modo, nell’analisi dei dati di Ecological Momentary Assessment (EMA), la probabilità soggettiva di un evento può essere messa in relazione alla variazione momentanea dell’umore, mostrando che episodi rari o inattesi tendono a generare oscillazioni emotive più marcate.

Questi risultati illustrano bene come il concetto di entropia possa essere utilizzato in psicologia non solo come strumento di misura della distribuzione di probabilità degli eventi, ma anche come variabile esplicativa in modelli che indagano il legame tra aspettative, sorpresa e stati emotivi. Questo stesso legame sarà centrale quando, nelle prossime sezioni, introdurremo la divergenza di Kullback–Leibler e la utilizzeremo per confrontare modelli in un’ottica bayesiana.

In questo esempio, simuliamo 200 osservazioni in cui ogni partecipante sperimenta un evento con probabilità variabile. La sorpresa di ciascun evento viene calcolata con la formula di Shannon, e l’effetto sull’umore viene simulato assumendo che eventi più sorprendenti producano, in media, variazioni di umore più ampie (positive o negative).

set.seed(123)

# Numero di osservazioni
n <- 200

# Probabilità percepita dell'evento (da molto probabile a molto improbabile)
p_event <- runif(n, min = 0.05, max = 0.95)

# Sorpresa di Shannon (in bit)
surprise <- -log2(p_event)

# Variazione di umore simulata:
# partiamo da un effetto medio proporzionale alla sorpresa, con rumore casuale
delta_mood <- 0.5 * surprise + rnorm(n, mean = 0, sd = 0.5)

# Mettiamo tutto in un data frame
df <- data.frame(
  p_event = p_event,
  surprise = surprise,
  delta_mood = delta_mood
)

# Visualizzazione
ggplot(df, aes(x = surprise, y = delta_mood)) +
  geom_point(alpha = 0.6) +
  geom_smooth(method = "lm", se = TRUE, color = "blue") +
  labs(
    title = "Relazione tra sorpresa dell'evento e\nvariazione di umore",
    x = "Sorpresa (bit)",
    y = "Δ Umore"
  ) 

Interpretazione. Il grafico mostra che, in questa simulazione, eventi più sorprendenti (bit più alti) tendono a produrre variazioni di umore maggiori. Questo illustra visivamente l’idea, già documentata empiricamente, che la sorpresa può amplificare la risposta emotiva.

Riflessioni conclusive

In questo capitolo abbiamo introdotto l’entropia come misura dell’incertezza associata a una variabile casuale e, più in generale, come strumento per quantificare la sorpresa media che ci attendiamo da un sistema. Attraverso esempi concreti — dalla moneta al dado, fino alla codifica di Huffman — abbiamo visto come l’entropia non sia solo una definizione matematica, ma una quantità operativa che descrive quanta informazione ci aspettiamo di ottenere osservando un evento, e quanto “costa” trasmettere quell’informazione in termini di bit.

Il legame tra entropia e codifica è particolarmente istruttivo: l’entropia rappresenta il limite teorico inferiore alla lunghezza media di qualsiasi codifica binaria senza perdita. L’algoritmo di Huffman, pur vincolato a lunghezze intere di bit, si avvicina molto a questo limite, dimostrando come principi puramente probabilistici possano guidare scelte pratiche di compressione ed efficienza.

Questi concetti, tuttavia, hanno una portata ben più ampia della sola teoria dell’informazione. Nel contesto della modellizzazione statistica — e in particolare dell’inferenzia bayesiana — l’entropia diventa la base per concetti più complessi, come la divergenza di Kullback–Leibler (KL), che misura la distanza tra due distribuzioni di probabilità. Se l’entropia ci dice quanto possiamo imparare da una distribuzione nota, la divergenza KL ci dice quanto un modello si discosta da quella distribuzione.

Questa connessione si estende direttamente alla valutazione predittiva dei modelli. L’Expected Log Predictive Density (ELPD), che utilizzeremo per confrontare modelli bayesiani, è infatti legato alla minimizzazione della divergenza KL rispetto al “vero” processo generatore dei dati. In altre parole:

  • l’entropia quantifica l’incertezza intrinseca dei dati;
  • la KL quantifica l’inefficienza di un modello nel rappresentare tale incertezza;
  • l’ELPD ci dice quanto bene un modello, in media, riesce a prevedere nuovi dati, proprio minimizzando quella inefficienza.

Comprendere l’entropia, quindi, non serve solo a sapere come comprimere messaggi o calcolare la sorpresa: è un passaggio essenziale per capire cosa significhi, in termini informativi, che un modello “funziona bene” e produce previsioni affidabili.

Chiudiamo con una riflessione di natura più filosofica. Come ricorda Eckhardt (2012), l’informazione non è solo una quantità misurabile:

“…our mind, and even the subconscious self, resonate. A poet can recall chains of ideas, emotions and memories with a well-turned word. In this sense, writing is magic.”

Questo ci invita a ricordare che l’informazione ha anche una dimensione qualitativa: nei modelli psicologici, soprattutto in quelli predittivi, è fondamentale considerare non solo la capacità di previsione, ma anche il significato e l’interpretabilità di ciò che il modello produce. Un buon modello non si limita a comprimere l’incertezza in bit: deve anche restituire risposte che risuonino con le domande scientifiche e psicologiche che ci poniamo.

Entropia \(H(X)\)
→ Misura l’incertezza intrinseca di una variabile casuale.
→ Interpretabile come la sorpresa media o la lunghezza media minima (in bit) necessaria per codificare gli esiti di \(X\).

Divergenza di Kullback–Leibler \(D_{KL}(P \parallel Q)\)
→ Confronta due distribuzioni di probabilità \(P\) (la “vera” distribuzione) e \(Q\) (il modello).
→ Misura quanto il modello \(Q\) si discosta da \(P\) in termini di inefficienza nel codificare i dati.

Expected Log Predictive Density (ELPD)
→ Valuta la capacità predittiva di un modello su dati nuovi.
→ Collegata alla minimizzazione della KL tra la distribuzione dei dati e la distribuzione predittiva del modello.
→ Più alto è l’ELPD, migliore è la capacità del modello di rappresentare e prevedere i dati.

Collegamento logico:
Entropia → ci dice quanta incertezza c’è nei dati.
KL → ci dice quanto un modello spreca informazione rispetto a quella incertezza.
ELPD → ci dice quanto bene il modello prevede, riducendo quello spreco.

Figura 75.1: Diagramma visivo che collega Entropia → Divergenza KL → ELPD.
sessionInfo()
#> R version 4.5.1 (2025-06-13)
#> Platform: aarch64-apple-darwin20
#> Running under: macOS Sequoia 15.6.1
#> 
#> Matrix products: default
#> BLAS:   /Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/lib/libRblas.0.dylib 
#> LAPACK: /Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/lib/libRlapack.dylib;  LAPACK version 3.12.1
#> 
#> locale:
#> [1] C/UTF-8/C/C/C/C
#> 
#> time zone: Europe/Zagreb
#> tzcode source: internal
#> 
#> attached base packages:
#> [1] stats     graphics  grDevices utils     datasets  methods   base     
#> 
#> other attached packages:
#>  [1] tidygraph_1.3.1       ggraph_2.2.1          igraph_2.1.4         
#>  [4] pillar_1.11.0         tinytable_0.11.0      patchwork_1.3.1      
#>  [7] ggdist_3.3.3          tidybayes_3.0.7       bayesplot_1.13.0     
#> [10] ggplot2_3.5.2         reliabilitydiag_0.2.1 priorsense_1.1.0     
#> [13] posterior_1.6.1       loo_2.8.0             rstan_2.32.7         
#> [16] StanHeaders_2.32.10   brms_2.22.0           Rcpp_1.1.0           
#> [19] sessioninfo_1.2.3     conflicted_1.2.0      janitor_2.2.1        
#> [22] matrixStats_1.5.0     modelr_0.1.11         tibble_3.3.0         
#> [25] dplyr_1.1.4           tidyr_1.3.1           rio_1.2.3            
#> [28] here_1.0.1           
#> 
#> loaded via a namespace (and not attached):
#>  [1] gridExtra_2.3        inline_0.3.21        sandwich_3.1-1      
#>  [4] rlang_1.1.6          magrittr_2.0.3       multcomp_1.4-28     
#>  [7] snakecase_0.11.1     compiler_4.5.1       mgcv_1.9-3          
#> [10] systemfonts_1.2.3    vctrs_0.6.5          stringr_1.5.1       
#> [13] pkgconfig_2.0.3      arrayhelpers_1.1-0   fastmap_1.2.0       
#> [16] backports_1.5.0      labeling_0.4.3       rmarkdown_2.29      
#> [19] ragg_1.4.0           purrr_1.1.0          xfun_0.52           
#> [22] cachem_1.1.0         jsonlite_2.0.0       tweenr_2.0.3        
#> [25] broom_1.0.9          parallel_4.5.1       R6_2.6.1            
#> [28] stringi_1.8.7        RColorBrewer_1.1-3   lubridate_1.9.4     
#> [31] estimability_1.5.1   knitr_1.50           zoo_1.8-14          
#> [34] Matrix_1.7-3         splines_4.5.1        timechange_0.3.0    
#> [37] tidyselect_1.2.1     viridis_0.6.5        abind_1.4-8         
#> [40] yaml_2.3.10          codetools_0.2-20     curl_6.4.0          
#> [43] pkgbuild_1.4.8       lattice_0.22-7       withr_3.0.2         
#> [46] bridgesampling_1.1-2 coda_0.19-4.1        evaluate_1.0.4      
#> [49] survival_3.8-3       RcppParallel_5.1.10  polyclip_1.10-7     
#> [52] tensorA_0.36.2.1     checkmate_2.3.2      stats4_4.5.1        
#> [55] distributional_0.5.0 generics_0.1.4       rprojroot_2.1.0     
#> [58] rstantools_2.4.0     scales_1.4.0         xtable_1.8-4        
#> [61] glue_1.8.0           emmeans_1.11.2       tools_4.5.1         
#> [64] graphlayouts_1.2.2   mvtnorm_1.3-3        grid_4.5.1          
#> [67] QuickJSR_1.8.0       colorspace_2.1-1     nlme_3.1-168        
#> [70] ggforce_0.5.0        cli_3.6.5            textshaping_1.0.1   
#> [73] svUnit_1.0.6         viridisLite_0.4.2    Brobdingnag_1.2-9   
#> [76] V8_6.0.5             gtable_0.3.6         digest_0.6.37       
#> [79] ggrepel_0.9.6        TH.data_1.1-3        htmlwidgets_1.6.4   
#> [82] farver_2.1.2         memoise_2.0.1        htmltools_0.5.8.1   
#> [85] lifecycle_1.0.4      MASS_7.3-65

Bibliografia

Eckhardt, W. (2012). Paradoxes in probability theory. Springer Science & Business Media.
Spector, A. J. (1956). Expectations, fulfillment, and morale. The Journal of Abnormal and Social Psychology, 52(1), 51–56.
Stone, J. V. (2022). Information theory: a tutorial introduction, 2nd edition.

  1. Ricorda che per le proprietà dei logaritmi: \(\log(1/x) = -\log(x)\), perché \(\log(1/x) = \log(1) - \log(x) = 0 - \log(x)\).↩︎