Ich werde die Funktion umbenennen take_closest, um sie den PEP8-Namenskonventionen anzupassen.
Wenn Sie schnell ausführen und nicht schnell schreiben möchten, minsollte dies nicht die Waffe Ihrer Wahl sein, außer in einem sehr engen Anwendungsfall. Die minLösung muss jede Zahl in der Liste untersuchen und für jede Zahl eine Berechnung durchführen. Mit bisect.bisect_leftstattdessen ist fast immer schneller.
Das "fast" kommt von der Tatsache, bisect_leftdass 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 bisectModul 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 myNumberanhand 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 bisectist fast 20 mal schneller. Bei längeren Listen ist der Unterschied größer.
Was ist, wenn wir das Spielfeld ausgleichen, indem wir die myListzu sortierende Voraussetzung entfernen ? Nehmen wir an, wir sortieren bei jedem take_closest Aufruf eine Kopie der Liste , während die minLösung unverändert bleibt . Unter Verwendung der 200-Punkte-Liste im obigen Test ist die bisectLö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 minimmer noch verloren geht, ist, dass die Sortierung minin hochoptimiertem c-Code erfolgt, während für jedes Element eine Lambda-Funktion aufgerufen werden muss. Mit myListzunehmender Größe wird die minLösung möglicherweise schneller. Beachten Sie, dass wir alles zu seinen Gunsten stapeln mussten, damit die minLösung gewinnt.