Quicksort dengan Python
Dalam kehidupan nyata, kita harus selalu menggunakan jenis bawaan yang disediakan oleh Python. Namun, memahami algoritme quicksort itu instruktif.
Tujuan saya di sini adalah untuk memecah subjek sedemikian rupa sehingga mudah dipahami dan dapat ditiru oleh pembaca tanpa harus kembali ke materi referensi.
Algoritme quicksort pada dasarnya adalah sebagai berikut:
- Pilih titik data pivot.
- Pindahkan semua titik data yang kurang dari (di bawah) poros ke posisi di bawah poros - pindahkan yang lebih besar dari atau sama dengan (di atas) poros ke posisi di atasnya.
- Terapkan algoritme ke area di atas dan di bawah poros
Jika data didistribusikan secara acak, memilih titik data pertama sebagai pivot sama dengan pemilihan acak.
Contoh yang bisa dibaca:
Pertama, mari kita lihat contoh yang dapat dibaca yang menggunakan komentar dan nama variabel untuk menunjuk ke nilai antara:
def quicksort(xs):
"""Given indexable and slicable iterable, return a sorted list"""
if xs:
pivot = xs[0]
below = [i for i in xs[1:] if i < pivot]
above = [i for i in xs[1:] if i >= pivot]
return quicksort(below) + [pivot] + quicksort(above)
else:
return xs
Untuk menyatakan kembali algoritme dan kode yang ditunjukkan di sini - kami memindahkan nilai di atas pivot ke kanan, dan nilai di bawah pivot ke kiri, lalu meneruskan partisi tersebut ke fungsi yang sama untuk diurutkan lebih lanjut.
Golf:
Ini bisa dimainkan hingga 88 karakter:
q=lambda x:x and q([i for i in x[1:]if i<=x[0]])+[x[0]]+q([i for i in x[1:]if i>x[0]])
Untuk melihat bagaimana kami sampai di sana, pertama-tama ambil contoh kami yang dapat dibaca, hapus komentar dan dokumen, dan temukan pivot di tempat:
def quicksort(xs):
if xs:
below = [i for i in xs[1:] if i < xs[0]]
above = [i for i in xs[1:] if i >= xs[0]]
return quicksort(below) + [xs[0]] + quicksort(above)
else:
return xs
Sekarang temukan di bawah dan di atas, di tempat:
def quicksort(xs):
if xs:
return (quicksort([i for i in xs[1:] if i < xs[0]] )
+ [xs[0]]
+ quicksort([i for i in xs[1:] if i >= xs[0]]))
else:
return xs
Sekarang, mengetahui bahwa and
mengembalikan elemen sebelumnya jika salah, jika tidak benar, itu mengevaluasi dan mengembalikan elemen berikut, kita memiliki:
def quicksort(xs):
return xs and (quicksort([i for i in xs[1:] if i < xs[0]] )
+ [xs[0]]
+ quicksort([i for i in xs[1:] if i >= xs[0]]))
Karena lambda mengembalikan satu epresi, dan kami telah menyederhanakan menjadi satu ekspresi (meskipun semakin tidak terbaca) sekarang kami dapat menggunakan lambda:
quicksort = lambda xs: (quicksort([i for i in xs[1:] if i < xs[0]] )
+ [xs[0]]
+ quicksort([i for i in xs[1:] if i >= xs[0]]))
Dan untuk mengurangi contoh kita, persingkat nama fungsi dan variabel menjadi satu huruf, dan hilangkan spasi yang tidak diperlukan.
q=lambda x:x and q([i for i in x[1:]if i<=x[0]])+[x[0]]+q([i for i in x[1:]if i>x[0]])
Perhatikan bahwa lambda ini, seperti kebanyakan kode golf, adalah gaya yang agak buruk.
Quicksort di tempat, menggunakan skema Partisi Hoare
Implementasi sebelumnya membuat banyak daftar tambahan yang tidak perlu. Jika kami dapat melakukan ini di tempat, kami akan menghindari pemborosan ruang.
Implementasi di bawah ini menggunakan skema partisi Hoare, yang dapat Anda baca lebih lanjut di wikipedia (tetapi kami tampaknya telah menghapus hingga 4 kalkulasi redundan per partition()
panggilan dengan menggunakan semantik while-loop alih-alih do-while dan memindahkan langkah-langkah penyempitan ke akhir loop sementara luar.).
def quicksort(a_list):
"""Hoare partition scheme, see https://en.wikipedia.org/wiki/Quicksort"""
def _quicksort(a_list, low, high):
if low < high:
p = partition(a_list, low, high)
_quicksort(a_list, low, p)
_quicksort(a_list, p+1, high)
def partition(a_list, low, high):
pivot = a_list[low]
while True:
while a_list[low] < pivot:
low += 1
while a_list[high] > pivot:
high -= 1
if low >= high:
return high
a_list[low], a_list[high] = a_list[high], a_list[low]
low += 1
high -= 1
_quicksort(a_list, 0, len(a_list)-1)
return a_list
Tidak yakin apakah saya mengujinya dengan cukup teliti:
def main():
assert quicksort([1]) == [1]
assert quicksort([1,2]) == [1,2]
assert quicksort([1,2,3]) == [1,2,3]
assert quicksort([1,2,3,4]) == [1,2,3,4]
assert quicksort([2,1,3,4]) == [1,2,3,4]
assert quicksort([1,3,2,4]) == [1,2,3,4]
assert quicksort([1,2,4,3]) == [1,2,3,4]
assert quicksort([2,1,1,1]) == [1,1,1,2]
assert quicksort([1,2,1,1]) == [1,1,1,2]
assert quicksort([1,1,2,1]) == [1,1,1,2]
assert quicksort([1,1,1,2]) == [1,1,1,2]
Kesimpulan
Algoritma ini sering diajarkan dalam kursus ilmu komputer dan diminta untuk wawancara kerja. Ini membantu kita memikirkan tentang rekursi dan bagi-dan-taklukkan.
Quicksort tidak terlalu praktis dengan Python karena algoritme timsort bawaan kami cukup efisien, dan kami memiliki batas rekursi. Kami berharap untuk mengurutkan daftar di tempat dengan list.sort
atau membuat daftar baru yang diurutkan dengan sorted
- keduanya mengambil argumen key
dan reverse
.
my_list = list1 + list2 + ...
. Atau buka paket daftar ke daftar barumy_list = [*list1, *list2]