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


31

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

표준 알고리즘 과정에서 우리는 quicksort 가 평균 O (n log n)이고 최악의 경우 O (n²) 임을 배웁니다 . 동시에 최악의 경우 ( sergesortheapsort 와 같은) 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.