Hem hızlı sıralama hem de yığın sıralaması yerinde ayırma yapar. Hangisi daha iyi? Herhangi birinin tercih edildiği uygulamalar ve durumlar nelerdir?
Yanıtlar:
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.
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);
}
Ç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.
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 :
Comp. arasında quick sort
ve merge sort
her 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.
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.
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 !! 😀
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.
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.
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.
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.
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.