Quicksort ve yığın sıralaması


Yanıtlar:


61

Bu yazıda bazı analizler var.

Ayrıca Wikipedia'dan:

Quicksort'un en doğrudan rakibi yığın sıralamasıdır. Yığın sıralaması tipik olarak hızlı sıralamadan biraz daha yavaştır, ancak en kötü durumda çalışma süresi her zaman Θ (nlogn) 'dur. Quicksort genellikle daha hızlıdır, ancak kötü bir vaka algılandığında heapsort'a geçen introsort varyantı dışında en kötü durum performansı şansı kalır. Yığın sırasının gerekli olacağı önceden biliniyorsa, onu doğrudan kullanmak, introsort'un ona geçmesini beklemekten daha hızlı olacaktır.


13
Tipik uygulamalarda, ne hızlı sıralama ne de yığın sıralaması kararlı türler değildir.
MjrKusanagi

@DVK, bağlantınıza göre cs.auckland.ac.nz/~jmor159/PLDS210/qsort3.html , yığın sıralaması n = 100 için 2.842 karşılaştırma alır, ancak n = 500 için 53.113 karşılaştırma alır. Ve bu, n = 500 ile n = 100 arasındaki oranın 18 kez olduğunu ve yığın sıralama algoritmasını O (N logN) karmaşıklığıyla EŞLEŞTİRMEDİĞİ anlamına gelir. Sanırım yığın sıralama uygulamalarının içinde bir tür hata olması muhtemeldir.
DU Jiaen

@DUJiaen - Ç () büyük N'de asimptotik davranışları hakkında ve olası bir çarpanına sahiptir unutmayın
DVK

Bu, çarpanla ilgili DEĞİLDİR. Bir algoritmanın karmaşıklığı O (N log N) ise, bir Zaman (N) = C1 * N * log (N) trendi izlemelidir. Ve Zaman (500) / Zaman (100) alırsanız, C1'in ortadan kalkacağı ve sonucun (500 log500) / (100 log100) = 6.7'ye kapatılması gerektiği açıktır.Ama bağlantınızdan 18, yani çok fazla ölçek dışı.
DU Jiaen

2
Bağlantı
kesildi

128

Heapsort, O (N log N) garantilidir, Quicksort'taki en kötü durumdan çok daha iyidir. Heapsort, sıralı verileri Mergesort'un gerektirdiği şekilde yerleştirmek için başka bir dizi için daha fazla belleğe ihtiyaç duymaz. Öyleyse neden ticari uygulamalar Quicksort ile uyumludur? Quicksort'un diğer uygulamalara göre bu kadar özel olan nesi var?

Algoritmaları kendim test ettim ve Quicksort'un gerçekten özel bir şeye sahip olduğunu gördüm. Hızlı, Heap and Merge algoritmalarından çok daha hızlı çalışır.

Quicksort'un sırrı şudur: Neredeyse gereksiz eleman değişimleri yapmaz. Takas zaman alıcıdır.

Heapsort ile, tüm verileriniz önceden sipariş edilmiş olsa bile, diziyi sipariş etmek için öğelerin% 100'ünü değiştireceksiniz.

Mergesort ile durum daha da kötü. Başka bir dizideki öğelerin% 100'ünü yazacaksınız ve veriler zaten sıralı olsa bile orijinal diziye geri yazacaksınız.

Quicksort ile önceden sipariş edilenleri değiştirmezsiniz. Verileriniz tamamen sıralanırsa, neredeyse hiçbir şeyi değiştirmezsiniz! En kötü durum hakkında çok fazla telaş olmasına rağmen, pivot seçiminde, dizinin ilk veya son elemanını almak dışında herhangi bir gelişme bundan kaçınabilir. İlk, son ve orta eleman arasında ara elemandan bir pivot alırsanız, en kötü durumdan kaçınmak yeterlidir.

Quicksort'ta üstün olan en kötü durum değil, en iyi durumdur! En iyi durumda, aynı sayıda karşılaştırma yaparsınız, ama neredeyse hiçbir şeyi değiştirmezsiniz. Ortalama durumda, Heapsort ve Mergesort'ta olduğu gibi öğelerin bir kısmını değiştirirsiniz, ancak tüm öğeleri değiştirmezsiniz. Quicksort'a en iyi zamanı veren budur. Daha az takas, daha fazla hız.

