Complessità asintotica

La complessità asintotica di un algoritmo descrive l'andamento temporale per input di grandezza .

Sequenza di Fibonacci

Per esempio, data la sequenza di Fibonacci definita come esistono diversi algoritmi per trovare l'-esimo numero:

CorrettezzaComplessità temporaleComplessità spaziale
BinetIncorretto
RicorsivoCorretto
Iterativo con arrayCorretto
IterativoCorretto

Binet

La formula di Binet lega la sezione aurea all'-esimo numero di Fibonacci: dove e sono le soluzioni a .

Di conseguenza, l'algoritmo risulta semplicemente essere:

Fib1(int n) -> int
	return 1/sqrt(5) * (pow(phi, n) - pow(1 - phi, n))

Complessità temporale e spaziale

In questo caso, dato che l'algoritmo è composto da un return e l'unico dato salvato è .

Correttezza

Per verificane la correttezza, bisogna dimostrare che:

Proseguendo per induzione, i casi base sono:

  • Per ,
  • Per ,

Per cui va verificato il passo induttivo: di conseguenza, dati i casi base, si suppone che la formula sia valida per e , per cui: che va trasformata in , cioè: e siccome e sono le uniche soluzioni del sistema, la formula è verificata.

Seppur dimostrato, Fib1 rimane incorretto perchè l'imprecisione aumenta con il crescere di .

Ricorsivo

Nel caso ricorsivo, l'algoritmo risulta essere:

Fib2(int n) -> int
	if n <= 2
		return 1
	return Fib2(n - 1) + Fib2(n - 2)

Complessità temporale e spaziale

I passaggi che fa questo algoritmo sono: dove è per l'if/return, mentre è per l'if più l'else/return.

Per trovare il valore di si può usare un albero ricorsivo . Per , l'albero sarà: Albero ricorsivo di Fibonacci con n = 5

Contando i pesi sui nodi dell'albero, si può ricavare il costo: dove è il numero di nodi interni e è il numero di foglie.

Si può quindi dimostrare che:

  1. , per induzione:

    Tra i casi base, se:

    Per cui, supponendo che sia vero per ipotesi induttiva, allora: dato che non possiede altri sottoalberi oltre che e . Di conseguenza:

  2. Tra i casi base, se:

    Supponendo vero per ipotesi induttiva, allora: visto che per la precedente dimostrazione .

Perciò, la complessità temporale di Fib2 sarà: e la complessità spaziale .

È anche possibile che la complessità cresce più velocemente che esponenzialmente: infatti per induzione, il caso base per e il passo induttivo per :

Iterativo con array

Sfruttando un array, si possono salvare i valori di senza doverli ricalcolare su :

Fib3(int n) -> int
	int F[n]
	F[1] = F[2] = 1
	for i = 3 to n
		F[i] = F[i-1] + F[i-2]
	return F[n]

Complessità temporale e spaziale

Il numero di istruzioni eseguite sono dove sono per l'inizializzazione di F e il return, mentre:

  • sul contenuto del ciclo: eseguito per e per il resto delle volte
  • sulla condizione: per , per il resto delle volte e una alla fine per

Mentre lo spazio occupato sarà , dato che viene salvato per ogni .

Iterativo

La precedente versione si può ottimizzare ulteriormente, salvando solamente e :

Fib4(int n) -> int
	int f1 = 1, f2 = 1, f
	for i = 3 to n
		f = f1 + f2
		f1 = f2
		f2 = f
	return f

per cui rimane che , mentre la complessità spaziale diventa .