Dimensione dell'input¶
Parte 4 · Analisi degli algoritmi · Capitolo 11 · Dispensa, volume 4 (PDF) · Esercizi: Dimensione dell'input
Introduzione¶
Per confrontare due algoritmi che risolvono lo stesso problema contiamo quante operazioni eseguono. Questo numero cresce con l'input: ordinare un array di un milione di componenti richiede più tempo che ordinare un array di dieci componenti, e verificare se un numero di venti cifre è primo richiede più tempo che verificarlo per un numero di due cifre. Per rendere preciso questo discorso serve una misura di quanto sia grande un input.
In questo capitolo fissiamo questa misura: la dimensione dell'input è il numero di bit necessari per scrivere l'input in binario, come nel capitolo «Rappresentazione dei numeri». Procediamo così:
-
definiamo la dimensione dell'input e richiamiamo la notazione asintotica usata per descriverne l'ordine di crescita (capitolo 9 «Notazione asintotica»);
-
calcoliamo la dimensione dell'input, e il suo ordine di crescita, quando l'input è un numero naturale, due numeri naturali, oppure un array di numeri naturali;
-
mostriamo perché la distinzione è importante: lo stesso numero di passi può essere piccolo o enorme rispetto alla dimensione dell'input, a seconda che il parametro \(n\) sia il valore di un numero o il numero di componenti di un array.
11.1 Dimensione dell'input¶
Definizione 11.1: dimensione dell'input
La dimensione dell'input \(|I|\) di un algoritmo è il numero totale di bit necessari per rappresentare in binario una data istanza di input \(I\).
-
La dimensione dell'input è una funzione che associa a ogni istanza di input un numero naturale, pari al numero totale di bit necessari per rappresentare in binario l'istanza di input.
-
Consideriamo algoritmi le cui istanze di input sono costituite da uno o più numeri naturali, oppure da array di numeri naturali.
-
Nel capitolo «Rappresentazione dei numeri» abbiamo visto che il numero minimo di cifre necessarie per scrivere un numero naturale \(n \ge 1\) in base \(2\) è \(\lfloor \log_2 n \rfloor + 1\). Equivalentemente, un numero naturale \(n\) si scrive con esattamente \(k\) bit se e solo se
\[ 2^{k-1} \le n \le 2^{k}-1. \]
Esempio 11.1: il numero di bit di alcune istanze di input
-
Un numero naturale, \(n = 37\). Poiché \(2^5 = 32 \le 37 < 64 = 2^6\), si ha \(\lfloor \log_2 37 \rfloor = 5\), e
\[ 37 = (100101)_2, \qquad |I| = \lfloor \log_2 37 \rfloor + 1 = 6. \] -
Due numeri naturali, \(n = 37\) e \(m = 200\). Poiché \(2^7 = 128 \le 200 < 256 = 2^8\), il numero \(200 = (11001000)_2\) richiede \(8\) bit, e
\[ |I| = 6 + 8 = 14. \] -
L'array \(a = [6, 1, 3, 8]\). Le sue componenti sono \(6 = (110)_2\), \(1 = (1)_2\), \(3 = (11)_2\) e \(8 = (1000)_2\), e
\[ |I| = 3 + 1 + 2 + 4 = 10. \]
La dimensione esatta dell'input dipende dai valori specifici dell'istanza. Nell'analisi degli algoritmi ci interessa come essa cresce, e per questo usiamo la notazione asintotica.
11.2 Notazione asintotica: un richiamo¶
La notazione asintotica è definita e studiata nel capitolo 9 «Notazione asintotica». Qui richiamiamo soltanto le definizioni e i due fatti che si usano in questo capitolo. Come là, \(f\) e \(g\) sono funzioni definite sui numeri naturali \(n \ge 1\) e a valori reali non negativi, e la notazione confronta i loro ordini di crescita per tutti gli \(n\) sufficientemente grandi.
-
\(f(n) = O\big(g(n)\big)\) (limite superiore) se esistono \(c \in \R_{>0}\) e \(n_1 \in \N\) tali che
\[ 0 \le f(n) \le c\, g(n), \qquad \forall n \ge n_1; \] -
\(f(n) = \Omega\big(g(n)\big)\) (limite inferiore) se esistono \(c \in \R_{>0}\) e \(n_1 \in \N\) tali che
\[ 0 \le c\, g(n) \le f(n), \qquad \forall n \ge n_1; \] -
\(f(n) = \Theta\big(g(n)\big)\) (entrambi) se esistono \(c_1, c_2 \in \R_{>0}\) e \(n_1 \in \N\) tali che
\[ 0 \le c_1\, g(n) \le f(n) \le c_2\, g(n), \qquad \forall n \ge n_1. \]
-
Per dimostrare una di queste affermazioni si determinano le costanti (\(c\), oppure \(c_1\) e \(c_2\)) e la soglia \(n_1\).
-
Per funzioni di due parametri, come \(f(n,m)\), le definizioni sono le stesse, con le disuguaglianze richieste per ogni \(n \ge n_1\) e ogni \(m \ge m_1\).
-
I due fatti seguenti sono dimostrati nel capitolo 9 «Notazione asintotica».
Osservazione 11.2: \(\Theta\) equivale a \(O\) e \(\Omega\)
Si ha \(f(n) = \Theta\big(g(n)\big)\) se e solo se \(f(n) = O\big(g(n)\big)\) e \(f(n) = \Omega\big(g(n)\big)\).
Proposizione 11.3: criterio del limite
Sia \(g(n) > 0\) per ogni \(n \ge 1\). Se
allora \(f(n) = \Theta\big(g(n)\big)\).
11.3 Istanza di input costituita da un numero naturale¶
Partiamo dall'input più semplice: un solo numero naturale.
-
Se l'input è costituito da un solo numero naturale \(n \ge 1\), la dimensione dell'input è
\[ |I|=\lfloor \log_2 n \rfloor +1, \]che è il numero minimo di bit necessari per rappresentare \(n\) in binario.
Osservazione 11.4: dimensione dell'input di un numero naturale
Si ha
Dimostrazione
Per dimostrare il risultato, determiniamo \(c_1,c_2 \in \R_{>0}\) e \(n_1 \in \N\) tali che
Poiché \(x < \lfloor x \rfloor + 1\) per ogni numero reale \(x\), si ha
Inoltre, poiché \(\lfloor x \rfloor \le x\) per ogni numero reale \(x\), e \(\log_2 n \ge 1\) per ogni \(n \ge 2\), si ha
Quindi, scegliendo \(c_1=1\), \(c_2=2\) e \(n_1=2\), otteniamo
□
Se l'input di un algoritmo è costituito da un solo numero naturale \(n \ge 1\), la dimensione dell'input soddisfa:
Figura 11.1: La dimensione dell'input \(|I| = \lfloor \log_2 n \rfloor + 1\) di un numero naturale \(n\) (asse orizzontale in scala logaritmica): aumenta di un bit a ogni potenza di \(2\), e per \(n \ge 2\) è compresa fra \(\log_2 n\) e \(2\log_2 n\), come afferma l'[Osservazione 11.4](#box-5-obsOneNumber).
Esempio 11.2: i limiti per un numero naturale
Per \(n = 37\) si ha \(|I| = 6\) (Esempio 11.1) e \(\log_2 37 \approx 5.21\). Le disuguaglianze della dimostrazione dell'Osservazione 11.4 diventano
Per \(n = 1\) si ha \(|I| = 1\) ma \(\log_2 1 = 0\): per questo la soglia della dimostrazione è \(n_1 = 2\).
11.4 Istanza di input costituita da due numeri naturali¶
Quando l'input è costituito da due numeri, vanno scritti i bit di entrambi.
-
Se l'input è costituito da due numeri naturali \(n,m \ge 1\), la dimensione dell'input è
\[ |I|=\big(\lfloor \log_2 n \rfloor +1\big)+\big(\lfloor \log_2 m \rfloor +1\big), \]che è il numero minimo di bit necessari per rappresentare in binario sia \(n\) sia \(m\).
Osservazione 11.5: dimensione dell'input di due numeri naturali
Si ha
Dimostrazione
Per dimostrare il risultato, determiniamo \(c_1,c_2 \in \R_{>0}\) e \(n_1,m_1 \in \N\) tali che
per ogni \(n \ge n_1\) e \(m \ge m_1\).
Abbiamo
Quindi
Inoltre
Dunque
Se \(n,m \ge 2\), allora \(\log_2 n \ge 1\) e \(\log_2 m \ge 1\), quindi
Pertanto
Quindi, scegliendo \(c_1=1\), \(c_2=2\), \(n_1=2\) e \(m_1=2\), otteniamo
□
Se l'input di un algoritmo è costituito da due numeri naturali \(n,m \ge 1\), la dimensione dell'input soddisfa:
La somma dei due logaritmi può essere sostituita dal logaritmo del maggiore dei due numeri: il numero maggiore richiede almeno metà dei bit dell'istanza.
Osservazione 11.6: dimensione dell'input di due numeri naturali e loro massimo
Si ha
dove
Dimostrazione
Per dimostrare il risultato, determiniamo \(c_1,c_2 \in \R_{>0}\) e \(n_1,m_1 \in \N\) tali che
Poiché uno dei due numeri è uguale a \(M\), si ha
Inoltre,
Quindi,
D'altra parte, poiché \(n \le M\) e \(m \le M\), si ha
Dunque
Se \(M \ge 2\), allora \(\log_2 M \ge 1\), quindi
Pertanto,
Quindi, scegliendo \(c_1=1\), \(c_2=4\), \(n_1=2\) e \(m_1=2\) (che danno \(M \ge 2\)), otteniamo
□
Se l'input di un algoritmo è costituito da due numeri naturali \(n,m \ge 1\), la dimensione dell'input soddisfa:
dove
Esempio 11.3: i limiti per due numeri naturali
Per \(n = 37\) e \(m = 200\) si ha \(|I| = 14\) (Esempio 11.1), \(M = 200\), e
Le disuguaglianze delle dimostrazioni delle Osservazioni 11.5 e Osservazione 11.6 diventano
dove tutti i valori sono arrotondati a due cifre decimali.
11.5 Istanza di input costituita da un array di numeri naturali¶
La maggior parte degli algoritmi del corso riceve un array. La sua dimensione dell'input dipende sia dal numero \(n\) di componenti sia dai loro valori.
-
Se l'istanza di input è costituita da un array \(a[1..n]\) di \(n\) numeri naturali, con \(a[i]\ge 1\) per ogni \(i \in \{1,2,\dots,n\}\), la dimensione dell'input è
\[ |I|=\sum_{i=1}^{n}\big(\lfloor \log_2 a[i] \rfloor +1\big), \]che è il numero minimo di bit necessari per rappresentare in binario tutte le componenti dell'array.
11.5.1 Un limite superiore¶
Osservazione 11.7: limite superiore sulla dimensione dell'input di un array
Dato un array \(a[1..n]\) di \(n\) numeri naturali, con \(a[i]\ge 1\) per ogni \(i \in \{1,2,\dots,n\}\), si ha
dove
Dimostrazione
Per dimostrare il risultato, determiniamo \(c \in \R_{>0}\) e \(n_1 \in \N\) tali che
Poiché \(a[i] \le M\) per ogni \(i \in \{1,2,\dots,n\}\), si ha
Quindi,
Inoltre, si ha
Dunque,
Quindi, scegliendo \(c=1\) e \(n_1=1\), otteniamo
□
Se l'input di un algoritmo è costituito da un array \(a[1..n]\) di \(n\) numeri naturali, con \(a[i]\ge 1\) per ogni \(i \in \{1,2,\dots,n\}\), allora la dimensione dell'input soddisfa
dove
Il limite superiore \(n(1+\log_2 M)\) conta ogni componente come se fosse grande quanto il massimo. Quando solo poche componenti sono grandi, è una forte sovrastima, e il limite non può essere sostituito da \(\Theta\).
Esempio 11.4: il limite superiore non è stretto
Dato un array \(a[1..n]\) di \(n\) numeri naturali, con \(a[i]\ge 1\) per ogni \(i \in \{1,2,\dots,n\}\), per dimostrare che, in generale,
è sufficiente fornire un controesempio.
-
Consideriamo la famiglia di array definita da
\[ a[1]=M, \qquad a[2]=a[3]=\cdots=a[n]=1, \]con \(M\ge 2\).
-
Per questi array, la dimensione dell'input è
\[ |I| = \sum_{i=1}^{n}\big(\lfloor \log_2 a[i] \rfloor +1\big) = (\lfloor \log_2 M \rfloor +1)+(n-1)(\lfloor \log_2 1 \rfloor +1). \]Poiché \(\lfloor \log_2 1 \rfloor +1 = 1\), otteniamo
\[ |I| = (\lfloor \log_2 M \rfloor +1)+(n-1) = n+\lfloor \log_2 M \rfloor. \] -
Scegliamo ora \(M=2^n\), in modo che \(\log_2 M=n\). Allora
\[ |I| = n + n = 2n, \qquad \text{mentre} \qquad n(1+\log_2 M)=n(1+n)=n^2+n. \] -
Se avessimo \(2n = \Theta(n^2+n)\), esisterebbero \(c_1 \in \R_{>0}\) e \(n_1 \in \N\) tali che \(c_1(n^2+n) \le 2n\) per ogni \(n \ge n_1\), cioè \(c_1(n+1) \le 2\) per ogni \(n \ge n_1\), il che è falso per \(n\) abbastanza grande.
Quindi, per questa famiglia di array, \(|I|\neq \Theta\big(n(1+\log_2 M)\big)\).
-
Se \(M\ge 2\), allora \(\log_2 M \ge 1\), e quindi \(1+\log_2 M \le 2\log_2 M\). Dunque
\[ n(1+\log_2 M)\le 2n\log_2 M, \]e la dimensione dell'input soddisfa
\[ |I|=O\big(n\log_2 M\big). \] -
Se \(M=1\), allora tutte le componenti dell'array sono uguali a \(1\), e quindi
\[ |I|=\sum_{i=1}^{n}(\lfloor \log_2 1 \rfloor +1)=n. \]
11.5.2 Un limite inferiore¶
Ogni componente dell'array richiede almeno un bit. Questa semplice osservazione dà un limite inferiore che vale per ogni array.
Osservazione 11.8: limite inferiore sulla dimensione dell'input di un array
Dato un array \(a[1..n]\) di \(n\) numeri naturali, con \(a[i]\ge 1\) per ogni \(i \in \{1,2,\dots,n\}\), si ha
Dimostrazione
Per dimostrare il risultato, determiniamo \(c \in \R_{>0}\) e \(n_1 \in \N\) tali che
Poiché \(a[i]\ge 1\) per ogni \(i \in \{1,2,\dots,n\}\), si ha
Quindi,
Quindi, scegliendo \(c=1\) e \(n_1=1\), otteniamo
□
Se l'input di un algoritmo è costituito da un array \(a[1..n]\) di \(n\) numeri naturali, con \(a[i]\ge 1\) per ogni \(i \in \{1,2,\dots,n\}\), allora la dimensione dell'input soddisfa
Esempio 11.5: i limiti per un array
Per l'array \(a = [6, 1, 3, 8]\) dell'Esempio 11.1 si ha \(n = 4\), \(|I| = 10\) e \(M = 8\). Il limite inferiore dell'Osservazione 11.8 e il limite superiore dell'Osservazione 11.7 danno
11.5.3 Componenti di dimensione costante¶
In molte applicazioni le componenti dell'array sono memorizzate con un numero fisso di bit, indipendente da \(n\). In questo caso i due limiti coincidono.
Corollario 11.9: componenti di dimensione costante
Dato un array \(a[1..n]\) di \(n\) numeri naturali, con \(a[i]\ge 1\) per ogni \(i \in \{1,2,\dots,n\}\), se tutte le componenti dell'array possono essere rappresentate usando al più un numero costante fissato di bit, allora
Dimostrazione
Per ipotesi esiste una costante \(c \in \N\) tale che
Quindi,
e dunque \(|I|=O(n)\). Per l'Osservazione 11.8 si ha anche \(|I|=\Omega(n)\), e quindi, per l'Osservazione 11.2,
□
11.5.4 Due varianti dell'input array¶
-
Se l'input è costituito da un numero naturale \(n \ge 1\) e da un array \(a[1..n]\) di \(n\) numeri naturali, con \(a[i]\ge 1\) per ogni \(i \in \{1,2,\dots,n\}\), la dimensione dell'input è
\[ |I|=\big(\lfloor \log_2 n \rfloor +1\big)+\sum_{i=1}^{n}\big(\lfloor \log_2 a[i] \rfloor +1\big). \]Sia
\[ M=\max\{n,a[1],a[2],\dots,a[n]\}. \]Allora si ha
\[ \lfloor \log_2 n \rfloor +1 \le 1+\log_2 M, \]e, per ogni \(i \in \{1,2,\dots,n\}\),
\[ \lfloor \log_2 a[i] \rfloor +1 \le 1+\log_2 M. \]Quindi,
\[ |I| \le (1+\log_2 M)+n(1+\log_2 M) = (n+1)(1+\log_2 M). \]Dunque
\[ |I|=O\big((n+1)(1+\log_2 M)\big). \]Poiché \(n+1\le 2n\) per ogni \(n\ge 1\), otteniamo anche
\[ |I|=O\big(n(1+\log_2 M)\big). \]Inoltre, se \(M\ge 2\), allora
\[ 1+\log_2 M \le 2\log_2 M, \]e quindi
\[ |I|=O\big(n\log_2 M\big). \]Il limite inferiore \(|I| = \Omega(n)\) continua a valere, poiché i soli bit dell'array sono almeno \(n\).
-
Se le componenti dell'array possono essere \(0\), la dimensione dell'input è
\[ |I|=\sum_{i=1}^{n}\big(\lfloor \log_2(\max\{1,a[i]\}) \rfloor +1\big), \]così che ogni componente uguale a \(0\) contribuisce esattamente con \(1\) bit. In questo caso, se poniamo
\[ M=\max\{1,a[1],a[2],\dots,a[n]\}, \]le formule precedenti diventano
\[ |I|=O\big(n(1+\log_2 M)\big), \]e, se \(M\ge 2\),
\[ |I|=O(n\log_2 M). \]Inoltre, continua a valere
\[ |I|=\Omega(n), \]poiché ogni componente contribuisce con almeno \(1\) bit.
In particolare, se tutte le componenti dell'array possono essere rappresentate usando al più un numero costante fissato di bit, allora
\[ |I|=\Theta(n). \]
11.6 Dimensione dell'input e numero di passi¶
Possiamo ora spiegare perché la dimensione dell'input si misura in bit. Il tempo richiesto da un algoritmo si stima contando i suoi passi: nello pseudocodice, un passo è l'esecuzione di una riga, e la condizione di un ciclo while conta come un passo ogni volta che viene valutata. Il numero di passi viene calcolato prima come funzione \(S(n)\) di un parametro naturale \(n\) dell'input, e poi espresso come funzione della dimensione dell'input \(|I|\).
Definizione 11.10: tempo di esecuzione nel caso peggiore
Il tempo di esecuzione nel caso peggiore \(T(|I|)\) di un algoritmo è il massimo numero di passi eseguiti dall'algoritmo su un qualsiasi input \(I\) di una data dimensione \(|I|\).
Definizione 11.11: algoritmi polinomiali ed esponenziali
Un algoritmo è polinomiale se il suo tempo di esecuzione nel caso peggiore soddisfa \(T(|I|)=O(|I|^k)\) per una qualche costante \(k \in \N\).
Un algoritmo è esponenziale se il suo tempo di esecuzione nel caso peggiore soddisfa \(T(|I|)=2^{\Theta(|I|)}\), cioè se esistono \(c_1, c_2 \in \R_{>0}\) e \(k_1 \in \N\) tali che
Il parametro \(n\) può avere due ruoli molto diversi. Può essere il valore di un numero dato in input, oppure il numero di componenti di un array dato in input. L'osservazione seguente mostra come \(n\) è legato a \(|I|\) nei due casi.
Osservazione 11.12: valore di un numero e numero di componenti di un array
-
Se l'input è costituito da un solo numero naturale \(n \ge 1\), allora
\[ 2^{|I|-1} \le n \le 2^{|I|} - 1: \]il valore \(n\) è esponenziale nella dimensione dell'input.
-
Se l'input è costituito da un array \(a[1..n]\) di \(n\) numeri naturali (ed eventualmente dal numero \(n\)), allora
\[ n \le |I|: \]il numero di componenti \(n\) è al più lineare nella dimensione dell'input.
Dimostrazione
-
Poiché \(|I| = \lfloor \log_2 n \rfloor + 1\), si ha \(\lfloor \log_2 n \rfloor = |I| - 1\), cioè
\[ |I| - 1 \le \log_2 n < |I|. \]La funzione \(x \mapsto 2^x\) è strettamente crescente, quindi \(2^{|I|-1} \le n < 2^{|I|}\). Poiché \(n\) è un intero, \(n \le 2^{|I|}-1\).
-
Ogni componente dell'array contribuisce con almeno un bit alla dimensione dell'input (Osservazione 11.8, e lo stesso vale per le componenti uguali a \(0\)), quindi \(|I| \ge n\). □
Quando un numero di passi è limitato da esponenziali con fattori costanti, i fattori costanti possono essere assorbiti nell'esponente.
Osservazione 11.13: fattori costanti nell'esponente
Siano \(a_1, a_2, b_1, b_2 \in \R_{>0}\). Se una funzione \(T\) soddisfa
per ogni numero naturale \(x\) sufficientemente grande, allora \(T(x) = 2^{\Theta(x)}\).
Dimostrazione
Scriviamo i fattori costanti come potenze di \(2\): \(a_1 = 2^{\log_2 a_1}\) e \(a_2 = 2^{\log_2 a_2}\). Allora
e
Entrambe le condizioni valgono per ogni \(x\) sufficientemente grande. Quindi, con \(c_1 = \frac{b_1}{2}\) e \(c_2 = 2b_2\), otteniamo \(2^{c_1 x} \le T(x) \le 2^{c_2 x}\) per ogni \(x\) sufficientemente grande, cioè \(T(x) = 2^{\Theta(x)}\). □
Esempio 11.6: la somma delle componenti di un array
Consideriamo il seguente algoritmo, che calcola la somma delle componenti di un array.
Algoritmo: SumArray
-
Passo 1: numero di passi. Le righe 2, 3 e 8 sono eseguite \(1\) volta ciascuna; la condizione alla riga 4 è valutata \(n+1\) volte (l'ultima volta è falsa); le righe 5 e 6 sono eseguite \(n\) volte ciascuna. Quindi
\[ S(n) = 3 + (n+1) + 2n = 3n + 4, \]per ogni array di \(n\) componenti, qualunque siano i suoi valori.
-
Passo 2: da \(n\) a \(|I|\). Per l'Osservazione 11.12, ogni input di dimensione \(|I|\) ha \(n \le |I|\) componenti, quindi richiede al più \(3|I| + 4\) passi. L'array di \(|I|\) componenti tutte uguali a \(1\) ha dimensione dell'input \(|I|\) e richiede esattamente \(3|I| + 4\) passi. Dunque
\[ T(|I|) = 3|I| + 4 = O(|I|). \] -
Passo 3: conclusione.
SumArrayè polinomiale (lineare) nella dimensione dell'input.
Se invece lo stesso ciclo andasse da \(1\) al valore di un numero \(n\) dato in input, i \(\Theta(n)\) passi sarebbero esponenziali nella dimensione dell'input, perché \(n\) può arrivare a \(2^{|I|}-1\) (Osservazione 11.12).
Punti chiave del capitolo
-
La dimensione dell'input \(|I|\) è il numero di bit necessari per scrivere in binario l'istanza di input.
-
Un numero naturale \(n\): \(|I| = \lfloor \log_2 n \rfloor + 1 = \Theta(\log_2 n)\). Due numeri naturali \(n, m\): \(|I| = \Theta(\log_2 n + \log_2 m) = \Theta(\log_2 \max\{n,m\})\).
-
Un array di \(n\) numeri naturali: \(|I| = \Omega(n)\) e \(|I| = O\big(n(1+\log_2 M)\big)\), con \(M\) la componente massima; il limite superiore in generale non è stretto. Se le componenti hanno un numero costante di bit, \(|I| = \Theta(n)\).
-
Lo stesso numero di passi \(S(n)\) può essere polinomiale o esponenziale in \(|I|\): il valore di un numero è esponenziale nella sua dimensione dell'input, mentre il numero di componenti di un array è al più la sua dimensione dell'input.
Esercizi su questo capitolo: Dimensione dell'input