Salta al contenuto
Note per Studenti Ricerca lineare e binaria

Ricerca lineare e binaria

In questa pagina 5

Problema: data una sequenza v e un valore x, trovare un indice i con v[i] == x, o segnalare che non c'è (convenzione: restituire -1).

Ricerca lineare

Funziona su qualunque sequenza, anche non ordinata.

python
def ricerca_lineare(v, x):
    for i in range(len(v)):
        if v[i] == x:
            return i
    return -1
  • Caso migliore O(1)O(1), caso peggiore O(n)O(n) (elemento assente).
  • Restituisce la prima occorrenza.
  • È ciò che fanno x in v e v.index(x).

Ricerca binaria

Richiede v ordinata. Si confronta x con l'elemento centrale e si scarta metà dell'intervallo a ogni passo.

python
def ricerca_binaria(v, x):
    lo, hi = 0, len(v) - 1          # intervallo di ricerca [lo, hi], estremi inclusi
    while lo <= hi:
        m = (lo + hi) // 2
        if v[m] == x:
            return m
        elif v[m] < x:
            lo = m + 1              # x, se c'è, sta a destra di m
        else:
            hi = m - 1              # x, se c'è, sta a sinistra di m
    return -1

Invariante: se x è in v, allora si trova in v[lo..hi]. Il ciclo termina perché a ogni giro l'intervallo si riduce di almeno un elemento (m + 1, m - 1); quando lo > hi l'intervallo è vuoto e x non c'è.

Costo: dopo kk iterazioni restano circa n/2kn / 2^k elementi, quindi al più ⌊log⁡2n⌋+1\lfloor \log_2 n \rfloor + 1 iterazioni: O(log⁡n)O(\log n). Con n=106n = 10^6 bastano 20 confronti.

Esempio su v = [2, 5, 8, 12, 16, 23, 38], x = 23:

lo hi m v[m] decisione
0 6 3 12 12 < 23, lo = 4
4 6 5 23 trovato

Versione ricorsiva

python
def ricerca_binaria_ric(v, x, lo=0, hi=None):
    if hi is None:
        hi = len(v) - 1
    if lo > hi:
        return -1
    m = (lo + hi) // 2
    if v[m] == x:
        return m
    if v[m] < x:
        return ricerca_binaria_ric(v, x, m + 1, hi)
    return ricerca_binaria_ric(v, x, lo, m - 1)

Equazione del costo T(n)=T(n/2)+O(1)T(n) = T(n/2) + O(1) (vedi RicorsioneFunzioni che chiamano se stesse: caso base e passo ricorsivo, stack delle chiamate, esempi su numeri, stringhe e liste, ricorsione multipla e suo costo, divide et impera, confronto con l'iterazione.Ricorsione →, Complessità computazionaleCosto di un algoritmo in funzione della dimensione dell'input; caso peggiore, migliore e medio; notazione O-grande e classi di crescita; come contare i passi di cicli e ricorsioni; costo delle operazioni Python.Complessità computazionale →). Passare gli indici e non v[:m]: lo slicing costerebbe O(n)O(n) a ogni chiamata.

Quale usare

Modulo bisect

python
import bisect
v = [2, 5, 8, 8, 12]
bisect.bisect_left(v, 8)     # 2: primo indice dove inserire 8 mantenendo l'ordine
bisect.bisect_right(v, 8)    # 4
bisect.insort(v, 7)          # inserimento ordinato: [2, 5, 7, 8, 8, 12]

bisect_left(v, x) restituisce un indice i con v[i] == x se x c'è (e i < len(v)).

Errori tipici

  • Ricerca binaria su una lista non ordinata: risultati sbagliati senza errori.
  • while lo < hi con estremi inclusi: l'ultimo elemento candidato non viene controllato.
  • lo = m o hi = m invece di m + 1 / m - 1: ciclo infinito quando l'intervallo ha due elementi.
  • return dentro il ciclo lineare nel ramo else (si esce dopo il primo confronto).

Teoria collegata