잘못된 정보가 있다면, 꼭 댓글로 알려주세요(비로그인 익명도 가능).

여러분의 피드백이 저와 방문자 모두를 올바른 정보로 인도할 수 있습니다.

감사합니다. -현록

후원해주실 분은 여기로→

현록의 기록저장소

정렬 - 합병 정렬 본문

Study/Algorithm

정렬 - 합병 정렬

현록 2019. 4. 28. 00:36

퀵 정렬 알고리즘을 공부하면서 직접 코드로 짜봤었는데,

[Study] - 정렬 - 퀵 정렬

 

배열의 크기가 꽤 크면, 라이브러리에서 제공해주는 정렬과 시간차이가 꽤 나서 좀 조사해보니,

 

라이브러리에서의 배열은 정수 배열은 합병 정렬(Merge Sort)를,

실수 배열은 퀵 정렬(Quick Sort)를 행한다고 한다.

(난 원래 섞어서 쓰는 줄 알았는데. 대충 검색한거라 정확한 정보는 아님.)

 

어쨌든 합병 정렬에 대해서도 알아봤다.

https://ko.wikipedia.org/wiki/%ED%95%A9%EB%B3%91_%EC%A0%95%EB%A0%AC

 

합병 정렬 - 위키백과, 우리 모두의 백과사전

위키백과, 우리 모두의 백과사전. 합병 정렬 또는 병합 정렬(merge sort)은 O(n log n) 비교 기반 정렬 알고리즘이다. 일반적인 방법으로 구현했을 때 이 정렬은 안정 정렬에 속하며, 분할 정복 알고리즘의 하나이다. 존 폰 노이만이 1945년에 개발했다.[1] 알고리즘[편집] 합병 정렬은 다음과 같이 작동한다. 리스트의 길이가 0 또는 1이면 이미 정렬된 것으로 본다. 그렇지 않은 경우에는 정렬되지 않은 리스트를 절반으로 잘라 비슷한 크기의

ko.wikipedia.org

합병 정렬도 퀵 정렬과 마찬가지로 분할 정렬 알고리즘이다.

 

수를 계속해서 한 개 단위가 될 때 까지 나눈 후, 나눈 애들끼리 정렬하면서 다시 조립된다고 보면 된다.

 

나눈 애가 정렬되고 합치는 과정에서 재귀를 이용한다.

public void sortAsc(double[] origin, double[] result, int start, int end) {
    if(end>start) {
        sortAsc(origin, result, start, (end+start)/2);
        sortAsc(origin, result, (end+start)/2+1, end);
        mergeAsc(origin, result, start, end);
    }
}

public void mergeAsc(double[] origin, double[] result, int start, int end) {
    int i1 = start;
    int end1 = (end+start)/2;
    int i2 = end1+1;
    int end2 = end;
    int index = start;
    while(index<=end) {
        if(i1<=end1&&i2<=end2) {
            if(origin[i1]<=origin[i2]) result[index++] = origin[i1++];
            else result[index++] = origin[i2++];
        } else if(i1<=end1) {
            result[index++] = origin[i1++];
        } else if(i2<=end2) {
            result[index++] = origin[i2++];
        }
    }
    for(int i=start;i<=end;i++) origin[i]=result[i];
}

 

 

 

int 배열과 double 배열을 각각 1억개의 크기만큼 만든 후,

Math.random()*1000을 이용해 무작위 수를 넣고 정렬해보았다.

 

<int 배열>

퀵 정렬: 13.031999826431274 초

합병 정렬: 16.816999912261963 초

Arrays.sort(): 4.474999904632568 초

 

<double 배열>

퀵 정렬: 21.91000008583069 초
합병 정렬: 27.405999898910522 초
Arrays.sort(): 13.824000120162964 초

 

단위에 상관없이 합병 정렬보다 퀵 정렬이 빨랐다.(라이브러리보다는 한참 못 미친다.)

(으아니.. 정수와 실수로 나눠서 해본 의미가...)

 

 

이번엔 처음에 내림차순으로 되어있는 배열을 만들었다.

(내림차순→오름차순. 즉, 최악의 경우)

이 경우에는 전부 오름차순으로 바꾸기까지 시간이 꽤 걸리므로,

크기를 1억개에서 10만개로 대폭 줄였다.

 

<int 배열>

퀵 정렬: 21.236000061035156 초

합병 정렬: 0.01399993896484375 초

Arrays.sort(): 0.004999876022338867 초

 

<double 배열>

퀵 정렬: 17.59500002861023 초
합병 정렬: 0.014000177383422852 초
Arrays.sort(): 0.007999897003173828 초

 

이번엔 합병 정렬이 퀵 정렬보다 훨씬 빨랐다.

이 부분은 시간복잡도에 의한, 최악의 경우를 처리하는 퀵 정렬의 상황 때문으로 보인다.

 

합병 정렬

최선의 경우에도, 최악의 경우에도 시간복잡도는 항상 O(n*log(n))이지만,

퀵 정렬

최선의 경우에는 O(n*log(n))이지만, 최악의 경우에는 O(n^2)이 된다.

(그래도 평균 시간복잡도는 O(n*log(n))이긴 하다.)

 

시간복잡도는

O(1)<O(log(n))<O(n)<O(n*log(n))<O(n^2)<O(n^3)<O(2^n)<O(n!) 순으로 알고리즘 처리시간이 급격하게 증가한다.

https://namu.wiki/w/%EC%8B%9C%EA%B0%84%20%EB%B3%B5%EC%9E%A1%EB%8F%84

 

 

 

정렬에 이렇게나 많은 종류가 존재한다는 건, 역시 왕도가 없다는 소리라고 보면 되려나..

 

라이브러리와 시간차가 나는 건 정렬하면서 수를 복사했다 바꾸는 과정 같은 부분이 누적되어서 영향을 주는걸까..

 

변수 종류에 차이가 나나 봤다가 시간복잡도에 대해 공부하고 가게 되네;;

'Study > Algorithm' 카테고리의 다른 글

Hash Function  (0) 2019.06.27
정렬 - 버블 정렬  (0) 2019.06.27
한 선이 만나는 다른 선의 수  (0) 2019.05.11
정규표현식(Regular Expression)  (0) 2019.05.05
정렬 - 퀵 정렬  (0) 2019.04.27
Comments

잘못된 정보가 있다면, 꼭 댓글로 알려주세요(비로그인 익명도 가능).

여러분의 피드백이 저와 방문자 모두를 올바른 정보로 인도할 수 있습니다.

감사합니다. -현록

후원해주실 분은 여기로→