실제로 다른 정렬 알고리즘보다 퀵 정렬이 더 나은 이유는 무엇입니까?


31

이것은의 재 게시입니다 cs.SE에 질문 하여 Janoma . 그 또는 cs.SE에 대한 완전한 크레딧 및 전리품.

표준 알고리즘 과정에서 우리는 quicksort 가 평균 O (n log n)이고 최악의 경우 O (n²) 임을 배웁니다 . 동시에 최악의 경우 ( sergesort 및 heapsort 와 같은) O (n log n) , 심지어 가장 좋은 경우 ( bubblesort 와 같은 ) 선형 시간 이지만 추가 메모리가 필요한 다른 정렬 알고리즘이 연구 됩니다.

에 잠깐 한눈 후 좀 더 실행 시간 이 퀵는 말을 당연 하지 말아야 다른 사람 효율적으로합니다.

또한 학생들은 기본 프로그래밍 과정에서 재귀가 너무 많은 메모리를 사용할 수 있기 때문에 일반적으로 좋지 않다는 것을 배우는 것을 고려하십시오. 따라서 (그리고 이것이 실제 논쟁이 아니더라도), 이것은 퀵 정렬이 그렇지 않을 수도 있다는 생각을줍니다. 재귀 알고리즘이기 때문에 정말 좋습니다.

그렇다면 퀵 정렬이 실제로 다른 정렬 알고리즘보다 우수한 이유는 무엇입니까? 실제 데이터 의 구조와 관련이 있습니까? 컴퓨터에서 메모리가 작동하는 방식과 관련이 있습니까? 나는 어떤 기억들이 다른 기억들보다 더 빠르다는 것을 알고 있지만, 이것이 이론상 추정치와 비교할 때이 반 직관적 인 성능의 실제 이유인지는 모른다.


3
Quicksort 평판은 캐시가 존재하지 않은 시간부터 시작됩니다.
— AProgrammer

9
"퀵 정렬이 실제로 다른 정렬 알고리즘보다 우수한 이유는 무엇입니까?" 그게 사실입니까? 이 진술을 통해 당신이 참조하고있는 실제 구현을 보여 주면, 커뮤니티는 왜 그 특정 구현이 그 방식대로 행동하는지 알려줄 것입니다. 다른 모든 것은 존재하지 않는 프로그램에 대한 거친 추측으로 이어질 것입니다.
— Doc Brown

1
@DocBrown : 많은 Quicksort (또는 그 변형) 구현은 많은 라이브러리에서 선택됩니다. 아마도 최고의 성능을 발휘하기 때문입니다 (바람직합니다). 따라서 구현 과 관계없이 Quicksort를 빠르게 만드는 알고리즘 에 대한 내용이있을 수 있습니다 .
— Raphael

1
누군가는 이것을 완전성으로 말해야하므로 Quicksort는 (보통) 안정적이지 않습니다. 이러한 이유로 사용하지 않을 수 있습니다. 또한 이러한 이유로 기본 정렬이 원하는 경우에도 빠른 정렬이 아닐 수 있습니다.
— RalphChapin

1
@Raphael : 종종 빠른 정렬이라고하는 것은 실제로는 빠른 빠른 정렬이 아닌 인트로 정렬 (C ++ 표준 라이브러리에서 사용, afaik)과 같은 변형입니다.
— 조르지오

답변:


21

나는 퀵 정렬이 실제로 다른 정렬 알고리즘보다 낫다는 것에 동의하지 않을 것입니다.

대부분의 경우, 팀 정렬 -병합 정렬 / 삽입 정렬 간의 하이브리드는 정렬하는 데이터가 거의 정렬되거나 역 정렬되는 경우가 많다는 사실을 이용합니다.

가장 간단한 퀵 정렬 (임의의 피벗 없음)은이 잠재적 인 경우를 O (N ^ 2) (임의의 피벗으로 O (N lg N)로 감소)로 취급하지만 TimSort는 O (N)에서 이러한 경우를 처리 할 수 ​​있습니다.

