Mengapa quicksort lebih baik daripada algoritma pengurutan lainnya dalam praktik?

308

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 )O(nlogn)O(n2)O(nlogn)

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 )O(nlogn)O(nlogn)

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.

Janoma
sumber
2
Urutkan gabungan adalah dalam kasus terburuk, dan mengurutkan array bilangan bulat di mana ada batasan yang diketahui pada ukuran bilangan bulat dapat dilakukan dalam waktu dengan waktu penghitungan. O ( n )O(nlogn)O(n)
Carl Mummert
13
sorting-algorithms.com memiliki perbandingan algoritma sorting yang cukup menyeluruh.
Joe
2
Pembaruan Iklan 1: Saya berpendapat bahwa Anda dapat memiliki analisis yang ketat atau asumsi yang realistis. Saya belum melihat keduanya. Misalnya, sebagian besar analisis formal hanya menghitung perbandingan.
Raphael
9
Pertanyaan ini memenangkan kontes baru-baru ini di programmer.SE !
Raphael
3
Pertanyaan menarik. Saya menjalankan beberapa tes beberapa waktu lalu dengan data acak dan implementasi naif dari quick sort dan merge sort. Kedua algoritma bekerja cukup baik untuk set data kecil (hingga 100.000 item) tetapi setelah penggabungan itu ternyata jauh lebih baik. Ini tampaknya bertentangan dengan asumsi umum bahwa quick sort sangat bagus dan saya masih belum menemukan penjelasan untuk itu. Satu-satunya ide yang bisa saya kemukakan adalah bahwa biasanya istilah quick sort digunakan untuk algoritma yang lebih kompleks seperti intro sort, dan implementasi naif quick sort dengan pivot acak tidak begitu bagus.
Giorgio

Jawaban:

215

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?O

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:

  1. 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

    • Quicksort:11.667(n+1)ln(n)1.74n18.74
    • Mergesort:12.5nln(n)
    • Heapsort: 16nln(n)+0.01n
    • Insertionsort: [ sumber ]2.25n2+7.75n3ln(n) Runtime dari beberapa algoritma penyortiran

    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:
    Runtime dari beberapa algoritma penyortiran untuk input kecil
    [ sumber ]

  2. 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

    • Quicksort: perbandingan dan swap rata-rata12nln(n)13nln(n)
    • Mergesort: , tetapi hingga array mengakses (mergesort bukan berbasis swap, jadi kami tidak dapat menghitungnya).8,66 n ln ( n )1.44nln(n)8.66nln(n)
    • Insertionsort: perbandingan dan rata-rata.114n214n2

    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

Sebastian
sumber
8
Hasil tipe 2. dapat diubah menjadi hasil tipe 1. dengan memasukkan konstanta yang bergantung pada mesin. Karena itu, saya berpendapat 2. adalah pendekatan yang unggul.
Raphael
2
@Raphael +1. Saya kira Anda berasumsi bahwa ketergantungan mesin juga tergantung pada implementasi, bukan? Maksud saya, mesin cepat + implementasi yang buruk mungkin tidak terlalu efisien.
Janoma
2
@ Janoma Saya mengasumsikan algoritma yang dianalisis diberikan dalam bentuk yang sangat terperinci (karena analisisnya terperinci) dan implementasinya sebanyak mungkin dengan surat. Tapi ya, implementasinya juga akan menjadi faktor.
Raphael
3
Sebenarnya, analisis tipe 2 lebih rendah dalam praktiknya. Mesin dunia nyata sangat rumit sehingga hasil dari tipe 2 tidak dapat secara layak diterjemahkan menjadi tipe 1. Bandingkan dengan tipe 1: merencanakan waktu menjalankan eksperimental membutuhkan waktu 5 menit kerja.
Jules
4
@ Jules: "merencanakan waktu running eksperimental" bukan tipe 1; ini bukan jenis analisis formal dan tidak dapat ditransfer ke mesin lain. Itu sebabnya kami melakukan analisis formal.
Raphael
78

Ada beberapa poin yang dapat dibuat mengenai pertanyaan ini.

Quicksort biasanya cepat

O(n2)

n1O(nlogn)

Quicksort biasanya lebih cepat daripada kebanyakan jenis

O(nlogn)O(n2)n

O(nlogn)O(nBlog(nB))B

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.

O(nBlogMB(nB))Mk

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.

nO(logn)O(n)

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.

