퀵 정렬 분류 : Hoare vs. Lomuto


82

Cormen에는 두 가지 빠른 정렬 파티션 방법이 있습니다.

Hoare-Partition(A, p, r)
x = A[p]
i = p - 1
j = r + 1
while true
    repeat
        j = j - 1
    until A[j] <= x
    repeat
        i = i + 1
    until A[i] >= x
    if i < j
        swap( A[i], A[j] )
    else
        return j

과:

Lomuto-Partition(A, p, r)
x = A[r]
i = p - 1
for j = p to r - 1
    if A[j] <= x
        i = i + 1
        swap( A[i], A[j] )
swap( A[i +1], A[r] )
return i + 1

피벗을 선택하는 방법을 무시하면서 어떤 상황에서 다른 것이 선호됩니까? 예를 들어 Loareto는 중복 값의 비율이 높을 때 (즉, 배열의 2/3 이상이 같은 값인 경우) 상대적으로 열악한 것으로 미리 알고 있습니다.

어떤 특별한 방법으로 한 파티션 방법이 다른 파티션 방법보다 더 중요합니까?


2
내가 생각할 수 있는 Lomuto이 호어보다 낫다있는 상황. Lomuto는 언제나 여분의 스왑을 수행하는 것 같습니다 A[i+1] <= x. 정렬 된 배열에서 (합리적으로 선택된 피벗이 주어지면) Hoare는 스왑을 거의하지 않으며 Lomuto는 톤을 수행합니다 (j는 충분히 작아지면 모든 A[j] <= x.) 무엇을 놓치고 있습니까?
방황 논리

2
@WanderingLogic 확실하지 않지만, 그의 책에서 Lomuto 파티션을 사용하려는 Cormen의 결정은 교육적인 것일 수 있습니다.
Robert S. Barnes

2
이 두 알고리즘은 같은 일을하지 않습니다. Hoare 알고리즘의 끝에서 피벗은 최종 위치가 아닙니다. swap(A[p], A[j])Hoare 's 끝에 a 를 추가하여 두 가지에 대해 동일한 동작을 얻을 수 있습니다.
Aurélien Ooms

또한 Hoare 분할의 반복 루프 i < j2 개를 확인해야 합니다.
Aurélien Ooms

@ AurélienOoms 코드는 책에서 직접 복사됩니다.
Robert S. Barnes

답변:


92

교육 학적 차원

단순성 때문에 Lomuto의 파티셔닝 방법은 구현하기가 더 쉬울 수 있습니다. 정렬에 관한 Jon Bentley의 Programming Pearl 에는 멋진 일화가 있습니다 .

“Quicksort에 대한 대부분의 논의는 두 개의 다가오는 지수 [...] [즉 Hoare 's]에 기반한 파티셔닝 체계를 사용합니다. 그 체계의 기본 아이디어는 간단하지만 항상 세부 사항을 까다롭게 발견했습니다. 나는 짧은 파티션 루프에 숨어있는 버그를 추적하는 데 이틀 동안 더 나은 부분을 보냈습니다. 예비 초안을 읽은 독자는 표준 2- 인덱스 방법이 실제로 Lomuto의 방법보다 간단하다고 주장하고 그의 요점을 밝히기 위해 몇 가지 코드를 스케치했습니다. 두 가지 버그를 발견 한 후 조사를 중단했습니다.”

성과 차원

실제 사용을 위해, 효율성을 위해 구현의 용이성이 희생 될 수있다. 이론적으로 우리는 성능을 비교하기 위해 요소 비교 및 ​​스왑 수를 결정할 수 있습니다. 또한 실제 실행 시간은 캐싱 성능 및 지점 오해와 같은 다른 요소의 영향을받습니다.

아래에 표시된 것처럼 알고리즘 은 스왑 수를 제외하고 임의 순열에서 매우 유사하게 작동합니다 . Lomuto는 Hoare의 3 배가 필요합니다 !

비교 횟수

n1n

스왑 수

스왑 수는 배열의 요소에 따라 두 알고리즘 모두에서 임의입니다. 임의의 순열 을 가정하면 , 즉 모든 요소가 고유하고 요소의 모든 순열이 동일하게 발생할 경우 예상 스왑 수를 분석 할 수 있습니다 .

1,,n

로무 토의 방법

jA[j]x1,,nx1xx1x

{1,,n}1n

1nx=1n(x1)=n212.

n

호 아레의 방법

x

ijxij

x

Hyp(n1,nx,x1)nxx1(nx)(x1)/(n1)x

마지막으로 Hoare 분할에 대한 전체 예상 스왑 수를 얻기 위해 모든 피벗 값에 대해 평균을 다시 계산합니다.

1nx=1n(nx)(x1)n1=n613.

(자세한 설명은 29 페이지 내 석사 논문 에서 확인할 수 있습니다 .)

메모리 액세스 패턴

두 알고리즘 모두 배열에 순차적으로 스캔하는 두 개의 포인터를 사용합니다 . 따라서 둘 다 거의 최적의 wrt 캐싱을 수행합니다.

동일한 요소와 이미 정렬 된 목록

Wandering Logic에서 이미 언급했듯이 임의 순열이 아닌 목록의 경우 알고리즘의 성능이 크게 다릅니다.

n/2

0ijO(nlogn)

0A[j] <= xi=nΘ(n2)

결론

Lomuto의 방법은 간단하고 구현하기 쉽지만 라이브러리 정렬 방법을 구현하는 데 사용해서는 안됩니다.


16
와우, 자세한 답변입니다. 잘 했어요!
Raphael

라파엘, 정말 좋은 답변에 동의해야합니다!
Robert S. Barnes

1
고유 한 요소 대 전체 요소의 비율이 낮아질수록 Lomuto의 비교 횟수가 Hoare보다 훨씬 빠르게 증가한다는 것을 간략하게 설명하겠습니다. Lomuto 부분의 분할이 불량하고 Hoare 부분의 분할이 양호하기 때문일 수 있습니다.
Robert S. Barnes

두 가지 방법에 대한 훌륭한 설명! 감사합니다!
v kouk

피봇과 동일한 모든 요소를 ​​추출하고 재귀에서 벗어날 수있는 Lomuto 방법의 변형을 쉽게 만들 수 있지만 평균 사례를 도울 지 또는 방해하는지 확실하지 않습니다.
Jakub Narębski는

5

일부 의견은 훌륭한 Sebastian 답변에 추가되었습니다.

나는 파티션 재 배열 알고리즘에 대해 이야기하고 Quicksort 의 특별한 사용에 대해서는 이야기하지 않겠다 .

안정

Lomuto의 알고리즘은 안정적이다 : 술어를 만족시키지 않는 요소들의 상대적 순서 는 유지된다. Hoare의 알고리즘이 불안정합니다.

요소 접근 패턴

Lomuto의 알고리즘은 단일 연결 목록 또는 이와 유사한 순방향 전용 데이터 구조와 함께 사용할 수 있습니다. Hoare의 알고리즘에는 양방향성이 필요합니다 .

비교 횟수

n1n

그러나 이것을하기 위해서는 2 개의 속성을 희생해야합니다 :

  1. 분할 될 시퀀스는 비워 둘 수 없습니다.
  2. 알고리즘이 파티션 포인트를 리턴 할 수 없습니다.

n

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.