내장 퀵 정렬 을 TimSort와 비교하는 C #의 이러한 벤치 마크 에 따르면 , Timsort는 대부분 정렬 된 경우에서 상당히 빠르며 임의의 데이터 경우에서는 약간 빠르며 비교 기능이 특히 느리면 TimSort가 더 좋습니다. 나는 이러한 벤치 마크를 반복하지 않았으며 임의의 데이터 조합에 대해 quicksort가 TimSort를 약간 이겼거나 C #의 내장 정렬 (quicksort 기반)에 기발한 것이있는 경우 놀라지 않을 것입니다. 그러나 TimSort는 데이터가 부분적으로 정렬 될 때 뚜렷한 이점이 있으며 데이터가 부분적으로 정렬되지 않은 경우 속도면에서 퀵 정렬과 거의 같습니다.

TimSort는 또한 퀵 정렬과 달리 안정적인 정렬이라는 추가 보너스를 제공합니다. TimSort의 유일한 단점은 일반적인 (빠른) 구현에서 O (N) 대 O (lg N) 메모리를 사용합니다.


18

계수가 다른 알려진 알고리즘보다 작기 때문에 빠른 정렬이 더 빠른 것으로 간주됩니다. 그 이유나 증거는 없으며, 계수가 더 작은 알고리즘은 발견되지 않았습니다. 다른 알고리즘에도 O ( n log n ) 시간이 있지만 실제로는 계수도 중요합니다.

작은 데이터 삽입 정렬 (O ( n 2 )으로 간주되는 )의 경우 수학 함수의 특성으로 인해 더 빠릅니다. 이는 기계마다 다른 특정 계수에 따라 다릅니다. (결국에는 어셈블리 만 실제로 실행됩니다.) 때로는 빠른 정렬 및 삽입 정렬의 하이브리드가 실제로 가장 빠릅니다.


7
+ 맞습니다. 교사는 일정한 요인이 순서에 따라 다를 수 있다는 사실을 더 잘 알고 있어야합니다 (그리고 저는 교사였습니다). 따라서 big-O에 관계없이 성능 조정 기술이 중요합니다. 문제는 교과 과정에서 그 총알 점을 지나야 만하 기 때문에 gprof 를 계속 가르치는 것입니다.
— Mike Dunlavey

2
“그 이유는 없다”고 확신합니다. 충분히 깊이 파면 이유를 찾을 수 있습니다.
— Gilles 'SO- 악마 중지'

2
@B Seven : 많이 단순화하기 위해… O (n log n) 정렬 알고리즘의 경우 n 개의 항목을 정렬하기 위해 정렬 루프의 (n log n) 반복이 있습니다. 계수는 루프의 각주기에 걸리는 시간입니다. n이 실제로 큰 경우 (최소한 수천), 계수가 크더라도 계수는 O ()만큼 중요하지 않습니다. 그러나 n이 작 으면 계수가 중요하며 10 개 항목 만 정렬하는 경우 가장 중요 할 수 있습니다.
— Matt Gallagher

4
@ MikeDunlavey-좋은 예는 피라미드를 만드는 것이 O (n)이고 사진을 정렬하는 것은 O (n ln n)이지만 더 빠릅니다!
— Martin Beckett

2
힙 정렬 및 병합 정렬과 같은 O (n log n) 알고리즘이 보장되므로 점근 적으로 최악의 경우 Quicksort는 최고만큼 빠르지 않습니다. 그러나 실제 성능에서 일부 퀵 정렬 유형은 매우 잘 작동합니다. 그러나 "계수가 작다"는 것은 "더 빠르기 때문에 더 빠르다"고 말하는 것과 같습니다. 상수 요소가 왜 그렇게 작은가? 주요 이유는 퀵 정렬이 로컬 리티 측면에서 매우 우수하기 때문입니다. 캐시를 매우 잘 사용합니다. 메르 소조 트도 좋은 지역성을 가지고 있지만, 제자리에서하기가 매우 어렵습니다.
— Steve314

