Ich werde die Funktion umbenennen take_closest
, um sie den PEP8-Namenskonventionen anzupassen.
Wenn Sie schnell ausführen und nicht schnell schreiben möchten, min
sollte dies nicht die Waffe Ihrer Wahl sein, außer in einem sehr engen Anwendungsfall. Die min
Lösung muss jede Zahl in der Liste untersuchen und für jede Zahl eine Berechnung durchführen. Mit bisect.bisect_left
stattdessen ist fast immer schneller.
Das "fast" kommt von der Tatsache, bisect_left
dass die Liste sortiert werden muss, um zu funktionieren. Hoffentlich ist Ihr Anwendungsfall so, dass Sie die Liste einmal sortieren und dann in Ruhe lassen können. Selbst wenn nicht, solange Sie nicht vor jedem Anruf sortieren müssen take_closest
, wird das bisect
Modul wahrscheinlich die Nase vorn haben. Wenn Sie Zweifel haben, versuchen Sie beides und sehen Sie sich den Unterschied in der realen Welt an.
from bisect import bisect_left
def take_closest(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 halbiert wiederholt eine Liste und ermittelt myNumber
anhand des Mittelwerts , in welcher Hälfte sich die Hälfte befinden muss. Dies bedeutet, dass es eine Laufzeit von O (log n) hat, im Gegensatz zur Laufzeit von O (n) der Antwort mit der höchsten Abstimmung . Wenn wir die beiden Methoden vergleichen und beide sortiert liefern myList
, sind dies die Ergebnisse:
$ python -m timeit -s "
vom nächsten Import take_closest
von zufälligen Import Randint
a = Bereich (-1000, 1000, 10) take_closest (a, randint (-1100, 1100))
100000 Schleifen, am besten 3: 2,22 usec pro Schleife
$ python -m timeit -s "
vom nächsten Import mit_min
von zufälligen Import Randint
a = Bereich (-1000, 1000, 10) with_min (a, Randint (-1100, 1100))
10000 Schleifen, am besten 3: 43,9 usec pro Schleife
Also in diesem speziellen Test bisect
ist fast 20 mal schneller. Bei längeren Listen ist der Unterschied größer.
Was ist, wenn wir das Spielfeld ausgleichen, indem wir die myList
zu sortierende Voraussetzung entfernen ? Nehmen wir an, wir sortieren bei jedem take_closest
Aufruf eine Kopie der Liste , während die min
Lösung unverändert bleibt . Unter Verwendung der 200-Punkte-Liste im obigen Test ist die bisect
Lösung immer noch die schnellste, wenn auch nur um etwa 30%.
Dies ist ein seltsames Ergebnis, wenn man bedenkt, dass der Sortierschritt O (n log (n)) ist ! Der einzige Grund, der min
immer noch verloren geht, ist, dass die Sortierung min
in hochoptimiertem c-Code erfolgt, während für jedes Element eine Lambda-Funktion aufgerufen werden muss. Mit myList
zunehmender Größe wird die min
Lösung möglicherweise schneller. Beachten Sie, dass wir alles zu seinen Gunsten stapeln mussten, damit die min
Lösung gewinnt.