05_sorting.py

Download
python 379 lines 10.7 KB
  1"""
  2์ •๋ ฌ ์•Œ๊ณ ๋ฆฌ์ฆ˜ (Sorting Algorithms)
  3Sorting Algorithms Comparison
  4
  5๋‹ค์–‘ํ•œ ์ •๋ ฌ ์•Œ๊ณ ๋ฆฌ์ฆ˜์˜ ๊ตฌํ˜„๊ณผ ๋น„๊ต์ž…๋‹ˆ๋‹ค.
  6"""
  7
  8import random
  9import time
 10from typing import List
 11
 12
 13# =============================================================================
 14# 1. ๋ฒ„๋ธ” ์ •๋ ฌ (Bubble Sort)
 15# =============================================================================
 16def bubble_sort(arr: List[int]) -> List[int]:
 17    """
 18    ๋ฒ„๋ธ” ์ •๋ ฌ
 19    ์‹œ๊ฐ„: O(nยฒ), ๊ณต๊ฐ„: O(1), ์•ˆ์ • ์ •๋ ฌ
 20    """
 21    arr = arr.copy()
 22    n = len(arr)
 23
 24    for i in range(n):
 25        swapped = False
 26        for j in range(0, n - i - 1):
 27            if arr[j] > arr[j + 1]:
 28                arr[j], arr[j + 1] = arr[j + 1], arr[j]
 29                swapped = True
 30        if not swapped:
 31            break
 32
 33    return arr
 34
 35
 36# =============================================================================
 37# 2. ์„ ํƒ ์ •๋ ฌ (Selection Sort)
 38# =============================================================================
 39def selection_sort(arr: List[int]) -> List[int]:
 40    """
 41    ์„ ํƒ ์ •๋ ฌ
 42    ์‹œ๊ฐ„: O(nยฒ), ๊ณต๊ฐ„: O(1), ๋ถˆ์•ˆ์ • ์ •๋ ฌ
 43    """
 44    arr = arr.copy()
 45    n = len(arr)
 46
 47    for i in range(n):
 48        min_idx = i
 49        for j in range(i + 1, n):
 50            if arr[j] < arr[min_idx]:
 51                min_idx = j
 52        arr[i], arr[min_idx] = arr[min_idx], arr[i]
 53
 54    return arr
 55
 56
 57# =============================================================================
 58# 3. ์‚ฝ์ž… ์ •๋ ฌ (Insertion Sort)
 59# =============================================================================
 60def insertion_sort(arr: List[int]) -> List[int]:
 61    """
 62    ์‚ฝ์ž… ์ •๋ ฌ
 63    ์‹œ๊ฐ„: O(nยฒ), ์ตœ์„  O(n), ๊ณต๊ฐ„: O(1), ์•ˆ์ • ์ •๋ ฌ
 64    ๊ฑฐ์˜ ์ •๋ ฌ๋œ ๋ฐ์ดํ„ฐ์— ํšจ์œจ์ 
 65    """
 66    arr = arr.copy()
 67    n = len(arr)
 68
 69    for i in range(1, n):
 70        key = arr[i]
 71        j = i - 1
 72        while j >= 0 and arr[j] > key:
 73            arr[j + 1] = arr[j]
 74            j -= 1
 75        arr[j + 1] = key
 76
 77    return arr
 78
 79
 80# =============================================================================
 81# 4. ๋ณ‘ํ•ฉ ์ •๋ ฌ (Merge Sort)
 82# =============================================================================
 83def merge_sort(arr: List[int]) -> List[int]:
 84    """
 85    ๋ณ‘ํ•ฉ ์ •๋ ฌ
 86    ์‹œ๊ฐ„: O(n log n), ๊ณต๊ฐ„: O(n), ์•ˆ์ • ์ •๋ ฌ
 87    """
 88    if len(arr) <= 1:
 89        return arr
 90
 91    mid = len(arr) // 2
 92    left = merge_sort(arr[:mid])
 93    right = merge_sort(arr[mid:])
 94
 95    return merge(left, right)
 96
 97
 98def merge(left: List[int], right: List[int]) -> List[int]:
 99    """๋‘ ์ •๋ ฌ๋œ ๋ฐฐ์—ด ๋ณ‘ํ•ฉ"""
