기수 정렬이 더 자주 사용되지 않는 이유는 무엇입니까?


31

안정적이며 시간 복잡도는 O (n)입니다. Quicksort 및 Mergesort와 같은 알고리즘보다 빠르지 만 사용 된 적이 거의 없습니다.


2
여기를 참조하십시오 : en.wikipedia.org/wiki/Radix_sort#Efficiency 효율은 O (kn)이며 O (n * log (n))보다 나을 수 있습니다.
FrustratedWithFormsDesigner 2014 년

2
기수 정렬은 게임과 같은 부드러운 실시간 시스템에서 자주 사용됩니다. 한 알고리즘이 다른 알고리즘보다 성능이 우수한지 여부는 평소와 같이 복잡성뿐만 아니라 문제의 모든 매개 변수에 따라 달라집니다.
awdz9nld

@FrustratedWithFormsDesigner 아마도 위키가 바뀌었을까요? 더 이상 `n log (n)에 대한 참조가 보이지 않습니다. FWIW ...
rogerdpack

Boost는 (제자리 변형) boost : org / doc / libs / 1_62_0 / libs / sort / doc / html / sort / sort_hpp.html을 가지고 있지만, 사람들은 그것이 존재한다는 것을 모른다고 생각합니다 ... 그 또는 그들은 모두 "표준"정렬 알고리즘을 사용합니다. 어떤 이유로 든 프레임 워크 작성자는 여전히 효율적이지 않은 "일반"정렬을 재사용하는 경향이 있습니다 ... 아마도 int 정렬에 초점을 맞추지 않았습니다. 일반적으로 사용 사례가 드물기 때문에?
rogerdpack

답변:


38

기수 정렬과 달리 퀵 정렬은 보편적 인 반면 기수 정렬은 고정 길이 정수 키에만 유용합니다.

또한 O (f (n))은 K * f (n)의 순서를 의미하며, 여기서 K는 임의의 상수입니다. 기수 정렬의 경우이 K는 상당히 커집니다 (적어도 정렬 된 정수의 비트 수 순서). 한편 quicksort는 모든 정렬 알고리즘 중 가장 낮은 K 중 하나이며 n * log (n)의 평균 복잡도입니다. 따라서 실제 시나리오에서 퀵 정렬은 기수 정렬보다 훨씬 빠릅니다.


명시된 복잡성에 대한 참고 사항 : (LSD) 기수 정렬의 복잡도는 O (n * K)이지만이 상수는 일반적으로 작으며 일반적으로 (2 ^ (W / K)) * C가 L1에 맞도록 선택됩니다. 여기서 C 카운터의 크기 (바이트), W는 정렬되는 키의 크기입니다. 대부분의 구현은 x86에서 32 비트 단어에 대해 K = [3,4]를 선택합니다. K는 또한 각 기수가 개별적으로 정렬되므로 시간적 일관성 (근거리 정렬)을 활용하도록 적응 적으로 만들 수 있습니다.
awdz9nld

11
보편성에 대한 참고 사항 : 기수 정렬은 가변 길이 정수 키뿐만 아니라 부동 소수점 키에서도 완벽하게 작동 할 수 있습니다
awdz9nld

20

대부분의 정렬 알고리즘은 범용입니다. 비교 함수가 주어지면 무엇이든 작동하며 Quicksort 및 Heapsort와 같은 알고리즘은 O (1) 추가 메모리로 정렬합니다.

기수 정렬이 더 전문적입니다. 사전 식 순서의 특정 키가 필요합니다. 키의 가능한 각 기호마다 하나의 버킷이 필요하며 버킷에는 많은 레코드가 있어야합니다. (또는 가능한 모든 키 값을 담을 수있는 하나의 큰 버킷 배열이 필요합니다.) 기수 정렬을 수행하려면 더 많은 메모리가 필요할 수 있으며 임의로 사용할 수 있습니다. Quicksort와 같은 페이지 오류가 발생하여 캐시 누락이 발생하기 때문에 최신 컴퓨터에는 적합하지 않습니다.

마지막으로 사람들은 일반적으로 더 이상 자체 정렬 알고리즘을 작성하지 않습니다. 대부분의 언어에는 정렬 할 라이브러리 기능이 있으며 일반적으로 사용하는 것이 올바른 것입니다. 기수 정렬은 보편적으로 적용 할 수 없으며 일반적으로 실제 용도에 맞게 조정해야하며 추가 메모리를 많이 사용하므로 라이브러리 함수 나 템플릿에 넣기가 어렵습니다.


실제로 퀵 정렬에는 왼쪽 및 오른쪽 파티션의 재귀 호출 O(n^2)로 인해 최악의 경우 메모리가 필요 n합니다. 구현에서 테일 재귀 최적화를 사용 O(n)하는 경우 올바른 파티션에 대한 호출에 추가 공간이 필요하지 않기 때문에 낮출 수 있습니다 . ( en.wikipedia.org/wiki/Quicksort#Space_complexity )
카오스의 스플린터

S(n) \in O(n)기수로 정렬하기위한 공간, 즉 힙 또는 빠른 정렬과 같은 공간 만 있으면됩니다 .
Velda

@SplinterofChaos 위키가 변경 되었을까요? n^2더 이상 quicksort 에 대해서는 언급하지 않지만 O(log n)...
rogerdpack

나는 그것이 더 많은 메모리, 아마도 2 * n이라고 생각하지 않습니다 (더 많지만 불가능하지는 않을 것입니다)? 그리고 버킷이 너무 작아서 (바이트로 나누고 재귀한다고 가정) 캐시에 잘 맞을 수 있습니까?
rogerdpack

5

정렬하는 키가 실제로 알려진 희소 범위의 정수인 경우는 거의 없습니다. 일반적으로 알파벳 필드 는 비교하지 않는 정렬을 지원 하는 것처럼 보이지만 실제 문자열은 알파벳 전체에 고르게 분포되어 있지 않으므로 이론적으로는 잘 작동하지 않습니다.

다른 경우에, 기준은 운영상으로 정의 됩니다 (두 개의 레코드가 주어지면 어느 것이 먼저 오는지를 결정할 수 있지만 분리 된 레코드의 규모가 '먼'정도인지 평가할 수는 없습니다). 따라서이 방법은 적용 할 수 없거나 생각보다 덜 적용 할 수 있거나 O (n * log (n))보다 빠르지 않습니다.


기수 정렬은 "한 번에 바이트"를 재귀 적으로 정렬하여 모든 범위의 정수 (또는 문자열)를 처리 할 수 ​​있으므로 희소 범위의 FWIW 일 필요는 없습니다.
rogerdpack

4

나는 항상 비교 기반 정렬보다 더 많이 사용하지만 다른 것보다 숫자와 더 잘 작동하는 이상한 공입니다. (문자열로 거의 작업하지 않으며 일반적으로 기수와 관련이 있습니다. 정렬은 중복을 걸러 내고 집합 교차점을 계산하는 데 다시 유용 할 수 있습니다. 사전 비교를하지 않습니다.

기본 예는 검색 또는 중간 분할의 일부로 지정된 차원에 따라 기수 정렬 포인트 또는 일치 지점, 깊이 정렬 조각을 감지하는 빠른 방법 또는 여러 루프에서 사용되는 인덱스 배열을 기수 정렬하여 캐시 친화적 인 액세스를 제공하는 것입니다 패턴 (메모리에서 앞뒤로 돌아 가지 않고 다시 돌아가서 동일한 메모리를 캐시 라인으로 다시로드하는 것). 적어도 내 도메인 (컴퓨터 그래픽)에는 고정 크기 32 비트 및 64 비트 숫자 키로 정렬하기위한 매우 넓은 응용 프로그램이 있습니다.

필자가 말하고 싶었던 것은 기수 정렬이 부동 소수점 숫자와 음수에서 작동 할 수 있지만 가능한 휴대용 FP 버전을 작성하는 것은 어렵다는 것입니다. 또한 O (n * K)이지만 K는 키 크기의 바이트 수 여야합니다 (예 : 버킷에 2 ^ 8 개의 항목이있는 경우 백만개의 32 비트 정수는 일반적으로 4 바이트 크기의 패스를 사용함) ). 메모리 액세스 패턴은 일반적으로 병렬 배열과 작은 버킷 배열이 필요하지만 (두 번째는 일반적으로 스택에 잘 맞을 수 있지만) 퀵 정렬보다 캐시 친화적 인 경향이 있습니다. QS는 5 천만 스왑을 수행하여 산발적 인 랜덤 액세스 패턴으로 백만 개의 정수 배열을 정렬 할 수 있습니다. 기수 정렬은 데이터를 캐시에 친화적 인 4 개의 선형 패스로 수행 할 수 있습니다.

그러나 작은 K로 부동 소수점과 함께 음수로 이것을 할 수 있다는 인식이 부족하면 기수 종류의 인기 부족에 크게 기여할 수 있습니다.

사람들이 왜 더 자주 사용하지 않는지에 대한 나의 의견은 일반적으로 숫자를 정렬하거나 검색 키로 사용할 필요가없는 많은 도메인과 관련이있을 수 있습니다. 그러나 내 개인적인 경험을 바탕으로 이전의 많은 동료들도 완벽하게 적합한 경우에 그것을 사용하지 않았으며 부분적으로 FP 및 부정적인 작업을 수행 할 수 있다는 것을 알지 못했기 때문에 사용하지 않았습니다. 따라서 숫자 유형에서만 작동하는 것 외에도 실제보다 일반적으로 적용되는 것으로 생각됩니다 . 부동 소수점 숫자와 음의 정수에서 작동하지 않는다고 생각하면 거의 사용하지 않을 것입니다.

일부 벤치 마크 :

Sorting 10000000 elements 3 times...

mt_sort_int: {0.135 secs}
-- small result: [ 12 17 17 22 52 55 67 73 75 87 ]

mt_radix_sort: {0.228 secs}
-- small result: [ 12 17 17 22 52 55 67 73 75 87 ]

std::sort: {1.697 secs}
-- small result: [ 12 17 17 22 52 55 67 73 75 87 ]

qsort: {2.610 secs}
-- small result: [ 12 17 17 22 52 55 67 73 75 87 ]

그리고 그것은 내 순진한 구현입니다 ( mt_sort_int기수 정렬이지만 키가 정수라고 가정 할 수있는 코드 분기가 더 빠름). 전문가가 작성한 표준 구현이 얼마나 빠를 지 상상해보십시오.

C ++의 실제 빠른 비교 기반보다 기수 정렬이 더 나쁘다는 것을 발견 한 유일한 경우 std::sort는 32 개의 작은 요소에 대한 것이 었습니다.이 시점에서 나는 std::sort힙 정렬 또는 삽입 정렬, 그 시점에서 내 구현은을 사용합니다 std::sort.


1
이 지역에서 경험이있는 사람들의 의견을 항상 환영합니다.
Frank Hileman

mt_는 다중 스레드 구현으로 나타납니다. softwareengineering.stackexchange.com/a/362097/65606
rogerdpack

1

또 다른 이유 : 요즘 정렬은 일반적으로 컴파일러 제공 정렬 논리에 연결된 사용자 제공 정렬 루틴으로 구현됩니다. 기수 정렬을 사용하면 정렬 루틴이 가변 길이의 여러 키에 작용할 때이 작업이 훨씬 더 복잡해지고 더 악화됩니다. (이름, 생년월일.)

현실에서는 실제로 한 번 기수 정렬을 구현했습니다.. 이것은 메모리가 제한되어 있던 옛날에 모든 데이터를 한 번에 메모리에 가져올 수 없었습니다. 이는 데이터에 대한 액세스 수가 O (n) 대 O (n log n)보다 훨씬 중요하다는 것을 의미했습니다. 각 레코드를 빈에 할당하는 데이터를 한 번 통과했습니다 (실제로 아무것도 이동하지 않는 레코드가 어떤 빈에 있는지 목록으로). 비어 있지 않은 빈마다 (내 정렬 키는 텍스트 였으므로 empty bins) 실제로 데이터를 메모리로 가져올 수 있는지 확인했습니다. 그렇다면 가져 와서 퀵 정렬을 사용하십시오. 그렇지 않은 경우, 저장소의 항목 만 포함하는 임시 파일을 빌드하고 루틴을 재귀 적으로 호출하십시오. (실제로 몇 개의 빈이 오버플로 될 것입니다.) 이로 인해 네트워크 저장소에 대한 2 개의 완전한 읽기와 1 개의 완전한 쓰기가 발생했으며이 중 10 %는 로컬 저장소에 대한 것입니다.

요즘 이러한 빅 데이터 문제는 다루기가 훨씬 어렵습니다. 아마 그런 것을 다시는 쓰지 않을 것입니다. (요즘 같은 데이터에 직면했다면 간단히 64 비트 OS를 지정하고 해당 편집기에서 스 래싱이 발생하면 RAM을 추가하십시오.)


기수 정렬에 대해 언급 된 단점 중 하나를 고려하면 매혹적입니다. "더 많은 공간이 필요합니다." 아직도
이걸로

1
@ rogerdpack 내 접근 방식이 적은 공간을 사용하지는 않았으며 데이터에 대한 액세스가 적었습니다. 코드 및 구조 제한 64kb를 포함한 총 메모리 사용 16MB 미만의 비트의 컴파일러 제한 (Windows가 아닌 DOS 보호 모드)을 처리하는 동안 기가 바이트 정도의 파일을 정렬했습니다.
Loren Pechtel

-1

모든 매개 변수가 모두 정수이고 1024 개 이상의 입력 매개 변수가 있으면 기수 정렬이 항상 더 빠릅니다.

왜?

Complexity of radix sort = max number of digits x number of input parameters.

Complexity of quick sort = log(number of input parameters) x   number of input parameters

기수 정렬이 더 빠를 때

log(n)> max num of digits

Java의 최대 정수는 2147483647입니다. 길이는 10 자리입니다.

따라서 기수 정렬은 항상 더 빠릅니다.

log(n)> 10

따라서 기수 정렬은 항상 더 빠릅니다 n>1024


구현 세부 사항에는 숨겨진 상수가 있지만 기본적으로 "큰 기수 정렬이 빠를수록 빠릅니다"라고 말하고 있습니다 ... 그렇습니다! 사용 사례를 찾기는 어렵지만 할 수있는 경우는 다음과
같습니다
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.