16

Quicksort는 다른 정렬 알고리즘을 모두 능가하지 않습니다. 예를 들어, 상향식 힙 정렬 ( Wegener 2002 )은 합리적인 양의 데이터 에서 빠른 정렬 보다 성능이 뛰어나고 내부 알고리즘이기도합니다. 구현하기도 쉽다 (적어도 최적화 된 퀵소트 변형보다 어렵지 않다).

그것은 잘 알려져 있지 않으며 많은 교과서에서 그것을 찾지 못합니다. 퀵 정렬만큼 인기가없는 이유를 설명 할 수 있습니다.


+1 : 몇 가지 테스트를 수행했으며 실제로 병합 정렬은 큰 배열 (> 100000 요소)의 빠른 정렬보다 낫습니다. 힙 정렬은 병합 정렬보다 약간 나빴지 만 병합 정렬에는 더 많은 메모리가 필요합니다. 사람들이 빠른 정렬이라고 부르는 것은 종종 인트로 정렬이라는 변형이라고 생각합니다. 재귀 깊이가 특정 제한을 초과하면 힙 정렬로 돌아가는 빠른 정렬입니다.
— 조르지오

@Giorgio : quicksort를 개선하기 위해 어떤 방법 으로든 수정할 수 있습니다 (예 : algs4.cs.princeton.edu/23quicksort) .
— Doc Brown

흥미 롭습니다. 더 자세한 정보를 얻기 위해 서적 / 사이트에 대한 언급을 할 수 있습니까? (바람직하게 책)
— Ramzi Kahil

@ 마틴 : 당신은 상향식 힙 정렬에 대한 의미입니까? 글쎄, 나는 위에서 언급했다. 무료 리소스를 원한다면 독일어 위키 백과에 대한 기사가 있습니다 ( de.wikipedia.org/wiki/BottomUp-Heapsort ). 독일어를 못해도 C99 예제를 계속 읽을 수 있습니다.
— Doc Brown

7

최악의 경우와 시간의 복잡성에만 집중해서는 안됩니다. 그것은 최악보다 평균에 관한 것이고 시간 과 공간 입니다.

퀵 정렬 :

  • 갖는 평균 Θ 시간 복잡도 ( N 로그 N );
  • 공간 복잡도 Θ (log n ) 로 구현 될 수 있으며 ;

또한 큰 O 표기법은 상수를 고려하지 않지만 실제로 알고리즘이 몇 배 더 빠르면 차이를 만듭니다. Θ ( n log n )은 알고리즘이 K  n  log ( n ) 에서 실행됨을 의미하며 , 여기서 K 는 일정합니다. Quicksort는 K 가 가장 낮은 비교 정렬 알고리즘입니다 .


1
@Gilles : 간단한 알고리즘이기 때문에 K가 낮습니다.
— vartec

5
WTF? 이것은 말이되지 않습니다. 알고리즘의 단순성은 실행 속도와 관련이 없습니다. 선택 정렬은 퀵 정렬보다 간단하므로 더 빠르지 않습니다.
— Gilles 'SO- 악마 그만'

1
@Gilles : 선택 정렬은 모든 경우에 대해 O (n ^ 2)입니다 (최악, 평균 및 최고). 따라서 그것이 얼마나 간단한지는 중요하지 않습니다. Quicksort는 평균 경우 O (n log n)이며 평균 O (n log n) 인 모든 알고리즘 중에서 가장 간단한 것입니다.
— vartec

1
@Gilles : 다른 것들은 같고 단순함은 성능을 돕는다. 각각의 내부 루프에서 각각 (K n log n) 반복을 수행하는 두 가지 알고리즘을 비교한다고 가정 해보십시오. 루프 당 더 적은 일을 해야하는 알고리즘은 성능 이점이 있습니다.
— 오는 폭풍

