2020-1ํ•™๊ธฐ, ๋Œ€ํ•™์—์„œ โ€˜์•Œ๊ณ ๋ฆฌ์ฆ˜โ€™ ์ˆ˜์—…์„ ๋“ฃ๊ณ  ๊ณต๋ถ€ํ•œ ๋ฐ”๋ฅผ ์ •๋ฆฌํ•œ ๊ธ€์ž…๋‹ˆ๋‹ค. ์ง€์ ์€ ์–ธ์ œ๋‚˜ ํ™˜์˜์ž…๋‹ˆ๋‹ค :)

6 minute read

2020-1ํ•™๊ธฐ, ๋Œ€ํ•™์—์„œ โ€˜์•Œ๊ณ ๋ฆฌ์ฆ˜โ€™ ์ˆ˜์—…์„ ๋“ฃ๊ณ  ๊ณต๋ถ€ํ•œ ๋ฐ”๋ฅผ ์ •๋ฆฌํ•œ ๊ธ€์ž…๋‹ˆ๋‹ค. ์ง€์ ์€ ์–ธ์ œ๋‚˜ ํ™˜์˜์ž…๋‹ˆ๋‹ค :)

Merge Sort

<์ •๋ ฌ>์€ ์•Œ๊ณ ๋ฆฌ์ฆ˜์˜ ๊ฐ€์žฅ ๊ทผ์›์ด ๋˜๋Š” ์ž‘์—…์ด๋‹ค. <๋ณ‘ํ•ฉ ์ •๋ ฌ; Merge Sort>์€ <ํ€ต ์ •๋ ฌ; Quick Sort>๊ณผ ํ•จ๊ป˜ ์ธ๋ฅ˜ ์—ญ์‚ฌ์—์„œ ์ตœ๊ณ ์˜ ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด๋ผ๊ณ  ์ƒ๊ฐํ•œ๋‹ค! ๐Ÿ‘

Merge Sort๋Š” <๋ถ„ํ•  ์ •๋ณต>์„ ์ •๋ง ์ถฉ์‹คํžˆ ์ˆ˜ํ–‰ํ•˜๋Š” ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด๋‹ค.

โ€œsplit the list into two halves, recursively sort each half, and them merge them.โ€

Merge Sort๋Š” ๊ธฐ๋Šฅ์ ์œผ๋กœ ๋ดค์„ ๋•Œ, โ€œMerge Sortโ€œ์™€ โ€œMergeโ€ ๋‘ ๊ฐ€์ง€ ์ž‘์—…๋งŒ ์ˆ˜ํ–‰ํ•˜๋ฉด ๋œ๋‹ค.

function: mergesort($a[1โ€ฆn]$)


if $n > 1$ then
โ€ƒโ€ƒ return merge(mergesort($a[1 โ€ฆ n/2]$), mergesort($a[n/2+1 โ€ฆ n]$))
else
โ€ƒโ€ƒ return $a$
end if

function: merge($x[1โ€ฆk]$, $y[1โ€ฆl]$)


if $k=0$: โ€ƒ return $y[1โ€ฆl]$
if $l=0$: โ€ƒ return $x[1โ€ฆk]$

if $x[1] \le y[1]$ then
โ€ƒโ€ƒ return $x[1]$ $\circ$ merge($x[2โ€ฆk]$, $y[1โ€ฆl]$)
else
โ€ƒโ€ƒ return $y[1]$ $\circ$ merge($x[1โ€ฆk]$, $y[2โ€ฆl]$)
end if

์ด๋ฒˆ์—๋Š” Complexity๋ฅผ ๊ณ„์‚ฐํ•ด๋ณด์ž. ์ ํ™”์‹์„ ์ž‘์„ฑํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

\[T(n) = 2 \cdot T(n/2) + O(n)\]

๊ณ ์ • ๋น„์šฉ $O(n)$๋Š” merge ์ž‘์—…์—์„œ ๋ฐœ์ƒํ•˜๋Š” ๋น„์šฉ์ด๋‹ค!

$a=2$, $b=2$, $c=1$์ด๋ฏ€๋กœ, $a/b^c = 2/2 = 1$์ด๋‹ค. ๋”ฐ๋ผ์„œ <Master Theorem>์— ๋”ฐ๋ผ

\[O(n) \cdot \log n = O(n \log n)\]

์˜ Complexity๋ฅผ ๊ฐ€์ง„๋‹ค!


Merge Sort๋Š” ๊ฐ„๋‹จํ•˜์ง€๋งŒ ๊ฐ•๋ ฅํ•˜๋‹ค! ๋ณดํ†ต Merge Sort๋ฅผ ์ง์ ‘ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒฝ์šฐ๋Š” ๋“œ๋ฌผ์ง€๋งŒ, ์–ด๋–ค ๊ฒฝ์šฐ์—์„  Merge Sort์˜ ์•„์ด๋””์–ด๋ฅผ ์ฑ„์šฉํ•ด์„œ ํ’€์–ด์•ผ ํ•œ๋‹ค!


Worst-case Analysis

