๐Ÿ Python์˜ sort()์™€ ํŒ€์†ŒํŠธ(Timsort) ์ •๋ฆฌ


๐ŸŽฏ ์ •๋ ฌ(Sort)์ด๋ž€?

์ •๋ ฌ์€ ๋ฐ์ดํ„ฐ๋ฅผ ์ผ์ •ํ•œ ์ˆœ์„œ(์˜ค๋ฆ„์ฐจ์ˆœ, ๋‚ด๋ฆผ์ฐจ์ˆœ ๋“ฑ)๋กœ ๋‚˜์—ดํ•˜๋Š” ์ž‘์—…์ž…๋‹ˆ๋‹ค.
์ •๋ ฌ๋œ ๋ฐ์ดํ„ฐ๋Š” ์ด์ง„ ํƒ์ƒ‰ ๋“ฑ ํšจ์œจ์ ์ธ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ํ™œ์šฉ์ด ๊ฐ€๋Šฅํ•ด์ง€๋ฏ€๋กœ, ์ปดํ“จํ„ฐ ๊ณผํ•™์—์„œ ๋งค์šฐ ์ค‘์š”ํ•œ ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค.


โš–๏ธ ์ •๋ ฌ์˜ ์•ˆ์ •์„ฑ (Stability)

  • ์•ˆ์ • ์ •๋ ฌ (Stable Sort) ๐ŸŸข
    • ๋™์ผํ•œ ๊ฐ’์„ ๊ฐ€์ง„ ๋ฐ์ดํ„ฐ์˜ ์›๋ž˜ ์ž…๋ ฅ ์ˆœ์„œ๊ฐ€ ์œ ์ง€๋˜๋Š” ์ •๋ ฌ
    • (์˜ˆ: ๋ณ‘ํ•ฉ์ •๋ ฌ, ๋ฒ„๋ธ”์ •๋ ฌ, ์‚ฝ์ž…์ •๋ ฌ)
  • ๋ถˆ์•ˆ์ • ์ •๋ ฌ (Unstable Sort) ๐Ÿ”ด
    • ๋™์ผํ•œ ๊ฐ’์˜ ์ž…๋ ฅ ์ˆœ์„œ๊ฐ€ ๋ณด์žฅ๋˜์ง€ ์•Š๋Š” ์ •๋ ฌ
    • (์˜ˆ: ํ€ต์ •๋ ฌ, ํž™์ •๋ ฌ, ์„ ํƒ์ •๋ ฌ)

๐Ÿ“Š ์ฃผ์š” ์ •๋ ฌ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์š”์•ฝ

์ •๋ ฌ ๋ฐฉ์‹ ์‹œ๊ฐ„๋ณต์žก๋„(ํ‰๊ท ) ์•ˆ์ •์„ฑ ํŠน์ง• ๋ฐ ์„ค๋ช…
๋ฒ„๋ธ” ์ •๋ ฌ O(nยฒ) ๐ŸŸข ์•ˆ์ • ์ธ์ ‘ํ•œ ์š”์†Œ๋ฅผ ๋ฐ˜๋ณต ๋น„๊ต ๋ฐ ๊ตํ™˜
์„ ํƒ ์ •๋ ฌ O(nยฒ) ๐Ÿ”ด ๋ถˆ์•ˆ์ • ์ตœ์†Ÿ๊ฐ’(๋˜๋Š” ์ตœ๋Œ“๊ฐ’) ์„ ํƒ ํ›„ ๊ตํ™˜
์‚ฝ์ž… ์ •๋ ฌ O(nยฒ) ๐ŸŸข ์•ˆ์ • ์ด๋ฏธ ์ •๋ ฌ๋œ ๋ถ€๋ถ„์— ์‚ฝ์ž…
๋ณ‘ํ•ฉ ์ •๋ ฌ O(n log n) ๐ŸŸข ์•ˆ์ • ๋ถ„ํ•  ์ •๋ณต, ์ถ”๊ฐ€ ๋ฉ”๋ชจ๋ฆฌ ํ•„์š”
ํž™ ์ •๋ ฌ O(n log n) ๐Ÿ”ด ๋ถˆ์•ˆ์ • ํž™ ์ž๋ฃŒ๊ตฌ์กฐ ์‚ฌ์šฉ, ์ œ์ž๋ฆฌ ์ •๋ ฌ
ํ€ต ์ •๋ ฌ O(n log n) ๐Ÿ”ด ๋ถˆ์•ˆ์ • ํ”ผ๋ฒ— ๊ธฐ์ค€ ๋ถ„ํ• , ํ‰๊ท ์ ์œผ๋กœ ๋น ๋ฆ„

๐Ÿ† ํŒŒ์ด์ฌ์˜ ๊ธฐ๋ณธ ์ •๋ ฌ: ํŒ€์†ŒํŠธ(Timsort)

