Saya akan mengganti nama fungsi take_closest
agar sesuai dengan konvensi penamaan PEP8.
Jika yang Anda maksud adalah eksekusi cepat dan bukan penulisan cepat, min
seharusnya tidak menjadi senjata pilihan Anda, kecuali dalam satu kasus penggunaan yang sangat sempit. The min
solusi perlu memeriksa setiap nomor dalam daftar dan melakukan perhitungan untuk setiap nomor. bisect.bisect_left
Sebaliknya, menggunakan hampir selalu lebih cepat.
"Hampir" berasal dari fakta yang bisect_left
mengharuskan daftar harus diurutkan untuk bekerja. Mudah-mudahan, use case Anda sedemikian rupa sehingga Anda dapat mengurutkan daftar sekali dan kemudian membiarkannya sendiri. Meskipun tidak, selama Anda tidak perlu menyortir sebelum setiap kali Anda menelepon take_closest
, bisect
modul tersebut kemungkinan akan keluar di atas. Jika Anda ragu, coba keduanya dan lihat perbedaan dunia nyata.
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 bekerja dengan berulang kali membagi dua daftar dan mencari tahu bagian mana yang myNumber
harus diambil dengan melihat nilai tengahnya. Ini berarti ia memiliki waktu berjalan O (log n) sebagai kebalikan dari O (n) waktu berjalan jawaban tertinggi yang dipilih . Jika kami membandingkan kedua metode dan menyediakan keduanya dengan diurutkan myList
, ini adalah hasilnya:
$ python -m timeit -s "
dari impor take_closest terdekat
dari randint impor acak
a = range (-1000, 1000, 10) "" take_closest (a, randint (-1100, 1100)) "
100000 loop, terbaik 3: 2,22 usec per loop
$ python -m timeit -s "
dari impor terdekat with_min
dari randint impor acak
a = range (-1000, 1000, 10) "" with_min (a, randint (-1100, 1100)) "
10.000 loop, terbaik dari 3: 43.9 USD per loop
Jadi dalam tes khusus ini, bisect
hampir 20 kali lebih cepat. Untuk daftar yang lebih panjang, perbedaannya akan lebih besar.
Bagaimana jika kita meratakan lapangan bermain dengan menghapus prasyarat yang myList
harus diurutkan? Katakanlah kita mengurutkan salinan daftar setiap kali take_closest
dipanggil, sementara membiarkan min
solusinya tidak berubah. Menggunakan daftar 200-item dalam tes di atas, bisect
solusinya masih yang tercepat, meskipun hanya sekitar 30%.
Ini adalah hasil yang aneh, mengingat langkah penyortirannya adalah O (n log (n)) ! Satu-satunya alasan min
yang masih kalah adalah bahwa penyortiran dilakukan dalam kode c yang sangat dioptimalkan, sementara min
harus bersusah payah memanggil fungsi lambda untuk setiap item. Seiring myList
bertambahnya ukuran, min
solusinya pada akhirnya akan lebih cepat. Perhatikan bahwa kami harus menumpuk segalanya agar min
solusi dapat menang.