์ •๋ ฌ ์•Œ๊ณ ๋ฆฌ์ฆ˜์˜ ์ ˆ์ฐจ๋Š” <ํŠธ๋ฆฌ Tree>์˜ ํ˜•ํƒœ๋กœ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์•„๋ž˜์˜ ํŠธ๋ฆฌ๋Š” $a_1$, $a_2$, $a_3$ ์„ธ ๊ฐœ์˜ ์›์†Œ์— ๋Œ€ํ•œ ์ •๋ ฌ์„ ๋„์‹ํ™”ํ•œ ๊ทธ๋ฆผ์ด๋‹ค.

์œ„์™€ ๊ฐ™์ด ์ฃผ์–ด์ง„ ๋ฐฐ์—ด์— ๋Œ€ํ•ด ์›์†Œ๋ฅผ ๋น„๊ตํ•˜๋Š” ๋ชจ๋“  ๊ฒฝ์šฐ๋ฅผ ๊ณ ๋ คํ•˜๊ฒŒ ๋˜๋ฉด, ์šฐ๋ฆฌ๋Š” ๋ฐฐ์—ด์˜ $n$๊ฐœ ์›์†Œ์— ๋Œ€ํ•œ ๊ฐ€๋Šฅํ•œ ๋ชจ๋“  <์ˆœ์—ด Permutation>์„ ํ™•์ธํ•˜๋Š” ๊ฒƒ๊ณผ ๋™์ผํ•˜๋‹ค. ์ฆ‰, $n!$ ๊ฐ€์ง“์ˆ˜์˜ ๊ฐ€๋Šฅํ•œ ์ˆœ์„œ๋ฅผ ์ƒ๊ฐํ•œ๋‹ค๋Š” ๋ง์ด๋‹ค. ์ด๋•Œ, โ€œ์ •๋ ฌโ€์ด๋ผ๋Š” ์ž‘์—…์€ <๋น„๊ต ํŠธ๋ฆฌ; Comparison Tree>์—์„œ ๊ฐ€๋Šฅํ•œ ํ•˜๋‚˜์˜ <๊ฒฝ๋กœ Path>๋ฅผ ์ทจํ•˜๋Š” ๊ฒƒ์ด๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋น„๊ต ํŠธ๋ฆฌ์—์„œ ๊ฐ€์žฅ ๊ธด ๊ฒฝ๋กœ(longest path)์˜ ๊ธธ์ด๋ฅผ ์ƒ๊ฐํ•ด๋ณด์ž. ๋น„๊ต ํŠธ๋ฆฌ๋Š” ์ด $n!$๊ฐœ ๋งŒํผ์˜ leaf node๋ฅผ ๊ฐ€์ง€๋ฏ€๋กœ ํŠธ๋ฆฌ์˜ ๊นŠ์ด(depth)๋Š” $\log {(n!)}$์ด ๋œ๋‹ค. ์ด ํŠธ๋ฆฌ์˜ โ€œ๊นŠ์ดโ€๋Š” ๊ณง Worst-case์—์„œ์˜ ๋น„๊ต ํšŸ์ˆ˜์ด๋‹ค!!

์•ฝ๊ฐ„์˜ ์ •๋ฆฌ๋“ค์„ ํ™œ์šฉํ•˜๋ฉด, $\log {(n!)}$์— ๋Œ€ํ•ด ์•„๋ž˜๊ฐ€ ์„ฑ๋ฆฝํ•จ์„ ๋ณด์ผ ์ˆ˜ ์žˆ๋‹ค.

\[\log {(n!)} \ge c \cdot n \log n \quad \textrm{for some} \; c > 0\]

์ด๊ฒƒ์€ ํ•จ์ˆ˜ $\log {(n!)}$์— ๋Œ€ํ•ด $\Omega \left( n \log {n} \right)$์ž„์„ ์˜๋ฏธํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์–ด๋–ค ์ •๋ ฌ ์•Œ๊ณ ๋ฆฌ์ฆ˜๋„ $n$๊ฐœ ์›์†Œ๋ฅผ ์ •๋ ฌํ•˜๋Š” ๋ฐ์— $\Omega (n \log {n})$ ๋งŒํผ์˜ ๋น„๊ต๋Š” ๋ฐ˜๋“œ์‹œ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•จ์„ ์˜๋ฏธํ•œ๋‹ค. ์ฆ‰, ์ •๋ ฌ ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋Š” Complexity์˜ ํ•˜ํ•œ์„ ์ด $n \log {n}$์ด๋ผ๋Š” ์˜๋ฏธ์ด๋‹ค.

