Dalam kursus algoritma standar kita diajarkan bahwa quicksort rata-rata adalah dan dalam kasus terburuk. Pada saat yang sama, algoritma pengurutan lainnya dipelajari yaitu dalam kasus terburuk (seperti mergesort dan heapsort ), dan bahkan waktu linier dalam kasus terbaik (seperti bubblesort ) tetapi dengan beberapa kebutuhan memori tambahan.O ( n 2 ) O ( n log n )
Setelah sekilas melihat beberapa waktu berlari , wajar untuk mengatakan bahwa quicksort tidak seefisien yang lainnya.
Juga, pertimbangkan bahwa siswa belajar dalam kursus pemrograman dasar bahwa rekursi tidak benar-benar baik secara umum karena dapat menggunakan terlalu banyak memori, dll. Oleh karena itu (dan meskipun ini bukan argumen nyata), ini memberikan gagasan bahwa quicksort mungkin tidak sangat bagus karena merupakan algoritma rekursif.
Mengapa, kemudian, apakah quicksort mengungguli algoritma pengurutan lainnya dalam praktek? Apakah itu ada hubungannya dengan struktur data dunia nyata ? Apakah itu ada hubungannya dengan cara memori bekerja di komputer? Saya tahu bahwa beberapa ingatan jauh lebih cepat daripada yang lain, tetapi saya tidak tahu apakah itu alasan sebenarnya untuk kinerja kontra-intuitif ini (bila dibandingkan dengan perkiraan teoritis).
Pembaruan 1: jawaban kanonik mengatakan bahwa konstanta yang terlibat dalam dari kasus rata-rata lebih kecil daripada konstanta yang terlibat dalam algoritma . Namun, saya belum melihat pembenaran yang tepat dari ini, dengan perhitungan yang tepat, bukan hanya ide-ide intuitif saja.O ( n log n )
Dalam kasus apa pun, sepertinya perbedaan nyata terjadi, seperti beberapa jawaban menyarankan, pada tingkat memori, di mana implementasi mengambil keuntungan dari struktur internal komputer, menggunakan, misalnya, bahwa memori cache lebih cepat daripada RAM. Diskusi ini sudah menarik, tapi aku masih ingin melihat lebih detail sehubungan dengan manajemen memori, karena tampaknya bahwa para jawaban hubungannya dengan itu.
Pembaruan 2: Ada beberapa halaman web yang menawarkan perbandingan algoritma pengurutan, beberapa lebih bagus daripada yang lain (yang paling utama adalah sorting-algorithms.com ). Selain memberikan bantuan visual yang bagus, pendekatan ini tidak menjawab pertanyaan saya.
sumber
Jawaban:
Jawaban singkat
Argumen efisiensi cache telah dijelaskan secara rinci. Selain itu, ada argumen intrinsik, mengapa Quicksort cepat. Jika diimplementasikan seperti dengan dua "crossing pointers", misalnya di sini , loop dalam memiliki tubuh yang sangat kecil. Karena ini adalah kode yang paling sering dieksekusi, ini terbayar.
Jawaban panjang
Pertama-tama,
Kasus Rata - rata tidak ada!
Karena kasus terbaik dan terburuk sering ekstrem jarang terjadi dalam praktik, analisis kasus rata-rata dilakukan. Tetapi setiap analisis kasus rata mengasumsikan distribusi input ! Untuk pengurutan, pilihan yang umum adalah model permutasi acak (diasumsikan diam-diam di Wikipedia).
Mengapa -Notasi?HAI
Membuang konstanta dalam analisis algoritma dilakukan karena satu alasan utama: Jika saya tertarik pada waktu berjalan yang tepat , saya memerlukan biaya (relatif) dari semua operasi dasar yang terlibat (bahkan masih mengabaikan masalah caching, pipelining di prosesor modern ...). Analisis matematis dapat menghitung seberapa sering setiap instruksi dieksekusi, tetapi waktu menjalankan instruksi tunggal bergantung pada detail prosesor, misalnya apakah perkalian integer 32-bit membutuhkan waktu sebanyak penambahan.
Ada dua jalan keluar:
Perbaiki beberapa model mesin.
Ini dilakukan dalam seri buku Don Knuth "The Art of Computer Programming" untuk komputer "tipikal" buatan yang ditemukan oleh penulis. Dalam volume 3 Anda menemukan hasil rata-rata kasus yang tepat untuk banyak algoritma pengurutan, misalnya
Hasil ini menunjukkan bahwa Quicksort tercepat. Tapi, itu hanya terbukti pada mesin buatan Knuth, itu tidak selalu berarti apa pun untuk mengatakan PC x86 Anda. Perhatikan juga bahwa algoritma berhubungan secara berbeda untuk input kecil:
[ sumber ]
Menganalisis operasi dasar abstrak .
Untuk penyortiran berbasis perbandingan, ini biasanya swap dan perbandingan kunci . Dalam buku Robert Sedgewick, misalnya "Algoritma" , pendekatan ini diupayakan. Anda temukan di sana
Seperti yang Anda lihat, ini tidak memungkinkan perbandingan algoritma sebagai analisis runtime yang tepat, tetapi hasilnya independen dari detail mesin.
Distribusi input lainnya
Seperti disebutkan di atas, kasus rata-rata selalu sehubungan dengan beberapa distribusi input, sehingga orang dapat mempertimbangkan yang lain selain permutasi acak. Misalnya penelitian telah dilakukan untuk Quicksort dengan elemen yang sama dan ada artikel yang bagus tentang fungsi sortir standar di Jawa
sumber
Ada beberapa poin yang dapat dibuat mengenai pertanyaan ini.
Quicksort biasanya cepat
Quicksort biasanya lebih cepat daripada kebanyakan jenis
Alasan efisiensi cache ini adalah bahwa ia memindai input secara linear dan secara linear mem-partisi input. Ini artinya kita dapat memanfaatkan setiap cache load yang kita lakukan dengan sebaik-baiknya sambil membaca setiap angka yang kita muat di cache sebelum menukar cache itu dengan yang lain. Secara khusus, algoritma ini tidak memperhatikan cache, yang memberikan kinerja cache yang baik untuk setiap level cache, yang merupakan kemenangan lain.
Quicksort biasanya lebih cepat dari Mergesort
Perbandingan ini sepenuhnya tentang faktor-faktor konstan (jika kita mempertimbangkan kasus khas). Secara khusus, pilihannya adalah antara pilihan pivot untuk Quicksort yang suboptimal versus salinan seluruh input untuk Mergesort (atau kompleksitas algoritma yang diperlukan untuk menghindari penyalinan ini). Ternyata yang pertama lebih efisien: tidak ada teori di balik ini, itu terjadi lebih cepat.
Terakhir, perhatikan bahwa Quicksort sedikit sensitif terhadap input yang berada dalam urutan yang benar, dalam hal ini Quicksort dapat melewati beberapa swap. Mergesort tidak memiliki optimasi seperti itu, yang juga membuat Quicksort sedikit lebih cepat dibandingkan dengan Mergesort.
Gunakan jenis yang sesuai dengan kebutuhan Anda
Kesimpulannya: tidak ada algoritma penyortiran yang selalu optimal. Pilih mana yang sesuai dengan kebutuhan Anda. Jika Anda membutuhkan algoritme yang tercepat untuk sebagian besar kasus, dan Anda tidak keberatan itu mungkin menjadi agak lambat dalam kasus yang jarang terjadi, dan Anda tidak perlu jenis yang stabil, gunakan Quicksort. Jika tidak, gunakan algoritma yang sesuai dengan kebutuhan Anda dengan lebih baik.
sumber
Dalam salah satu tutorial pemrograman di universitas saya, kami meminta siswa untuk membandingkan kinerja quicksort, mergesort, jenis penyisipan vs list.sort bawaan Python (disebut Timsort ). Hasil percobaan sangat mengejutkan saya sejak built-in list.sort tampil jauh lebih baik daripada algoritma pengurutan lainnya, bahkan dengan contoh yang dengan mudah membuat quicksort, mergesort crash. Jadi terlalu dini untuk menyimpulkan bahwa penerapan quicksort yang biasa adalah yang terbaik dalam praktiknya. Tapi saya yakin ada implementasi quicksort yang jauh lebih baik, atau beberapa versi hybrid di luar sana.
Ini adalah artikel blog yang bagus dari David R. MacIver menjelaskan Timsort sebagai bentuk mergesort adaptif.
sumber
list.sort
manfaat dari menjadi fungsi bawaan yang dioptimalkan oleh para profesional. Perbandingan yang lebih adil akan membuat semua fungsi ditulis dalam bahasa yang sama pada tingkat upaya yang sama.Saya pikir salah satu alasan utama mengapa QuickSort sangat cepat dibandingkan dengan algoritma pengurutan lainnya adalah karena itu ramah cache. Ketika QS memproses segmen array, QS mengakses elemen di awal dan akhir segmen, dan bergerak menuju pusat segmen.
Jadi, ketika Anda mulai, Anda mengakses elemen pertama dalam array dan sepotong memori ("lokasi") dimuat ke dalam cache. Dan ketika Anda mencoba mengakses elemen kedua, itu (kemungkinan besar) sudah ada di cache, jadi sangat cepat.
Algoritma lain seperti heapsort tidak berfungsi seperti ini, mereka melompat dalam array, yang membuatnya lebih lambat.
sumber
Yang lain sudah mengatakan bahwa runtime rata-rata asimptotik Quicksort lebih baik (dalam konstanta) daripada algoritma pengurutan lainnya (dalam pengaturan tertentu).
Perhatikan bahwa ada banyak varian Quicksort (lihat misalnya disertasi Sedgewick). Mereka tampil berbeda pada distribusi input yang berbeda (seragam, hampir diurutkan, hampir diurutkan terbalik, banyak duplikat, ...), dan algoritma lainnya mungkin lebih baik untuk beberapa.
sumber
ps: lebih tepatnya, lebih baik daripada algoritma lain tergantung tugas. Untuk beberapa tugas, mungkin lebih baik menggunakan algoritma penyortiran lainnya.
Lihat juga:
Perbandingan quick-sort dengan algoritma pengurutan lainnya
Perbandingan heap-sort dengan algoritma pengurutan lainnya
sumber
Alasan kedua adalah bahwa ia melakukan
in-place
penyortiran dan bekerja dengan sangat baik dengan lingkungan memori virtual.UPDATE:: (Setelah komentar Janoma dan Svick)
Untuk mengilustrasikan ini dengan lebih baik, izinkan saya memberikan contoh menggunakan Gabung Sortir (karena Gabung sort adalah algoritma pengurutan berikutnya yang diadopsi secara luas setelah pengurutan cepat, saya pikir) dan memberi tahu Anda dari mana konstanta tambahan berasal (sesuai dengan pengetahuan saya dan mengapa saya berpikir Sortir cepat lebih baik):
Pertimbangkan seqence berikut:
Jika Anda benar-benar memperhatikan bagaimana tahap terakhir terjadi, 12 pertama dibandingkan dengan 8 dan 8 lebih kecil sehingga berjalan lebih dulu. Sekarang 12 adalah LAGI dibandingkan dengan 21 dan 12 berjalan berikutnya dan seterusnya dan seterusnya. Jika Anda mengambil penggabungan akhir, yaitu 4 elemen dengan 4 elemen lainnya, itu membuat banyak perbandingan EXTRA sebagai konstanta yang TIDAK dikeluarkan dalam Quick Sort. Inilah alasan mengapa quick sort lebih disukai.
sumber
in-place
yaitu, tidak ada memori tambahan yang diperlukan.Pengalaman saya bekerja dengan data dunia nyata adalah bahwa quicksort adalah pilihan yang buruk . Quicksort berfungsi baik dengan data acak, tetapi data dunia nyata paling sering tidak acak.
Kembali pada 2008 saya melacak bug perangkat lunak yang menggantung ke penggunaan quicksort. Beberapa saat kemudian saya menulis implikasi sederhana jenis penyisipan, quicksort, tumpukan heap dan menggabungkan semacam dan menguji ini. Jenis gabungan saya mengungguli semua yang lain saat mengerjakan kumpulan data besar.
Sejak itu, merge sort adalah algoritma pengurutan pilihan saya. Itu elegan. Sederhana untuk diterapkan. Ini adalah jenis yang stabil. Itu tidak merosot ke perilaku kuadrat seperti quicksort. Saya beralih ke jenis penyisipan untuk mengurutkan array kecil.
Dalam banyak kesempatan saya menemukan diri saya berpikir bahwa implementasi yang diberikan bekerja sangat baik untuk quicksort hanya untuk mengetahui bahwa itu sebenarnya bukan quicksort. Kadang-kadang implementasi beralih antara quicksort dan algoritma lain dan kadang-kadang tidak menggunakan quicksort sama sekali. Sebagai contoh, fungsi qsort () GLibc sebenarnya menggunakan semacam gabungan. Hanya jika mengalokasikan ruang kerja gagal apakah itu kembali ke quicksort di tempat yang disebut kode komentar "algoritma yang lebih lambat" .
Sunting: Memprogram bahasa seperti Java, Python dan Perl juga menggunakan jenis gabungan, atau lebih tepatnya turunan, seperti Timsort atau jenis gabungan untuk set besar dan jenis penyisipan untuk set kecil. (Java juga menggunakan quicksort dual-pivot yang lebih cepat daripada quicksort biasa.)
sumber
1 - Penyortiran cepat ada di tempatnya (tidak perlu memori tambahan, selain jumlah yang konstan.)
2 - Penyortiran cepat lebih mudah diterapkan daripada algoritma penyortiran efisien lainnya.
3 - Penyortiran cepat memiliki faktor konstan yang lebih kecil dalam waktu berjalannya daripada algoritma penyortiran efisien lainnya.
Pembaruan: Untuk penggabungan penggabungan, Anda perlu melakukan beberapa "penggabungan," yang membutuhkan array tambahan untuk menyimpan data sebelum menggabungkan; tetapi dalam penyortiran cepat, Anda tidak. Karena itulah pengurutan cepat dilakukan. Ada juga beberapa perbandingan ekstra yang dibuat untuk menggabungkan yang meningkatkan faktor konstan dalam jenis gabungan.
sumber
Dalam kondisi apa sebenarnya algoritma pemilahan spesifik paling cepat?
3) Apakah struktur data yang mendasarinya terdiri dari elemen terkait? Ya -> allways digunakan di tempat menggabungkan semacam. Ada dua hal mudah untuk menerapkan ukuran tetap atau adaptif (alias alami) bottom-up di tempat menggabungkan berbagai jenis arities yang berbeda untuk struktur data yang ditautkan, dan karena mereka tidak pernah perlu menyalin seluruh data dalam setiap langkah dan mereka tidak pernah memerlukan rekursi juga, mereka adalah lebih cepat daripada jenis berbasis perbandingan umum lainnya, bahkan lebih cepat daripada jenis cepat.
5) Dapatkah ukuran data yang mendasarinya terikat ke ukuran kecil hingga sedang? mis. Apakah n <10.000 ... 100.000.000 (tergantung pada arsitektur dan struktur data yang mendasarinya)? Ya -> gunakan bitonic sort atau Batcher odd-even mergesort. Kebagian 1)
Petunjuk implementasi untuk quicksort:
2) Terdapat varian quicksort dari bawah ke atas, iteratif, tetapi AFAIK, mereka memiliki batas ruang dan waktu asimptotik yang sama dengan yang dari atas ke bawah, dengan sisi bawah tambahan yang sulit untuk diimplementasikan (misalnya, mengelola antrian secara eksplisit). Pengalaman saya adalah bahwa untuk tujuan praktis apa pun, itu tidak pernah layak dipertimbangkan.
Petunjuk implementasi untuk mergesort:
1) bottum-up mergesort selalu lebih cepat daripada top-down mergesort, karena tidak memerlukan panggilan rekursi.
2) mergesort yang sangat naif dapat dipercepat dengan menggunakan buffer ganda dan mengganti buffer alih-alih menyalin data kembali dari array temporal setelah setiap langkah.
3) Untuk banyak data dunia nyata, mergesort adaptif jauh lebih cepat daripada mergesort ukuran tetap.
Dari apa yang saya tulis, jelas bahwa quicksort sering bukan algoritma tercepat, kecuali ketika semua kondisi berikut ini berlaku:
1) ada lebih dari beberapa nilai yang mungkin
2) struktur data yang mendasarinya tidak terhubung
3) kita tidak perlu pesanan yang stabil
4) data cukup besar sehingga run-time asymptotic sedikit sub-optimal sorter bitonic atau Batcher odd-even mergesort
5) data hampir tidak diurutkan dan tidak terdiri dari bagian yang lebih besar sudah diurutkan
6) kita dapat mengakses urutan data secara bersamaan dari berbagai tempat
ps: Seseorang perlu membantu saya dengan pemformatan teks.
sumber
Sebagian besar metode penyortiran harus memindahkan data dalam langkah-langkah singkat (misalnya, menggabungkan jenis membuat perubahan secara lokal, lalu menggabungkan bagian kecil data ini, kemudian menggabungkan yang lebih besar ...). Karena itu, Anda memerlukan banyak pergerakan data jika data jauh dari tujuannya.
sumber