Alex ten Brink
sumber
3
Komentar terakhir Anda sangat berharga. Seorang kolega saya saat ini menganalisis implementasi Quicksort di bawah distribusi input yang berbeda. Beberapa dari mereka memecah banyak duplikat, misalnya.
Raphael
4
O(n2)
8
"Tidak ada teori di balik ini, kebetulan lebih cepat." Pernyataan itu sangat tidak memuaskan dari sudut pandang ilmiah. Bayangkan Newton berkata, "Kupu-kupu terbang, apel jatuh: tidak ada teori di balik ini, apel kebetulan jatuh."
David Richerby
2
@Alex ten Brink, apa yang Anda maksud dengan “Secara khusus, algoritmanya tidak menyadari cache ”?
Hibou57
4
@ David Richerby, “Pernyataan itu sangat tidak memuaskan dari sudut pandang ilmiah”: dia mungkin hanya menyaksikan fakta tanpa berpura-pura kita harus senang dengannya. Beberapa keluarga algoritma mengalami kekurangan formalisasi penuh; fungsi hashing adalah contoh kasus.
Hibou57
45

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.

Dai
sumber
17
@Raphael Untuk menjelaskannya, Timsort menggabungkan jenis untuk asimptotik plus jenis penyisipan untuk input pendek plus beberapa heuristik untuk mengatasi secara efisien dengan data yang memiliki semburan yang sudah disortir (yang sering terjadi dalam praktik). Dai: selain algoritme, list.sortmanfaat 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.
Gilles
1
@Dai: Setidaknya Anda bisa menggambarkan dengan input seperti apa (resp. Distribusinya) dalam kondisi apa (RAM rendah, lakukan satu implementasi paralel, ...) Anda memperoleh hasil.
Raphael
7
Kami menguji pada daftar angka acak, dan sebagian diurutkan, sepenuhnya diurutkan, dan diurutkan terbalik. Itu adalah kursus tahun pertama pengantar, jadi itu bukan studi empiris yang mendalam. Tetapi kenyataan bahwa sekarang secara resmi digunakan untuk mengurutkan array di Java SE 7 dan pada platform Android memang berarti sesuatu.
Dai
3
Ini juga dibahas di sini: cstheory.stackexchange.com/a/927/74
Jukka Suomela
34

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.

svick
sumber
5
Itu penjelasan yang bisa diperdebatkan: mergesort juga ramah terhadap cache.
Dmytro Korduban
2
Saya pikir jawaban ini pada dasarnya benar, tetapi inilah beberapa detail youtube.com/watch?v=aMnn0Jq0J-E
rgrig
3
mungkin konstanta multiplikatif untuk kompleksitas waktu kasus rata-rata dari quick-sort juga lebih baik (terlepas dari faktor cache yang telah Anda sebutkan).
Kaveh
1
Poin yang Anda sebutkan tidak terlalu penting, dibandingkan dengan properti cepat lainnya yang bagus.
MMS
1
@ Kaveh: "konstanta multiplikasi untuk kompleksitas waktu kasus rata-rata dari quick-sort juga lebih baik" Apakah Anda memiliki data tentang ini?
Giorgio
29

Yang lain sudah mengatakan bahwa runtime rata-rata asimptotik Quicksort lebih baik (dalam konstanta) daripada algoritma pengurutan lainnya (dalam pengaturan tertentu).

O(nlogn)

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.

k10

Raphael
sumber
20

O(nlgn)

ps: lebih tepatnya, lebih baik daripada algoritma lain tergantung tugas. Untuk beberapa tugas, mungkin lebih baik menggunakan algoritma penyortiran lainnya.

Lihat juga:

Kaveh
sumber
3
@ Janoma ini adalah masalah apa bahasa dan kompiler yang Anda gunakan. Hampir semua bahasa fungsional (ML, Lisp, Haskell) dapat melakukan optimasi yang mencegah tumpukan tumbuh, dan kompiler yang lebih pintar untuk bahasa imperatif dapat melakukan hal yang sama (GCC, G ++, dan saya percaya MSVC semua melakukan ini). Pengecualian penting adalah Java, yang tidak akan pernah melakukan optimasi ini, jadi masuk akal di Jawa untuk menulis ulang rekursi Anda sebagai iterasi.
Rafe Kettler
4
@ JD, Anda tidak dapat menggunakan pengoptimalan panggilan ekor dengan quicksort (setidaknya tidak sepenuhnya), karena panggilan itu sendiri dua kali. Anda dapat mengoptimalkan panggilan kedua, tetapi bukan panggilan pertama.
svick
1
@ Janoma, Anda tidak benar-benar membutuhkan implementasi rekursif. Sebagai contoh, jika Anda melihat implementasi fungsi qsort di C, ia tidak menggunakan panggilan rekursif, dan karenanya implementasinya menjadi jauh lebih cepat.
Kaveh
1
Heapsort juga tersedia, mengapa QS sering lebih cepat?
Kevin
6
23240
16

Θ(n2)Θ(nlogn)

Alasan kedua adalah bahwa ia melakukan in-placepenyortiran 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:

12,30,21,8,6,9,1,7. The merge sort algorithm works as follows:

