Ma dla mnie sens, że przyspiesza to rozwiązanie problemu, jeśli dwie połowy zajmują mniej niż połowę pracy nad całym zestawem danych.
To nie jest istota algorytmów „dziel i rządź”. Zazwyczaj chodzi o to, że algorytmy nie mogą w ogóle „poradzić sobie z całym zestawem danych”. Zamiast tego jest on podzielony na części, które są trywialne do rozwiązania (jak sortowanie dwóch liczb), następnie są one rozwiązywane trywialnie, a wyniki ponownie łączone w sposób, który daje rozwiązanie dla pełnego zestawu danych.
Ale dlaczego nie podzielić zestawu danych na trzy części? Cztery? n?
Głównie dlatego, że podzielenie go na więcej niż dwie części i ponowne połączenie więcej niż dwóch wyników skutkuje bardziej złożoną implementacją, ale nie zmienia podstawowej (Big O) charakterystyki algorytmu - różnica jest stałym czynnikiem i może spowodować spowolnienie jeśli podział i rekombinacja więcej niż 2 podzbiorów powoduje dodatkowe obciążenie.
Na przykład, jeśli wykonujesz sortowanie metodą łączenia w 3 kierunkach, w fazie rekombinacji musisz teraz znaleźć największy z 3 elementów dla każdego elementu, co wymaga 2 porównań zamiast 1, więc wykonasz dwa razy więcej porównań ogółem . W zamian zmniejszasz głębokość rekurencji o współczynnik ln (2) / ln (3) == 0,63, więc masz o 37% mniej swapów, ale 2 * 0,63 == 26% więcej porównań (i dostępu do pamięci). To, czy to dobrze, czy źle, zależy od tego, który jest droższy w twoim sprzęcie.
Widziałem także wiele odniesień do 3-kierunkowego szybkiego sortowania. Kiedy to jest szybsze?
Najwyraźniej można udowodnić, że wariant podwójnego obrotu Quicksort wymaga takiej samej liczby porównań, ale średnio o 20% mniej swapów, więc jest to zysk netto.
Co stosuje się w praktyce?
Obecnie prawie nikt już nie programuje własnych algorytmów sortowania; używają jednego dostarczonego przez bibliotekę. Na przykład interfejs API języka Java 7 faktycznie używa podwójnego przestawnego szybkiego sortowania.
Ludzie, którzy faktycznie programują własny algorytm sortowania z jakiegoś powodu, będą skłonni trzymać się prostego wariantu dwukierunkowego, ponieważ mniejszy potencjał błędów przewyższa o 20% lepszą wydajność przez większość czasu. Pamiętaj: zdecydowanie najważniejszą poprawą wydajności jest zmiana kodu z „niedziałający” na „działający”.