100    result = []
101    i = j = 0
102
103    while i < len(left) and j < len(right):
104        if left[i] <= right[j]:
105            result.append(left[i])
106            i += 1
107        else:
108            result.append(right[j])
109            j += 1
110
111    result.extend(left[i:])
112    result.extend(right[j:])
113    return result
114
115
116# =============================================================================
117# 5. ํ€ต ์ •๋ ฌ (Quick Sort)
118# =============================================================================
119def quick_sort(arr: List[int]) -> List[int]:
120    """
121    ํ€ต ์ •๋ ฌ
122    ์‹œ๊ฐ„: ํ‰๊ท  O(n log n), ์ตœ์•… O(nยฒ), ๊ณต๊ฐ„: O(log n), ๋ถˆ์•ˆ์ • ์ •๋ ฌ
123    """
124    if len(arr) <= 1:
125        return arr
126
127    pivot = arr[len(arr) // 2]
128    left = [x for x in arr if x < pivot]
129    middle = [x for x in arr if x == pivot]
130    right = [x for x in arr if x > pivot]
131
132    return quick_sort(left) + middle + quick_sort(right)
133
134
135def quick_sort_inplace(arr: List[int], low: int = 0, high: int = None) -> List[int]:
136    """ํ€ต ์ •๋ ฌ (in-place)"""
137    if high is None:
138        arr = arr.copy()
139        high = len(arr) - 1
140
141    if low < high:
142        pivot_idx = partition(arr, low, high)
143        quick_sort_inplace(arr, low, pivot_idx - 1)
144        quick_sort_inplace(arr, pivot_idx + 1, high)
145
146    return arr
147
148
149def partition(arr: List[int], low: int, high: int) -> int:
150    """ํŒŒํ‹ฐ์…˜ (Lomuto scheme)"""
151    pivot = arr[high]
152    i = low - 1
153
154    for j in range(low, high):
155        if arr[j] <= pivot:
156            i += 1
157            arr[i], arr[j] = arr[j], arr[i]
158
159    arr[i + 1], arr[high] = arr[high], arr[i + 1]
160    return i + 1
161
162
163# =============================================================================
164# 6. ํž™ ์ •๋ ฌ (Heap Sort)
165# =============================================================================
166def heap_sort(arr: List[int]) -> List[int]:
167    """
168    ํž™ ์ •๋ ฌ
169    ์‹œ๊ฐ„: O(n log n), ๊ณต๊ฐ„: O(1), ๋ถˆ์•ˆ์ • ์ •๋ ฌ
170    """
171    arr = arr.copy()
172    n = len(arr)
173
174    # ์ตœ๋Œ€ ํž™ ๊ตฌ์„ฑ
175    for i in range(n // 2 - 1, -1, -1):
176        heapify(arr, n, i)
177
178    # ํ•˜๋‚˜์”ฉ ์ถ”์ถœ
179    for i in range(n - 1, 0, -1):
180        arr[0], arr[i] = arr[i], arr[0]
181        heapify(arr, i, 0)
182
183    return arr
184
185
186def heapify(arr: List[int], n: int, i: int):
187    """์ตœ๋Œ€ ํž™ ์†์„ฑ ์œ ์ง€"""
188    largest = i
189    left = 2 * i + 1
190    right = 2 * i + 2
191
192    if left < n and arr[left] > arr[largest]:
193        largest = left
194    if right < n and arr[right] > arr[largest]:
195        largest = right
196
197    if largest != i:
198        arr[i], arr[largest] = arr[largest], arr[i]
199        heapify(arr, n, largest)
200
201
202# =============================================================================
203# 7. ๊ณ„์ˆ˜ ์ •๋ ฌ (Counting Sort)
204# =============================================================================
205def counting_sort(arr: List[int]) -> List[int]:
206    """
207    ๊ณ„์ˆ˜ ์ •๋ ฌ
208    ์‹œ๊ฐ„: O(n + k), ๊ณต๊ฐ„: O(k), ์•ˆ์ • ์ •๋ ฌ
209    k = ์ตœ๋Œ“๊ฐ’ - ์ตœ์†Ÿ๊ฐ’ + 1
210    ์ •์ˆ˜ ๋ฒ”์œ„๊ฐ€ ์ž‘์„ ๋•Œ ํšจ์œจ์ 
211    """
212    if not arr:
213        return []
214
215    min_val, max_val = min(arr), max(arr)
216    range_val = max_val - min_val + 1
217
218    count = [0] * range_val
219    output = [0] * len(arr)
220
221    # ์นด์šดํŠธ
222    for num in arr:
223        count[num - min_val] += 1
224
225    # ๋ˆ„์ 
226    for i in range(1, range_val):
227        count[i] += count[i - 1]
228
229    # ์—ญ์ˆœ์œผ๋กœ ๋ฐฐ์น˜ (์•ˆ์ •์„ฑ ์œ ์ง€)
230    for i in range(len(arr) - 1, -1, -1):
231        num = arr[i]
232        count[num - min_val] -= 1
233        output[count[num - min_val]] = num
234
235    return output
236
237
238# =============================================================================
239# 8. ๊ธฐ์ˆ˜ ์ •๋ ฌ (Radix Sort)
240# =============================================================================
241def radix_sort(arr: List[int]) -> List[int]:
242    """
243    ๊ธฐ์ˆ˜ ์ •๋ ฌ (LSD)
244    ์‹œ๊ฐ„: O(d * (n + k)), ๊ณต๊ฐ„: O(n + k)
245    d = ์ž๋ฆฟ์ˆ˜, k = ๊ธฐ์ˆ˜ (๋ณดํ†ต 10)
246    ์Œ์ˆ˜ ๋ฏธ์ง€์› ๋ฒ„์ „
247    """
248    if not arr or min(arr) < 0:
249        return sorted(arr)  # ์Œ์ˆ˜ ํฌํ•จ ์‹œ ๊ธฐ๋ณธ ์ •๋ ฌ
250
251    max_val = max(arr)
252
253    exp = 1
254    while max_val // exp > 0:
255        arr = counting_sort_by_digit(arr, exp)
256        exp *= 10
257
258    return arr
259
260
261def counting_sort_by_digit(arr: List[int], exp: int) -> List[int]:
262    """ํŠน์ • ์ž๋ฆฟ์ˆ˜ ๊ธฐ์ค€ ๊ณ„์ˆ˜ ์ •๋ ฌ"""
263    n = len(arr)
264    output = [0] * n
265    count = [0] * 10
266
267    for num in arr:
268        digit = (num // exp) % 10
269        count[digit] += 1
270
271    for i in range(1, 10):
272        count[i] += count[i - 1]
273
274    for i in range(n - 1, -1, -1):
275        digit = (arr[i] // exp) % 10
276        count[digit] -= 1
277        output[count[digit]] = arr[i]
278
279    return output
280
281
282# =============================================================================
283# ์„ฑ๋Šฅ ๋น„๊ต
284# =============================================================================
285def benchmark_sorts(size: int = 1000):
286    """์ •๋ ฌ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์„ฑ๋Šฅ ๋น„๊ต"""
287    arr = [random.randint(0, 10000) for _ in range(size)]
288
289    algorithms = [
290        ("Bubble Sort", bubble_sort, size <= 1000),
291        ("Selection Sort", selection_sort, size <= 1000),
292        ("Insertion Sort", insertion_sort, size <= 1000),
293        ("Merge Sort", merge_sort, True),
294        ("Quick Sort", quick_sort, True),
295        ("Heap Sort", heap_sort, True),
296        ("Counting Sort", counting_sort, True),
297        ("Radix Sort", radix_sort, True),
298        ("Python sorted()", sorted, True),
299    ]
300
301    print(f"\n๋ฐฐ์—ด ํฌ๊ธฐ: {size}")
302    print("-" * 50)
303
304    for name, func, should_run in algorithms:
305        if should_run:
306            test_arr = arr.copy()
307            start = time.perf_counter()
308            result = func(test_arr)
309            elapsed = time.perf_counter() - start
310            print(f"{name:20s}: {elapsed * 1000:8.3f} ms")
311        else:
312            print(f"{name:20s}: (ํฌ๊ธฐ ์ดˆ๊ณผ๋กœ ์Šคํ‚ต)")
313
314
315# =============================================================================
316# ํ…Œ์ŠคํŠธ
317# =============================================================================
318def main():
319    print("=" * 60)
320    print("์ •๋ ฌ ์•Œ๊ณ ๋ฆฌ์ฆ˜ (Sorting Algorithms)")
321    print("=" * 60)
322
323    # ํ…Œ์ŠคํŠธ ๋ฐฐ์—ด
324    arr = [64, 34, 25, 12, 22, 11, 90]
325    print(f"\n์›๋ณธ ๋ฐฐ์—ด: {arr}")
326    print("-" * 40)
327
328    # ๊ฐ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ํ…Œ์ŠคํŠธ
329    algorithms = [
330        ("๋ฒ„๋ธ” ์ •๋ ฌ", bubble_sort),
331        ("์„ ํƒ ์ •๋ ฌ", selection_sort),
332        ("์‚ฝ์ž… ์ •๋ ฌ", insertion_sort),
333        ("๋ณ‘ํ•ฉ ์ •๋ ฌ", merge_sort),
334        ("ํ€ต ์ •๋ ฌ", quick_sort),
335        ("ํž™ ์ •๋ ฌ", heap_sort),
336        ("๊ณ„์ˆ˜ ์ •๋ ฌ", counting_sort),
337        ("๊ธฐ์ˆ˜ ์ •๋ ฌ", radix_sort),
338    ]
339
340    for name, func in algorithms:
341        result = func(arr.copy())
342        print(f"{name}: {result}")
343
344    # ์„ฑ๋Šฅ ๋น„๊ต
345    print("\n" + "=" * 60)
346    print("์„ฑ๋Šฅ ๋น„๊ต (Performance Benchmark)")
347    print("=" * 60)
348
349    for size in [100, 1000, 5000]:
350        benchmark_sorts(size)
351
352    # ์ •๋ ฌ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๋น„๊ตํ‘œ
353    print("\n" + "=" * 60)
354    print("์ •๋ ฌ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๋น„๊ต")
355    print("=" * 60)
356    print("""
357    | ์•Œ๊ณ ๋ฆฌ์ฆ˜     | ํ‰๊ท       | ์ตœ์•…      | ๊ณต๊ฐ„   | ์•ˆ์ •์„ฑ | ํŠน์ง•                |
358    |-------------|----------|----------|--------|-------|---------------------|
359    | ๋ฒ„๋ธ” ์ •๋ ฌ    | O(nยฒ)    | O(nยฒ)    | O(1)   | ์•ˆ์ •  | ๋‹จ์ˆœ, ๊ต์œก์šฉ         |
360    | ์„ ํƒ ์ •๋ ฌ    | O(nยฒ)    | O(nยฒ)    | O(1)   | ๋ถˆ์•ˆ์ •| ๋‹จ์ˆœ, ๊ตํ™˜ ํšŸ์ˆ˜ ์ ์Œ  |
361    | ์‚ฝ์ž… ์ •๋ ฌ    | O(nยฒ)    | O(nยฒ)    | O(1)   | ์•ˆ์ •  | ๊ฑฐ์˜ ์ •๋ ฌ๋œ ๋ฐ์ดํ„ฐ์— ์ข‹์Œ|
362    | ๋ณ‘ํ•ฉ ์ •๋ ฌ    | O(nlogn) | O(nlogn) | O(n)   | ์•ˆ์ •  | ์ผ์ •ํ•œ ์„ฑ๋Šฅ          |
363    | ํ€ต ์ •๋ ฌ      | O(nlogn) | O(nยฒ)    | O(logn)| ๋ถˆ์•ˆ์ •| ํ‰๊ท ์ ์œผ๋กœ ๊ฐ€์žฅ ๋น ๋ฆ„  |
364    | ํž™ ์ •๋ ฌ      | O(nlogn) | O(nlogn) | O(1)   | ๋ถˆ์•ˆ์ •| in-place, ์ผ์ •ํ•œ ์„ฑ๋Šฅ|
365    | ๊ณ„์ˆ˜ ์ •๋ ฌ    | O(n+k)   | O(n+k)   | O(k)   | ์•ˆ์ •  | ์ •์ˆ˜, ๋ฒ”์œ„ ์ž‘์„ ๋•Œ    |
366    | ๊ธฐ์ˆ˜ ์ •๋ ฌ    | O(d(n+k))| O(d(n+k))| O(n+k) | ์•ˆ์ •  | ์ž๋ฆฟ์ˆ˜ ๊ธฐ๋ฐ˜           |
367
368    ์‹ค๋ฌด ์„ ํƒ ๊ฐ€์ด๋“œ:
369    - ์ผ๋ฐ˜์ ์ธ ๊ฒฝ์šฐ: ํ€ต ์ •๋ ฌ ๋˜๋Š” ์–ธ์–ด ๋‚ด์žฅ ์ •๋ ฌ
370    - ์•ˆ์ •์„ฑ ํ•„์š”: ๋ณ‘ํ•ฉ ์ •๋ ฌ
371    - ๋ฉ”๋ชจ๋ฆฌ ์ œํ•œ: ํž™ ์ •๋ ฌ
372    - ๊ฑฐ์˜ ์ •๋ ฌ๋จ: ์‚ฝ์ž… ์ •๋ ฌ
373    - ์ •์ˆ˜ + ๋ฒ”์œ„ ์ž‘์Œ: ๊ณ„์ˆ˜/๊ธฐ์ˆ˜ ์ •๋ ฌ
374    """)
375
376
377if __name__ == "__main__":
378    main()