Bilgisayarımdaki C # 'da aşağıdaki uygulama, serbest bırakma modunda çalışıyor, Array.Sort'u orta pivot ile 3 saniye ve geliştirilmiş pivot ile 2 saniye olarak sırala (evet, iyi bir pivot elde etmek için bir ek yük var).

static void Main(string[] args)
{
    int[] arrToSort = new int[100000000];
    var r = new Random();
    for (int i = 0; i < arrToSort.Length; i++) arrToSort[i] = r.Next(1, arrToSort.Length);

    Console.WriteLine("Press q to quick sort, s to Array.Sort");
    while (true)
    {
        var k = Console.ReadKey(true);
        if (k.KeyChar == 'q')
        {
            // quick sort
            Console.WriteLine("Beg quick sort at " + DateTime.Now.ToString("HH:mm:ss.ffffff"));
            QuickSort(arrToSort, 0, arrToSort.Length - 1);
            Console.WriteLine("End quick sort at " + DateTime.Now.ToString("HH:mm:ss.ffffff"));
            for (int i = 0; i < arrToSort.Length; i++) arrToSort[i] = r.Next(1, arrToSort.Length);
        }
        else if (k.KeyChar == 's')
        {
            Console.WriteLine("Beg Array.Sort at " + DateTime.Now.ToString("HH:mm:ss.ffffff"));
            Array.Sort(arrToSort);
            Console.WriteLine("End Array.Sort at " + DateTime.Now.ToString("HH:mm:ss.ffffff"));
            for (int i = 0; i < arrToSort.Length; i++) arrToSort[i] = r.Next(1, arrToSort.Length);
        }
    }
}

static public void QuickSort(int[] arr, int left, int right)
{
    int begin = left
        , end = right
        , pivot
        // get middle element pivot
        //= arr[(left + right) / 2]
        ;

    //improved pivot
    int middle = (left + right) / 2;
    int
        LM = arr[left].CompareTo(arr[middle])
        , MR = arr[middle].CompareTo(arr[right])
        , LR = arr[left].CompareTo(arr[right])
        ;
    if (-1 * LM == LR)
        pivot = arr[left];
    else
        if (MR == -1 * LR)
            pivot = arr[right];
        else
            pivot = arr[middle];
    do
    {
        while (arr[left] < pivot) left++;
        while (arr[right] > pivot) right--;

        if(left <= right)
        {
            int temp = arr[right];
            arr[right] = arr[left];
            arr[left] = temp;

            left++;
            right--;
        }
    } while (left <= right);

    if (left < end) QuickSort(arr, left, end);
    if (begin < right) QuickSort(arr, begin, right);
}

10
Hayır üzerinde düşünceler için +1. farklı sıralama algoritmaları için gerekli olan takas, okuma / yazma işlemleri
ycy

2
Herhangi bir deterministik, sabit zamanlı pivot seçim stratejisi için, en kötü durumu O (n ^ 2) üreten bir dizi bulabilirsiniz. Sadece minimum olanı ortadan kaldırmak yeterli değildir. Belli bir pecrentile bandında bulunan pivotları güvenilir bir şekilde seçmelisiniz.
Antimony

1
El kodlu hızlı sıralama ve C # yerleşik Array.sort arasındaki simülasyonlarınız için çalıştırdığınız kodun tam olarak bu olup olmadığını merak ediyorum. Bu kodu test ettim ve tüm testlerimde en iyi ihtimalle elle kodlanmış hızlı sıralama, Array.sort ile aynıydı. Bunu test ederken kontrol ettiğim bir şey, rastgele dizinin iki özdeş kopyasını yapmaktı. Sonuçta, belirli bir randomizasyon, başka bir randomizasyondan potansiyel olarak daha uygun olabilir (en iyi duruma doğru eğilimli). Bu yüzden her biri boyunca aynı setleri inceledim. Array.sort her seferinde berabere veya yendi (sürüm build btw)
Chris