1
@comingstorm : 당신의 진술이 팽팽한 것이지만 "단순성"과는 관련이 없습니다. 예를 들어 더 복잡한 Quicksort 변형 (대소 문자 구분)이있어 런타임 (작은 이론과 실습 모두)이 줄어 듭니다.
— Raphael

5

Quicksort는 종종 빠르고 합리적으로 빠르고 구현하기 쉽기 때문에 좋은 선택입니다.

대량의 데이터를 매우 빠르게 정렬하는 것이 중요하다면 MergeSort와 약간의 차이가있을 수 있습니다. 이것은 외부 저장소를 이용하거나 여러 스레드 또는 프로세스를 사용할 수 있지만 코드에는 쉽지 않습니다.


1

알고리즘의 실제 성능은 플랫폼, 언어, 컴파일러, 구현 세부 사항에 대한 프로그래머의 관심, 특정 최적화 노력 등에 따라 다릅니다. 따라서 퀵 정렬의 "상수 요인 이점"은 잘 정의되어 있지 않습니다. 현재 사용 가능한 도구를 기반으로하는 주관적인 판단이며, 실제로 비교 성능 연구를 수행하는 사람에 의한 "동등한 구현 노력"에 대한 대략적인 평가입니다. .

즉, quicksort는 단순하고 재귀 구조가 비교적 캐시 친화적이기 때문에 무작위 입력의 경우 성능이 우수하다고 생각합니다. 반면에 최악의 경우는 트리거하기 쉽기 때문에 퀵 정렬의 실제 사용은 교과서 설명에 표시된 것보다 더 복잡해야합니다. 따라서 소개와 같은 수정 된 버전입니다.

시간이 지남에 따라 주요 플랫폼이 변경됨에 따라 다른 알고리즘이 (정의되지 않은) 상대적인 이점을 얻거나 잃을 수 있습니다. 상대 성능에 대한 기존의 지혜는 이러한 변화에 뒤 떨어질 수 있으므로 어떤 알고리즘이 애플리케이션에 가장 적합한 지 확실하지 않은 경우 두 알고리즘을 모두 구현하고 테스트해야합니다.


나는 다른 사람들이 그것과 관련된 "더 작은 상수"는 공식적인 분석에서, 즉 비교 나 스왑의 수에 관한 것이라고 생각합니다. 이것은 잘 정의되어 있지만 이것이 어떻게 런타임으로 변환되는지는 확실하지 않습니다. 동료는 현재 실제로 이에 대한 조사를하고 있습니다.
— Raphael

내 인상은 그것이 일반화 된 성능에 관한 것이지만, 나는 어느 쪽에도 의존하지 않을 것이라고 생각했다. 당신은, 비록 맞아 : 당신의 비교가 특히 비싼 경우가 예상되는 비교의 수 ... 찾아 볼 수 있습니다
— comingstorm

1
당신이 언급 한 이유 때문에, 전체 성능 (시간별)에 대해 말하는 것은 일반적인 경우에 너무 많은 세부 사항 요소를 의미하는 것은 아닙니다. 선택 작업 만 계산하는 이유는 비용이 많이 들지 않지만 "가장 자주"발생하기 때문입니다. "Landau-notation (Big-Oh) 의미로"그것을 세면 거친 증상이 없습니다. 상수 및 / 또는 런타임을 고려 하자마자이 전략은 훨씬 덜 흥미 롭습니다.
— Raphael

QuickSort를 올바르게 구현하면 피벗 값이 필요한 한 CPU 레지스터에 유지되도록 컴파일됩니다. 이것은 종종 비슷한 Big-O 시간으로 이론적으로 더 빠른 정렬을 이기기에 충분합니다.
— Dan Lyons

서로 다른 정렬 알고리즘은 비교 횟수와 교환 횟수와 관련하여 다른 특성을 갖습니다. 그리고 @DanLyons는 라이브러리의 일반적인 정렬은 사용자 제공 함수를 통해 비교를 수행하며 많은 함수 호출에서 레지스터의 값을 유지하는 것은 매우 까다 롭습니다.
— Pointy
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.