Se vuoi dire veloce da eseguire come oppone a quick-to-write, min
dovrebbe non essere la vostra arma di scelta, tranne in un caso d'uso molto stretto. La soluzione min
deve esaminare ogni numero nell'elenco e eseguire un calcolo per ciascun numero. L'utilizzo di bisect.bisect_left
invece è quasi sempre più veloce.
Il "quasi" deriva dal fatto che bisect_left
richiede che l'elenco sia ordinato per funzionare. Spero che il tuo caso d'uso sia tale da poter ordinare l'elenco una sola volta e lasciarlo da solo. Anche se non lo è, purché non sia necessario prima di ordinare ogni volta che si chiama takeClosest
, è probabile che il modulo bisect
esca in cima. Se sei in dubbio, prova entrambi e guarda la differenza nel mondo reale.
from bisect import bisect_left
def takeClosest(myList, myNumber):
"""
Assumes myList is sorted. Returns closest value to myNumber.
If two numbers are equally close, return the smallest number.
"""
pos = bisect_left(myList, myNumber)
if pos == 0:
return myList[0]
if pos == len(myList):
return myList[-1]
before = myList[pos - 1]
after = myList[pos]
if after - myNumber < myNumber - before:
return after
else:
return before
Bisect funziona ripetutamente dimezzare un elenco e scoprire che la metà myNumber
deve essere in guardando il valore medio. Ciò significa che ha un tempo di esecuzione di O (log n) in contrapposizione al tempo di esecuzione O (n) dello highest voted answer. Se mettiamo a confronto i due metodi e forniamo entrambi con un ordinato myList
, questi sono i risultati:
$ python -m timeit -s "
from closest import takeClosest
from random import randint
a = range(-1000, 1000, 10)" "takeClosest(a, randint(-1100, 1100))"
100000 loops, best of 3: 2.22 usec per loop
$ python -m timeit -s "
from closest import with_min
from random import randint
a = range(-1000, 1000, 10)" "with_min(a, randint(-1100, 1100))"
10000 loops, best of 3: 43.9 usec per loop
Quindi, in questo particolare test, bisect
è quasi 20 volte più veloce. Per gli elenchi più lunghi, la differenza sarà maggiore.
Cosa succede se livelliamo il campo di gioco rimuovendo il presupposto che deve essere ordinato myList
? Diciamo che ordiniamo una copia della lista ogni volta che viene chiamatotakeClosest
, lasciando inalterata la soluzione min
. Utilizzando l'elenco di 200 voci nel test precedente, la soluzione bisect
è ancora la più veloce, anche se solo del 30% circa.
Questo è uno strano risultato, considerando che il passo di smistamento è O (n log (n))! L'unica ragione per cui lo min
è ancora in perdita è che l'ordinamento viene eseguito in un codice c ottimizzato, mentre min
deve arrangiare chiamando una funzione lambda per ogni elemento. Con lo sviluppo della dimensione myList
, la soluzione min
sarà più veloce. Si noti che abbiamo dovuto impilare tutto a suo favore per la soluzione min
per vincere.
che dire anche restituire l'indice che questo è accaduto nella lista. –
Possibile duplicato di [trovare l'indice di un elemento più vicino al valore in un elenco che non è interamente ordinato] (https://stackoverflow.com/questions/9706041/finding-index-of-an-item-closest-to-the -valore-in-a-lista-thats-non-interamente-sort) –
@ sancho.s Ben avvistato. Anche se le risposte a questa domanda sono molto meglio di quelle sull'altra domanda. Quindi voterò per chiudere l'altro come duplicato di questo. –