1
Bir ders kitabından çok saf bir uygulama olmadığı sürece, birleştirme sıralaması öğelerin% 100'ünü kopyalamak zorunda değildir. Bunu uygulamak basittir, böylece bunların yalnızca% 50'sini kopyalamanız gerekir (iki birleştirilmiş dizinin sol tarafı). Kopyalamayı gerçekten iki öğeyi "değiş tokuş etmek" zorunda kalana kadar ertelemek de önemsizdir, böylece önceden sıralanmış verilerle herhangi bir bellek ek yükünüz olmaz. Yani% 50 bile aslında en kötü durumdur ve bununla% 0 arasında herhangi bir şeye sahip olabilirsiniz.
ddekany

1
@MarquinhoPeli Sıralanan listenin boyutuna kıyasla yalnızca% 50 daha fazla kullanılabilir belleğe ihtiyacınız olduğunu,% 100'e değil, yaygın bir yanılgı gibi görünüyor. Bu yüzden en yüksek bellek kullanımından bahsediyordum. Bir bağlantı veremem, ancak bir dizinin halihazırda sıralanmış iki yarısını yerinde birleştirmeye çalışıp çalışmadığınızı görmek kolaydır (yalnızca sol yarısında henüz tüketmediğiniz öğelerin üzerine yazma sorunu vardır). Tüm sıralama işlemi sırasında ne kadar bellek kopyalamanız gerektiği başka bir sorudur, ancak açıkçası en kötü durum herhangi bir sıralama algoritması için% 100'ün altında olamaz.
ddekany

15

Çoğu durumda, hızlı ve biraz daha hızlı olmak önemsizdir ... sadece ara sıra beklemenin yavaş olmasını asla istemezsiniz. Yavaş durumlardan kaçınmak için QuickSort'u ayarlayabilseniz de, temel QuickSort'un zarafetini kaybedersiniz. Bu yüzden, çoğu şey için, aslında HeapSort'u tercih ediyorum ... onu tamamen basit zarafetiyle uygulayabilir ve asla yavaş bir tür elde edemezsiniz.