ํŒŒ์ด์ฌ์˜ list.sort()์™€ sorted() ํ•จ์ˆ˜๋Š” ํŒ€์†ŒํŠธ(Timsort)๋ผ๋Š” ํ•˜์ด๋ธŒ๋ฆฌ๋“œ ์ •๋ ฌ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

๐Ÿค” ํŒ€์†ŒํŠธ(Timsort)๋ž€?

  • Tim Peters๊ฐ€ 2002๋…„ ํŒŒ์ด์ฌ์„ ์œ„ํ•ด ์„ค๊ณ„ํ•œ ์ •๋ ฌ ์•Œ๊ณ ๋ฆฌ์ฆ˜
  • ๋ณ‘ํ•ฉ์ •๋ ฌ(Merge Sort)๊ณผ ์‚ฝ์ž…์ •๋ ฌ(Insertion Sort)๋ฅผ ๊ฒฐํ•ฉํ•œ ํ•˜์ด๋ธŒ๋ฆฌ๋“œ ๋ฐฉ์‹
  • ์‹ค์ œ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ถ€๋ถ„์ ์œผ๋กœ ์ด๋ฏธ ์ •๋ ฌ๋˜์–ด ์žˆ๋Š” ๊ฒฝ์šฐ ๋งค์šฐ ๋น ๋ฅธ ์„ฑ๋Šฅ์„ ๋ณด์ž„
  • ํŒŒ์ด์ฌ, Java SE 7, Android, Swift ๋“ฑ ๋‹ค์–‘ํ•œ ์–ธ์–ด์—์„œ ํ‘œ์ค€ ์ •๋ ฌ๋กœ ์ฑ„ํƒ

โš™๏ธ ๋™์ž‘ ์›๋ฆฌ

  1. ๋Ÿฐ(Run) ๋ถ„ํ• 
    • ์ž…๋ ฅ ๋ฐ์ดํ„ฐ๋ฅผ ์ตœ์†Œ run ํฌ๊ธฐ(min_run, ๋ณดํ†ต 32 ๋˜๋Š” 64)๋กœ ์ž˜๋ผ
      ๊ฐ ๊ตฌ๊ฐ„(run)์ด ์ด๋ฏธ ์ •๋ ฌ๋˜์–ด ์žˆ๊ฑฐ๋‚˜,
      ์‚ฝ์ž…์ •๋ ฌ๋กœ ๋น ๋ฅด๊ฒŒ ์ •๋ ฌ๋  ์ˆ˜ ์žˆ๋„๋ก ํ•จ[2][3][4][5][7].
  2. ๋Ÿฐ ๋ณ‘ํ•ฉ
    • ์ •๋ ฌ๋œ run๋“ค์„ ๋ณ‘ํ•ฉ์ •๋ ฌ ๋ฐฉ์‹์œผ๋กœ ํ•ฉ์นจ
    • ๋ณ‘ํ•ฉ ์‹œ, ์Šคํƒ์— ์Œ“์ธ run์˜ ํฌ๊ธฐ ๋น„์œจ์„ ์กฐ์ ˆํ•ด
      ๊ท ํ˜•์žกํžŒ ๋ณ‘ํ•ฉ๊ณผ ์•ˆ์ •์„ฑ์„ ํ™•๋ณด[3][4].
  3. ๊ฐค๋กœํ•‘(Galloping) ๋ชจ๋“œ
    • ๋ณ‘ํ•ฉ ๊ณผ์ •์—์„œ ํ•œ run์˜ ๊ฐ’์ด ์—ฐ์†์ ์œผ๋กœ ์„ ํƒ๋˜๋Š” ๊ฒฝ์šฐ,
      ์ด์ง„ ํƒ์ƒ‰์„ ํ™œ์šฉํ•ด ๋Œ€๋Ÿ‰์˜ ๋ฐ์ดํ„ฐ๋ฅผ ํ•œ ๋ฒˆ์— ๋ณ‘ํ•ฉํ•˜์—ฌ ํšจ์œจ์„ ๋†’์ž„[3][4].

โœจ ํŒ€์†ŒํŠธ์˜ ์ฃผ์š” ํŠน์ง•

  • ์•ˆ์ • ์ •๋ ฌ: ๋™์ผํ•œ ๊ฐ’์˜ ์ž…๋ ฅ ์ˆœ์„œ๊ฐ€ ๋ณด์กด๋จ
  • ์ตœ์•…/ํ‰๊ท  ์‹œ๊ฐ„๋ณต์žก๋„: O(n log n)
  • ๋ถ€๋ถ„์ ์œผ๋กœ ์ •๋ ฌ๋œ ๋ฐ์ดํ„ฐ์—์„œ ๋งค์šฐ ๋น ๋ฆ„
  • ์‹ค์ œ ๋ฐ์ดํ„ฐ์˜ ํŒจํ„ด(์ž์—ฐ์Šค๋Ÿฌ์šด ์ •๋ ฌ ๊ตฌ๊ฐ„)์„ ์ตœ๋Œ€ํ•œ ํ™œ์šฉ