(a) 12,30,21,8    6,9,1,7  //divide stage
(b) 12,30   21,8   6,9   1,7   //divide stage
(c) 12   30   21   8   6   9   1   7   //Final divide stage
(d) 12,30   8,21   6,9   1,7   //Merge Stage
(e) 8,12,21,30   .....     // Analyze this stage

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.

0x0
sumber
1
Tapi apa yang membuat konstanta begitu kecil?
svick
1
@vick Karena mereka diurutkan in-placeyaitu, tidak ada memori tambahan yang diperlukan.
0x0
Θ(nlgn)
15

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.)

Erwan Legrand
sumber
Saya telah melihat sesuatu yang mirip dengan ini karena kami terus-menerus menambahkan / menggunakan untuk memasukkan ke dalam kumpulan data yang sudah diurutkan. Anda dapat mengatasi ini rata-rata dengan menggunakan quicksort acak (dan terkejut dengan jenis lambat lambat acak dan acak), atau Anda bisa mentolerir jenis selalu lebih lambat yang tidak pernah membutuhkan jumlah waktu mengejutkan untuk menyelesaikannya. Kadang-kadang Anda memerlukan stabilitas semacam juga. Java telah beralih dari penggunaan penggabungan ke varian quicksort.
Rob
@Rob Ini tidak akurat. Java masih menggunakan varian mergesort (Timsort) hingga hari ini. Itu memang menggunakan varian quicksort juga (dual-pivot quicksort).
Erwan Legrand
14

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.

MMS
sumber
3
Pernahkah Anda melihat implementasi Quicksort berulang di tempat, berulang? Mereka banyak hal tetapi tidak "mudah".
Raphael
2
Nomor 2 tidak menjawab pertanyaan saya sama sekali, dan angka 1 dan 3 perlu dibenarkan, menurut pendapat saya.
Janoma
@ Raphael: Mereka SANGAT mudah. Jauh lebih mudah untuk menerapkan pengurutan cepat di tempat menggunakan array, bukan pointer. Dan tidak perlu berulang untuk berada di tempat.
MMS
Array untuk digabung tidak terlalu buruk. Setelah Anda memindahkan satu item dari tumpukan sumber ke tumpukan tujuan, itu tidak perlu lagi ada di sana. Jika Anda menggunakan array dinamis, ada overhead memori konstan saat penggabungan.
Oskar Skog
@ 1 Mergesort juga tidak cocok. @ 2 Apa yang mendefinisikan efisien? Saya suka semacam penggabungan karena sangat sederhana namun efisien menurut saya. @ 3 Tidak relevan ketika Anda menyortir data dalam jumlah besar, dan mengharuskan algoritma tersebut diterapkan secara efisien.
Oskar Skog
11

Dalam kondisi apa sebenarnya algoritma pemilahan spesifik paling cepat?

Θ(log(n)2)Θ(nlog(n)2)

Θ(nk)Θ(nm)k=2#number_of_Possible_valuesm=#maximum_length_of_keys

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.

Θ(n)

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)

Θ(n)Θ(n2)Θ(nlog(n)2)jangka waktu kasus terburuk diketahui, atau mungkin mencoba jenis sisir. Saya tidak yakin apakah shell sort atau comb sort akan bekerja dengan cukup baik dalam latihan.

Θ(log(n))Θ(n)Θ(n)Θ(log(n))Θ(n2)Θ(n)Θ(n)Θ(log(n))Θ(nlog(n))

Θ(nlog(n))

Petunjuk implementasi untuk quicksort:

Θ(n)Θ(log(n))Θ(nlogk(k1))

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.

Θ(k)Θ(log(k))Θ(1)Θ(n)

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

Θ(log(n))Θ(n)

ps: Seseorang perlu membantu saya dengan pemformatan teks.

Franki
sumber
(5): Implementasi pengurutan Apple memeriksa satu proses dengan urutan naik atau turun baik di awal maupun di akhir array terlebih dahulu. Ini sangat cepat jika tidak ada banyak elemen seperti itu, dan dapat menangani elemen ini dengan sangat efektif jika ada lebih dari n / ln dari mereka. Menggabungkan dua array yang diurutkan dan mengurutkan hasilnya, dan Anda mendapatkan penggabungan
gnasher729
8

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.

ab

fernand0
sumber
5
Argumen Anda tentang quicksort vs merge sort tidak menampung air. Quicksort dimulai dengan gerakan besar, kemudian membuat gerakan kecil dan kecil (sekitar setengah lebih besar di setiap langkah). Urutkan gabungan dimulai dengan langkah kecil, lalu membuat gerakan yang lebih besar dan lebih besar (sekitar dua kali lebih besar di setiap langkah). Ini tidak menunjukkan bahwa yang satu lebih efisien daripada yang lain.
Gilles