Possiamo progettare diversi algoritmi per risolvere uno stesso problema
Un algoritmo può impiegare molto meno tempo di un altro (MergeSort $nlogn$, QuickSort $n^2$), o usare molto meno spazio di un altro (BubbleSort, SelectionSort, InsertionSort, HeapSort, ..)
Abbiamo bisogno di tecniche di analisi che consentano di valutare un algoritmo solo un base alle sue caratteristiche e non a quelle del codice che lo implementa o della macchina su cui è eseguito
Vogliamo una definizione concreta di efficienza:
<aside> 💡 Forza bruta: per molti problemi non triviali, esiste un naturale algoritmo di forza bruta che controlla ogni potenziale soluzione
</aside>
Tipicamente impiega tempo $2^N$ (o peggio) per input di dimensione $N$
Problemi con l’approccio basato sulla ricerca esaustiva nello spazio di tutte le possibili soluzioni (forza bruta):
<aside> 💡 Un algoritmo è efficiente se ha una performance migliore, da un punto di vista analitico, dell’algoritmo di forza bruta
</aside>
Algoritmi che hanno performance migliori rispetto agli algoritmi di forza bruta di solito usano euristiche interessanti e forniscono informazioni rilevanti sulla struttura intrinseca del problema e sulla sua trattabilità computazionale
Proprietà desiderata: quando la dimensione dell’input raddoppia, l’algoritmo dovrebbe risultare più lento solo di un fattore costante $c'$
<aside> 💡 Si dice che un algoritmo impiega tempo polinomiale (poly-time) se quando la dimensione dell’input aumenta di un fattore costante, ad esempio raddoppia, l’algoritmo risulta più lento solo di un fattore costante $c$
</aside>