๐Ÿš€ ํŒ€์†ŒํŠธ์˜ ํ•ต์‹ฌ ์ตœ์ ํ™”

  • Run ํƒ์ง€ ๋ฐ ํ™•์žฅ: ์ด๋ฏธ ์ •๋ ฌ๋œ ๊ตฌ๊ฐ„(run)์„ ์ž๋™์œผ๋กœ ์ธ์‹,
    run์˜ ๊ธธ์ด๊ฐ€ min_run๋ณด๋‹ค ์งง์œผ๋ฉด ์‚ฝ์ž…์ •๋ ฌ๋กœ ํ™•์žฅ[2][3][4][7].
  • ์Šคํƒ ๊ธฐ๋ฐ˜ ๋ณ‘ํ•ฉ ๊ทœ์น™:
    ๋ณ‘ํ•ฉํ•  run์˜ ํฌ๊ธฐ ๋น„์œจ์„ ์—„๊ฒฉํžˆ ๊ด€๋ฆฌํ•ด
    ๋ณ‘ํ•ฉ ํšŸ์ˆ˜๋ฅผ ์ตœ์†Œํ™”ํ•˜๊ณ , ๋ฉ”๋ชจ๋ฆฌ ์บ์‹œ ํšจ์œจ์„ ๋†’์ž„[3].
  • ๊ฐค๋กœํ•‘(Galloping) ๊ธฐ๋ฒ•:
    ํ•œ์ชฝ run์—์„œ ์—ฐ์†์ ์œผ๋กœ ๋งŽ์€ ๊ฐ’์ด ์„ ํƒ๋˜๋ฉด,
    ์ด์ง„ ํƒ์ƒ‰์œผ๋กœ ๋ณ‘ํ•ฉ ์†๋„๋ฅผ ๊ฐ€์†ํ™”[3][4].

๐Ÿ“ ํŒ€์†ŒํŠธ์˜ ๋™์ž‘ ์˜ˆ์‹œ (๊ฐ„๋‹จ ์š”์•ฝ)

  1. ๋ฆฌ์ŠคํŠธ๋ฅผ min_run ํฌ๊ธฐ๋กœ ๋ถ„ํ• 
  2. ๊ฐ run์„ ์‚ฝ์ž…์ •๋ ฌ๋กœ ์ •๋ ฌ
  3. ์ธ์ ‘ํ•œ run๋“ค์„ ๋ณ‘ํ•ฉ์ •๋ ฌ ๋ฐฉ์‹์œผ๋กœ ํ•ฉ์นจ
  4. ๋ชจ๋“  run์ด ํ•˜๋‚˜๋กœ ํ•ฉ์ณ์งˆ ๋•Œ๊นŒ์ง€ ๋ฐ˜๋ณต

๐Ÿ’ป ํŒŒ์ด์ฌ์—์„œ ํŒ€์†ŒํŠธ ์‚ฌ์šฉ ์˜ˆ์‹œ

numbers = [5, 3, 8, 6, 2]
numbers.sort() # ๋‚ด๋ถ€์ ์œผ๋กœ ํŒ€์†ŒํŠธ ์‚ฌ์šฉ
print(numbers) # [2, 3, 5, 6, 8]

๐Ÿ“œ ์ฐธ๊ณ : ํŒ€์†ŒํŠธ ๊ตฌํ˜„์˜ ํ•ต์‹ฌ ์ฝ”๋“œ ๊ตฌ์กฐ (์˜์‚ฌ์ฝ”๋“œ)

def timsort(arr):
    min_run = 32
    n = len(arr)

    # 1. ๊ฐ run๋งˆ๋‹ค ์‚ฝ์ž…์ •๋ ฌ ์ˆ˜ํ–‰
    for start in range(0, n, min_run):
        end = min(start + min_run - 1, n - 1)
        insertion_sort(arr, start, end)

    # 2. run ๋ณ‘ํ•ฉ (๋ณ‘ํ•ฉ์ •๋ ฌ ๋ฐฉ์‹)
    size = min_run
    while size < n:
        for left in range(0, n, 2 * size):
            mid = min(n - 1, left + size - 1)
            right = min((left + 2 * size - 1), (n - 1))
            merge(arr, left, mid, right)
        size *= 2

โš ๏ธ ์‹ค์ œ ๊ตฌํ˜„์€ ํ›จ์”ฌ ๋ณต์žกํ•˜๋ฉฐ, run์˜ ํƒ์ง€, ์Šคํƒ ๊ด€๋ฆฌ, galloping ๋“ฑ ๋‹ค์–‘ํ•œ ์ตœ์ ํ™”๊ฐ€ ์ ์šฉ๋จ


๐Ÿ“š ์ฐธ๊ณ  ์ž๋ฃŒ

  • [Wikipedia: Timsort][3]
  • [GeeksforGeeks: Tim Sort in Python][2]
  • [Real Python: Sorting Algorithms in Python][5]
  • [skerritt.blog: Timsort โ€” the fastest sorting algorithm youโ€™ve never heard of][4]