Çoğu durumda maksimum hız istediğiniz durumlarda, QuickSort HeapSort yerine tercih edilebilir, ancak ikisi de doğru cevap olmayabilir. Hız açısından kritik durumlar için, durumun ayrıntılarını yakından incelemeye değer. Örneğin, hız açısından kritik kodumun bazılarında, verilerin önceden sıralanması veya yakın sıralanması çok yaygındır (genellikle ya yukarı ve aşağı hareket eden VEYA birbirinin karşısına yukarı ve aşağı hareket eden birden çok ilgili alanı indeksliyor, yani bir kez sıraladığınızda, diğerleri ya sıralanır ya da ters sıralanır ya da kapatılır ... her ikisi de QuickSort'u öldürebilir). Bu durumda, ben ikisini de uygulamadım ... bunun yerine Dijkstra's SmoothSort'u uyguladım ... halihazırda sıralandığında veya yakın sıralandığında O (N) olan bir HeapSort varyantı ... çok zarif değil, anlaşılması çok kolay değil, ama hızlı ... okuhttp://www.cs.utexas.edu/users/EWD/ewd07xx/EWD796a.PDF kodlaması biraz daha zor bir şey istiyorsanız.


6

Quicksort-Heapsort yerinde hibritler de gerçekten ilginçtir, çünkü çoğu en kötü durumda n * log n karşılaştırmasına ihtiyaç duyar (asimptotiklerin ilk terimine göre optimaldirler, bu nedenle en kötü durum senaryolarından kaçınırlar. of Quicksort), O (log n) ekstra alan ve önceden sıralı veri kümesine göre Quicksort'un iyi davranışının en az "yarısını" korurlar. Son derece ilginç bir algoritma Dikert ve Weiss tarafından http://arxiv.org/pdf/1209.4214v1.pdf adresinde sunulmuştur :

  • Rastgele bir sqrt (n) öğesi örneğinin medyanı olarak bir pivot p seçin (bu, Tarjan & co algoritması aracılığıyla en fazla 24 sqrt (n) karşılaştırmasında veya çok daha kıvrımlı örümcek aracılığıyla 5 sqrt (n) karşılaştırmasında yapılabilir. -Schonhage'ın fabrika algoritması);
  • Quicksort'un ilk adımında olduğu gibi dizinizi iki bölüme ayırın;
  • En küçük parçayı yığınlayın ve her sol çocuğun kardeşinden daha büyük bir değere sahip olduğu bir yığını kodlamak için O (log n) ekstra bitleri kullanın;
  • Yığının kökünü özyinelemeli olarak çıkarın, kökün bıraktığı boşluğu yığının bir yaprağına ulaşıncaya kadar eleyin, ardından dizinin diğer kısmından alınan uygun bir elemanla boşluğu doldurun;
  • Dizinin kalan sırasız kısmı üzerinde yineleyin (tam medyan olarak p seçilirse, hiç özyineleme yoktur).

2

Comp. arasında quick sortve merge sorther ikisi de yerinde sıralama türü olduğundan, hızlı sıralama için wrost durumunun çalışma süresi arasında bir fark vardır O(n^2)ve yığın sıralama için hareketsizdir O(n*log(n))ve ortalama bir veri miktarı için hızlı sıralama daha yararlı olacaktır. Rastgele algoritma olduğu için doğru ans elde etme olasılığı. daha kısa sürede seçtiğiniz pivot elemanının konumuna bağlı olacaktır.

Yani bir

İyi görüş: L ve G boyutlarının her biri 3s / 4'ten küçük

Kötü çağrı: L ve G'den birinin boyutu 3s / 4'ten büyük

küçük miktar için eklemeli sıralamaya gidebiliriz ve çok büyük miktarda veri için yığın sıralamaya gidebiliriz.


Yerinde sıralama ile birleştirme sıralaması uygulanabilse de, uygulama karmaşıktır. AFAIK, çoğu birleştirme sıralama uygulaması yerinde değildir, ancak kararlıdır.
MjrKusanagi

2

Heapsort, en kötü çalışan O (n * log (n)) durumuna sahip olma avantajına sahiptir, bu nedenle, hızlı sıralamanın kötü performans göstermesinin muhtemel olduğu durumlarda (çoğunlukla sıralı veri kümeleri genel olarak) yığın sıralaması çok tercih edilir.


4
Quicksort, zayıf bir pivot seçme yöntemi seçilirse yalnızca çoğunlukla sıralanmış bir veri kümesinde kötü performans gösterir. Yani, kötü pivot seçme yöntemi, pivot olarak her zaman ilk veya son öğeyi seçmek olacaktır. Her seferinde rastgele bir pivot seçilirse ve tekrarlanan öğeleri işlemek için iyi bir yöntem kullanılırsa, en kötü durumdaki hızlı sıralama şansı çok düşüktür.
Justin Peel

1
@Justin - Bu çok doğru, saf bir uygulama üzerine konuşuyordum.
zellio

1
@Justin: Doğru, ancak küçük de olsa büyük bir yavaşlama şansı her zaman var. Bazı uygulamalar için, daha yavaş olsa bile O (n log n) davranışını garantilemek isteyebilirim.
David Thornley

2

Mimari düzeyine giderseniz ... önbellekte kuyruk veri yapısını kullanırız. Böylece kuyrukta bulunanlar sıralanır Hızlı sıralamada olduğu gibi diziyi herhangi bir uzunluğa bölme konusunda hiçbir sorunumuz yok ... sırala (dizi kullanarak) öyle olabilir ki, üst önbellekte bulunan alt dizide bulunmayabilir ve daha sonra onu önbellek belleğine getirmek zorunda kalabilir ... Bu hızlı sıralama en iyisidir !! 😀


1

Yığın sıralaması bir yığın oluşturur ve ardından maksimum öğeyi tekrar tekrar çıkarır. En kötü durumu O (n log n).

Ancak, hızlı sıralamanın en kötü halini , yani O (n2) 'yi görürseniz, hızlı sıralamanın büyük veriler için pek de iyi bir seçim olmayacağını fark edersiniz.

Yani bu, sıralamayı ilginç kılıyor; Bugün bu kadar çok sıralama algoritmasının yaşama sebebinin hepsinin en iyi yerlerinde 'en iyi' olmaları olduğuna inanıyorum. Örneğin, kabarcık sıralama, veriler sıralanırsa hızlı sıralama gerçekleştirebilir. Veya sıralanacak öğeler hakkında bir şeyler biliyorsak, muhtemelen daha iyisini yapabiliriz.

Bu, sorunuza doğrudan cevap vermeyebilir, iki sentimi eklerim diye düşündüm.


1
Asla kabarcıklı sıralama kullanmayın. Verilerinizin sıralanacağını makul bir şekilde düşünüyorsanız, o zaman araya göre sıralamayı kullanabilir, hatta sıralı olup olmadıklarını görmek için verileri test edebilirsiniz. Baloncuklar kullanmayın.
vy32

Çok büyük bir RASGELE veri kümeniz varsa, en iyi bahsiniz hızlı sıralamadır. Kısmen sipariş edilmişse, o zaman değil, ancak büyük veri kümeleriyle çalışmaya başlarsanız, en azından onlar hakkında bu kadarını bilmelisiniz.
Kobor42

1

Yığın Sıralama, çok büyük girdilerle uğraşırken güvenli bir bahistir. Asimptotik analiz, en kötü durumda Heapsort'un büyüme sırasını ortaya çıkarır Big-O(n logn)ki bu, en Big-O(n^2)kötü durum olarak Quicksort'tan daha iyidir . Ancak, Heapsort , çoğu makinede pratikte iyi uygulanan hızlı bir sıralamadan biraz daha yavaştır. Heapsort aynı zamanda kararlı bir sıralama algoritması değildir.

Yığın sırasının pratikte hızlı sıraya göre daha yavaş olmasının nedeni , veri öğelerinin nispeten yakın depolama konumlarında olduğu quicksort'ta daha iyi referans yerelliği (" https://en.wikipedia.org/wiki/Locality_of_reference ") nedeniyledir. Güçlü bir referans bölgesi sergileyen sistemler, performans optimizasyonu için harika adaylardır. Ancak yığın sıralama, daha büyük sıçramalarla ilgilenir. Bu, hızlı sıralamayı daha küçük girdiler için daha uygun hale getirir.


3
Hızlı sıralama da kararlı değil.
Antimony

1

Bana göre yığın sıralaması ve hızlı sıralama arasında çok temel bir fark vardır: ikincisi bir özyineleme kullanır. Özyinelemeli algoritmalarda yığın, özyineleme sayısı ile büyür. Bu n'nin küçük olması önemli değil , ama şu anda n = 10 ^ 9 ile iki matrisi sıralıyorum !!. Program yaklaşık 10 GB ram alır ve herhangi bir ekstra bellek bilgisayarımın sanal disk belleğine geçiş yapmaya başlamasına neden olur. Diskim bir RAM disktir, ancak yine de ona geçiş yapmak hızda büyük bir fark yaratır . Bu yüzden, programcı tarafından önceden bilinmeyen boyuta sahip ayarlanabilir boyut matrislerini ve parametrik olmayan istatistiksel sıralama türünü içeren C ++ ile kodlanmış bir istatistik paketinde, çok büyük veri matrisleriyle kullanımda gecikmeleri önlemek için yığın sırasını tercih ederim.


2
Ortalama olarak yalnızca O (oturum açma) belleğine ihtiyacınız vardır. Özyineleme ek yükü önemsizdir, pivotlarla şanssız olmadığınızı varsayarsak, bu durumda endişelenecek daha büyük sorunlarınız olur.
Antimony

Quicksort mutlaka yinelemeli değildir. Aslında, yinelemeli bir algoritmayı yinelemeli olmayan bir algoritmaya dönüştürmek her zaman mümkündür. Elbette, QS'nin klasik sunumlarının tümü özyinelemeyi içerir, ancak pratikte mutlaka böyle değildir.
gniourf_gniourf

0

Basit terimlerle >> HeapSort, QuickSort'un "O (n log n)" ortalama çalıştırma süresinin tersine, "O (n log n)" için ~ en kötü durumda ~ çalışma süresini garanti etmiştir. QuickSort genellikle pratikte kullanılır, çünkü tipik olarak daha hızlıdır, ancak HeapSort, bilgisayarınızın belleğine sığmayan büyük dosyaları sıralamanız gerektiğinde harici sıralama için kullanılır.


-1

Orijinal soruyu cevaplamak ve diğer yorumlardan bazılarını burada ele almak için:

Birbirlerine karşı nasıl yığıldıklarını görmek için seçme, hızlı, birleştirme ve yığın sıralama uygulamalarını karşılaştırdım. Cevap, hepsinin dezavantajları olmasıdır.

TL; DR: Hızlı, en iyi genel amaçlı sıralama (makul derecede hızlı, kararlı ve çoğunlukla yerinde) Şahsen ben kararlı bir sıralamaya ihtiyacım olmadığı sürece yığın sıralamayı tercih ederim.

Seçim - N ^ 2 - Gerçekten sadece 20 elementten daha azı için iyidir, o zaman daha iyi performans gösterir. Verileriniz halihazırda sıralanmadıysa veya neredeyse öyle olmadıkça. N ^ 2 gerçekten çok hızlı bir şekilde yavaşlar.

Hızlı, benim deneyimime göre, aslında her zaman o kadar hızlı değil . Hızlı sıralamayı genel bir tür olarak kullanmak için bonuslar, makul derecede hızlı ve kararlı olmasıdır. Aynı zamanda yerinde bir algoritmadır, ancak genellikle yinelemeli olarak uygulandığı için ek yığın alanı kaplar. Ayrıca O (n log n) ve O (n ^ 2) arasında bir yere düşer. Bazı türlerdeki zamanlama, özellikle değerler dar bir aralık dahilinde olduğunda bunu doğruluyor gibi görünüyor. 10.000.000 öğede seçim sıralamasından çok daha hızlı, ancak birleştirme veya yığınlamadan daha yavaş.

Birleştirme sıralaması, sıralaması veriye bağlı olmadığından O (n log n) garantilidir. Verdiğiniz değerler ne olursa olsun, sadece yaptığı şeyi yapar. Aynı zamanda kararlıdır, ancak uygulama konusunda dikkatli değilseniz çok büyük türler yığınızı patlatabilir. Bazı karmaşık yerinde birleştirme sıralama uygulamaları vardır, ancak genellikle değerlerinizi ile birleştirmek için her seviyede başka bir diziye ihtiyacınız vardır. Bu diziler yığın üzerinde yaşıyorsa sorunlarla karşılaşabilirsiniz.

Yığın sıralaması maksimum O'dur (n log n), ancak çoğu durumda değerlerinizi log n derin yığınında ne kadar ileriye taşımak zorunda olduğunuza bağlı olarak daha hızlıdır. Yığın, orijinal dizide kolayca yerinde uygulanabilir, bu nedenle ek belleğe ihtiyaç duymaz ve yinelemelidir, bu nedenle yineleme sırasında yığın taşması konusunda endişelenmeyin. Yığın sıralamanın en büyük dezavantajı, kararlı bir tür olmamasıdır, bu da ihtiyacınız olursa hemen çıktığı anlamına gelir.


Hızlı Sıralama, kararlı bir sıralama değildir. Bunun ötesinde, bu nitelikteki sorular fikir temelli yanıtları teşvik eder ve savaşların ve argümanların düzenlenmesine yol açabilir. Fikir temelli yanıtlar gerektiren sorular, SO yönergelerinde açıkça önerilmemektedir. Cevaplayıcılar, önemli deneyime ve bilgeliğe sahip olsalar bile, onlara cevap verme cazibesinden kaçınmalıdır. Kapatma için işaretleyin veya yeterli itibara sahip birinin onları işaretleyip kapatmasını bekleyin. Bu yorum bilginiz veya cevabınızın geçerliliği üzerine bir yansıma değildir.
MikeC
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.