[Algorithm] Shell Sort

2023. 10. 13. 20:44

정렬이란, 임의의 순서가 있는 데이터가 무작위로 주어졌을 때 이를 순서대로 나열하는 것을 의미한다. 정렬 방식은 여러가지가 있으며 오늘은 그 중 Shell Sort에 대해 알아보자.

Insertion Sort

기본 개념

Shell sort를 알기 위해서는 Insertion Sort부터 알아야한다. Insertion Sort 는 이미 정렬된 상태에 새로운 데이터의 위치를 찾아 넣는 방식이다. 

위 이미지에서 파란 부분이 이미 정렬된 부분, 붉은 부분이 새로 넣고자 하는 원소이다. 붉은 원소를 파란 부분의 어느 위치에 넣을 것인지 찾아 '삽입'하는 형태로 정렬을 진행하기 때문에 '삽입 정렬'이라고 부른다. 

 

코드

# input : 정렬되지 않은 list
# output : 정렬된 list
def insertionsort(a):
    for i in range(1, len(a)):
    	key = a[i]
        j = i-1
        while j >=0 and a[j] > key:
        	a[j+1] = a[j] # key가 더 작으면 옆으로 값 밀기
            j-=1
        a[j+1]=key
	return a

 

시간복잡도

Insertion sort는 이미 정렬된 상태이거나 거의 정렬된 상태인 경우 매우 좋은 성능을 낸다.

입력 데이터 대소 비교 Swap
이미 정렬된 상태 (best case) N-1 0
반대 방향 정렬 (worst case) ~ N^2 /2 ~ N^2 /2
절반정도 정렬 (avg) ~ N^2 / 4 ~ N^2 / 4

 

특히, 이미 정렬된 상태에 가까울 때 swap 횟수가 많이 줄어든다. insertion sort는 Inversion 수에 따라 swap을 진행한다.

  • Inversion
    정렬 순서가 어긋난 쌍을 의미한다.
    A E E L M O T R X P S 의 경우 T-R, T-P, T-S, R-P, X-P, X-S 가 정렬 순서가 어긋난 쌍, 즉 Inversion이다.
    따라서 위 경우는 Insertion Sort를 실행하면 6번 Swap을 수행한다. ( 이 말이 진짜 바꾸는 것을 6번 하는게 아니라 원소를 비교하면서 위치까지 도달하여 swap을 하는 것을 6번 한다는 것이다. )

 

h-sort

기본 개념

모든 h 만큼 떨어진 원소끼리 정렬된 상태로 만드는 것이다. h의 값이 커질수록 실행해야하는 연산량이 줄어들지만 h가 작을 수록 더 완전한 정렬 결과를 보인다.

1-sort는 insertion sort와 동일한 결과를 도출한다.

코드

def hInsertionSort(a, h):
	for i in range(h, len(a)):
    	key = a[i]
        j = i - h # h 간격으로 진행
        while j >=0 and a[j] > key:
        	a[j+h] = a[j]
            j-=h
        a[j+h] = key
    return a

 

Shell Sort

기본 개

shell sort는 h-sort를 h 값을 점점 감소시키면서 최종적으로 1-sort를 진행하여 정렬된 상태로 만드는 것이다. 

이 방식으로 하면 여러 칸 앞으로 가야하는 원소들을 한 번에 이동시키기 때문에 1-sort 하기 전 inversion을 크게 줄여주기 때문에 마지막 정렬이 ~N 에 비슷해진다. 또한, 각각의 h-sort를 진행할 때 생각보다 비교 횟수가 많지 않다는 것을 직접 해보면 알게 될 것이다.

 

h-sort의 h 값은 어떻게 정할까?

가장 대중적인 방법은 Donald Knuth가 제안한 3x+1 방식이다.

1 <-4 <-13 <-40 <-121 ...

계산하기 쉽고 준수한 성능을 낸다고 한다.

+) 이 외에도 2^k +1, 프린스턴 대학 Sedgewick 교수가 제안한 수열 등 다양한 방식이 있다.

하나 중요한 점은 어떤 수의 배수로는 잘 진행하지 않는다. 특히 2^k 는 짝수만 계속 진행하기 때문에 매우 좋지 않다.

 

코드

Knuth가 제안한 3x + 1 방식을 이용해 h를 정했다.

def shellSort(a):
	N = len(a)
    h = 1
    while h < N/3: # N을 넘지 않는 h를 구한다.
    	h = 3 * h +1
    
    while h >= 1:
    	hInsertionSort(a, h) # h-sort 진행
        h = h //= 3

 

시간복잡도

Shell Sort의 성능을 표현할 수 있는 수학적 모델을 아직 찾지 못했다. h = 3x+1를 사용하는 경우 ~logN회의 h-sort를 수행하기 때문에 ~NlogN으로 추측하고 있다. 

 

 

'Computer Science > Data structure & Algorithm' 카테고리의 다른 글

[Algorithm] Merge Sort  (0) 2023.10.13
[Algorithm] Shuffle Sort  (1) 2023.10.13
[Algorithm] Union Find  (1) 2023.09.16
[Algorithm] Greedy - Scheduling  (0) 2023.06.13
[Algorithm] Minimum Spanning Tree - Prim  (0) 2023.06.13

BELATED ARTICLES

more