๊ทธ๋Ÿฐ๋ฐ ์šฐ๋ฆฌ๋Š” ์œ— ๋ฌธ๋‹จ์—์„œ <Master Theorem>์œผ๋กœ Merge Sort๊ฐ€ $O(n \log {n})$์˜ Complexity๋ฅผ ๊ฐ€์ง„๋‹ค๋Š” ๊ฑธ ์œ ๋„ํ–ˆ๋‹ค. ์ฆ‰, Merge Sort์˜ Complexity๊ฐ€ $n \log {n}$ ๋ณด๋‹ค ์ ˆ๋Œ€ ๋†’์•„์ง€์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฑฐ๋‹ค! ์ด ๋ง์€ ๊ณง $O(n \log {n})$์˜ ํšจ์œจ์„ ๊ฐ–๋Š” Merge Sort๊ฐ€ optimal ์ •๋ ฌ ๋ฐฉ์‹์ด๋ผ๋Š” ๊ฒƒ์„ ๋งํ•œ๋‹ค!! $\blacksquare$

p.s. ๋ณธ์ธ๋„ ์ฒ˜์Œ์—๋Š” ์ดํ•ด๊ฐ€ ์ž˜ ๋˜์งˆ ์•Š์•˜๋‹ค. ์•„๋งˆ $\Omega (n \log {n})$์˜ ์˜๋ฏธ๊ฐ€ ์ž˜ ์™€๋‹ฟ์ง€ ์•Š์•˜๋˜ ๊ฒƒ ๊ฐ™์€๋ฐ, ๋ณธ์ธ์€ $\Omega (n \log {n})$๋ฅผ Complextiy์˜ ํ•˜ํ•œ์„ ์ด๋ผ๊ณ  ํ•ด์„ํ–ˆ๋‹ค. ์ฆ‰, ์ •๋ ฌ ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด $\Omega (n \log {n})$์˜ Complexity๋ฅผ ๊ฐ–๋Š”๋‹ค๋Š” ๊ฑด ๋ชจ๋“  ์ •๋ ฌ ์•Œ๊ณ ๋ฆฌ์ฆ˜์€ $n \log {n}$ ๋ณด๋‹ค ํฌ๊ฑฐ๋‚˜ ๊ฐ™์€ Complexity๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ๋ฐ–์— ์—†๋‹ค๊ณ  ํ•ด์„ํ–ˆ๋‹ค๋Š” ๋ง์ด๋‹ค. ๊ทธ๋Ÿฐ๋ฐ Merge Sort๋Š” $O(n \log {n})$์˜ Complextiy๋ฅผ ๊ฐ€์ง€๋‹ˆ, ์ ˆ๋Œ€ $n \log {n}$ ์ด์ƒ์œผ๋กœ๋Š” Complexity๊ฐ€ ์ปค์ง€์ง€ ์•Š๋Š”๋‹ค. ๊ทธ๋Ÿฌ๋‹ˆ $O(n \log {n)}$์€ $\Omega(n \log {n)}$ ์•„๋ž˜์—์„œ optimal ์ผ ์ˆ˜ ๋ฐ–์— ์—†๋‹ค!


Merge Sort - Code

์•„๋ž˜ ์ฝ”๋“œ๋Š” ๋ณธ์ธ ์Šคํƒ€์ผ ๋Œ€๋กœ ๊ตฌํ˜„ํ•œ Merge Sort ์ฝ”๋“œ๋‹ค. ์›๋ž˜๋Š” ๋ฐฐ์—ด์„ ์‚ฌ์šฉํ•ด in_place๋กœ ๊ตฌํ˜„ํ•ด๋ณด๋ ค๊ณ  ํ–ˆ๋Š”๋ฐ, ๋ญ”๊ฐ€ ์ฝ”๋“œ๊ฐ€ ์˜ˆ์˜๊ฒŒ ๋‚˜์˜ค์ง€ ์•Š์„ ๊ฒƒ ๊ฐ™์•„์„œ vector STL๊ณผ in_place๊ฐ€ ์•„๋‹Œ ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„ํ•ด๋ดค๋‹ค.

vector<int> merge(vector<int> left, vector<int> right) {
  int left_idx = 0, right_idx = 0;

  vector<int> sorted;

  while((left_idx < left.size()) && (right_idx < right.size())) {
    if (left[left_idx] <= right[right_idx]) {
      sorted.push_back(left[left_idx]);
      left_idx += 1;
    } else {
      sorted.push_back(right[right_idx]);
      right_idx += 1;
    }
  }

  for (int i = left_idx; i < left.size(); i++) {
    sorted.push_back(left[i]);
  }

  for (int i = right_idx; i < right.size(); i++) {
    sorted.push_back(right[i]);
  }

  return sorted;

}

vector<int> mergeSort(vector<int> arr) {
  if (arr.size() <= 1) {
    return arr;
  }
  int mid = arr.size() / 2;
  vector<int> leftSide = mergeSort(vector<int>(arr.begin(), arr.begin() + mid));
  vector<int> rightSide = mergeSort(vector<int>(arr.begin() + mid, arr.end()));

  return merge(leftSide, rightSide);
}

๊ฐœ์ธ์ ์ธ ๊ฒฝํ—˜์œผ๋ก  MergeSort๋Š” python์œผ๋กœ ๊ตฌํ˜„ํ•˜๋Š”๊ฒŒ ๊ฐ€์žฅ ์‰ฝ๊ณ  ํŽธํ•˜๋‹ค!

Categories:

Updated: