Spaces:
Runtime error
Runtime error
| """ | |
| USSU Algorithm Analyzer v4.0 - Sorting Algorithms Suite | |
| With step capture for cyberpunk visualization and full metrics. | |
| """ | |
| import math | |
| import random | |
| from typing import List, Dict, Any, Optional, Tuple | |
| from utils.core import profile_algorithm, OperationCounter | |
| class SortingAlgorithms(OperationCounter): | |
| """Complete suite of sorting algorithms with operation counting and step logging""" | |
| def __init__(self, capture_steps: bool = False): | |
| super().__init__() | |
| self.capture_steps = capture_steps | |
| self.steps: List[Dict] = [] | |
| self.sorted_data: List[Any] = [] | |
| def reset(self): | |
| self.reset_counters() | |
| self.steps = [] | |
| self.sorted_data = [] | |
| def _log_step(self, arr: List[Any], title: str = "Sorting", | |
| compare: Optional[Tuple[int, int]] = None, | |
| swap: Optional[Tuple[int, int]] = None, | |
| sorted_prefix: int = 0): | |
| if self.capture_steps: | |
| self.steps.append({ | |
| 'array': arr.copy(), | |
| 'title': title, | |
| 'compare': compare, | |
| 'swap': swap, | |
| 'sorted_prefix': sorted_prefix | |
| }) | |
| def _make_result(self, name: str, arr: List[Any], time_c: str, space_c: str, stable: bool) -> Dict: | |
| return { | |
| 'algorithm': name, | |
| 'sorted': arr, | |
| 'time_complexity': time_c, | |
| 'space_complexity': space_c, | |
| 'stable': stable, | |
| 'comparisons': self.comparisons, | |
| 'swaps': self.swaps, | |
| 'accesses': self.accesses, | |
| 'recursions': self.recursions, | |
| 'steps': self.steps if self.capture_steps else [], | |
| } | |
| def bubble_sort(self, arr: List[Any]) -> Dict: | |
| self.reset() | |
| a = arr.copy() | |
| n = len(a) | |
| for i in range(n): | |
| swapped = False | |
| for j in range(0, n - i - 1): | |
| self.comparisons += 1 | |
| self.accesses += 2 | |
| self._log_step(a, "Bubble Sort", compare=(j, j+1), sorted_prefix=n-i) | |
| if a[j] > a[j + 1]: | |
| a[j], a[j + 1] = a[j + 1], a[j] | |
| self.swaps += 1 | |
| swapped = True | |
| self._log_step(a, "Bubble Sort - Swap", swap=(j, j+1), sorted_prefix=n-i) | |
| if not swapped: | |
| break | |
| self._log_step(a, "Bubble Sort - Complete", sorted_prefix=n) | |
| return self._make_result('Bubble Sort', a, 'O(n²)', 'O(1)', True) | |
| def selection_sort(self, arr: List[Any]) -> Dict: | |
| self.reset() | |
| a = arr.copy() | |
| n = len(a) | |
| for i in range(n): | |
| min_idx = i | |
| for j in range(i + 1, n): | |
| self.comparisons += 1 | |
| self.accesses += 1 | |
| if a[j] < a[min_idx]: | |
| min_idx = j | |
| if min_idx != i: | |
| a[i], a[min_idx] = a[min_idx], a[i] | |
| self.swaps += 1 | |
| self._log_step(a, "Selection Sort", swap=(i, min_idx), sorted_prefix=i) | |
| self._log_step(a, "Selection Sort - Complete", sorted_prefix=n) | |
| return self._make_result('Selection Sort', a, 'O(n²)', 'O(1)', False) | |
| def insertion_sort(self, arr: List[Any]) -> Dict: | |
| self.reset() | |
| a = arr.copy() | |
| for i in range(1, len(a)): | |
| key = a[i] | |
| self.accesses += 1 | |
| j = i - 1 | |
| while j >= 0: | |
| self.comparisons += 1 | |
| self.accesses += 1 | |
| if a[j] > key: | |
| a[j + 1] = a[j] | |
| self.swaps += 1 | |
| j -= 1 | |
| else: | |
| break | |
| a[j + 1] = key | |
| self.swaps += 1 | |
| self._log_step(a, "Insertion Sort", sorted_prefix=i) | |
| self._log_step(a, "Insertion Sort - Complete", sorted_prefix=len(a)) | |
| return self._make_result('Insertion Sort', a, 'O(n²)', 'O(1)', True) | |
| def merge_sort(self, arr: List[Any]) -> Dict: | |
| self.reset() | |
| a = arr.copy() | |
| def merge(left: List, right: List) -> List: | |
| result = [] | |
| i = j = 0 | |
| while i < len(left) and j < len(right): | |
| self.comparisons += 1 | |
| self.accesses += 2 | |
| if left[i] <= right[j]: | |
| result.append(left[i]); i += 1 | |
| else: | |
| result.append(right[j]); j += 1 | |
| result.extend(left[i:]) | |
| result.extend(right[j:]) | |
| return result | |
| def sort(sub_arr: List) -> List: | |
| self.recursions += 1 | |
| if len(sub_arr) <= 1: | |
| return sub_arr | |
| mid = len(sub_arr) // 2 | |
| left = sort(sub_arr[:mid]) | |
| right = sort(sub_arr[mid:]) | |
| merged = merge(left, right) | |
| self._log_step(merged, "Merge Sort - Merge") | |
| return merged | |
| sorted_arr = sort(a) | |
| self._log_step(sorted_arr, "Merge Sort - Complete") | |
| return self._make_result('Merge Sort', sorted_arr, 'O(n log n)', 'O(n)', True) | |
| def quick_sort(self, arr: List[Any]) -> Dict: | |
| self.reset() | |
| a = arr.copy() | |
| def partition(low: int, high: int) -> int: | |
| pivot = a[high] | |
| self.accesses += 1 | |
| i = low - 1 | |
| for j in range(low, high): | |
| self.comparisons += 1 | |
| self.accesses += 1 | |
| if a[j] <= pivot: | |
| i += 1 | |
| a[i], a[j] = a[j], a[i] | |
| self.swaps += 1 | |
| a[i + 1], a[high] = a[high], a[i + 1] | |
| self.swaps += 1 | |
| self._log_step(a, "Quick Sort - Partition", swap=(i+1, high)) | |
| return i + 1 | |
| def sort(low: int, high: int): | |
| self.recursions += 1 | |
| if low < high: | |
| pi = partition(low, high) | |
| sort(low, pi - 1) | |
| sort(pi + 1, high) | |
| sort(0, len(a) - 1) | |
| self._log_step(a, "Quick Sort - Complete") | |
| return self._make_result('Quick Sort', a, 'O(n log n) avg', 'O(log n)', False) | |
| def heap_sort(self, arr: List[Any]) -> Dict: | |
| self.reset() | |
| a = arr.copy() | |
| n = len(a) | |
| def heapify(size: int, root: int): | |
| largest = root | |
| left = 2 * root + 1 | |
| right = 2 * root + 2 | |
| self.accesses += 1 | |
| if left < size and a[left] > a[largest]: | |
| largest = left | |
| self.accesses += 1 | |
| if right < size and a[right] > a[largest]: | |
| largest = right | |
| self.comparisons += 1 | |
| if largest != root: | |
| a[root], a[largest] = a[largest], a[root] | |
| self.swaps += 1 | |
| heapify(size, largest) | |
| for i in range(n // 2 - 1, -1, -1): | |
| heapify(n, i) | |
| self._log_step(a, "Heap Sort - Build Heap") | |
| for i in range(n - 1, 0, -1): | |
| a[0], a[i] = a[i], a[0] | |
| self.swaps += 1 | |
| heapify(i, 0) | |
| self._log_step(a, "Heap Sort - Extract", swap=(0, i), sorted_prefix=n-i) | |
| self._log_step(a, "Heap Sort - Complete") | |
| return self._make_result('Heap Sort', a, 'O(n log n)', 'O(1)', False) | |
| def shell_sort(self, arr: List[Any]) -> Dict: | |
| self.reset() | |
| a = arr.copy() | |
| n = len(a) | |
| gap = n // 2 | |
| while gap > 0: | |
| for i in range(gap, n): | |
| temp = a[i] | |
| self.accesses += 1 | |
| j = i | |
| while j >= gap: | |
| self.comparisons += 1 | |
| self.accesses += 1 | |
| if a[j - gap] > temp: | |
| a[j] = a[j - gap] | |
| self.swaps += 1 | |
| j -= gap | |
| else: | |
| break | |
| a[j] = temp | |
| self.swaps += 1 | |
| self._log_step(a, f"Shell Sort - Gap {gap}") | |
| gap //= 2 | |
| self._log_step(a, "Shell Sort - Complete") | |
| return self._make_result('Shell Sort', a, 'O(n log² n)', 'O(1)', False) | |
| def cocktail_shaker_sort(self, arr: List[Any]) -> Dict: | |
| self.reset() | |
| a = arr.copy() | |
| n = len(a) | |
| swapped = True | |
| start = 0 | |
| end = n - 1 | |
| while swapped: | |
| swapped = False | |
| for i in range(start, end): | |
| self.comparisons += 1 | |
| self.accesses += 2 | |
| if a[i] > a[i + 1]: | |
| a[i], a[i + 1] = a[i + 1], a[i] | |
| self.swaps += 1 | |
| swapped = True | |
| if not swapped: | |
| break | |
| swapped = False | |
| end -= 1 | |
| for i in range(end - 1, start - 1, -1): | |
| self.comparisons += 1 | |
| self.accesses += 2 | |
| if a[i] > a[i + 1]: | |
| a[i], a[i + 1] = a[i + 1], a[i] | |
| self.swaps += 1 | |
| swapped = True | |
| start += 1 | |
| self._log_step(a, "Cocktail Shaker Sort", sorted_prefix=start) | |
| self._log_step(a, "Cocktail Shaker Sort - Complete") | |
| return self._make_result('Cocktail Shaker Sort', a, 'O(n²)', 'O(1)', True) | |
| def comb_sort(self, arr: List[Any]) -> Dict: | |
| self.reset() | |
| a = arr.copy() | |
| n = len(a) | |
| gap = n | |
| shrink = 1.3 | |
| sorted_flag = False | |
| while not sorted_flag: | |
| gap = int(gap / shrink) | |
| if gap <= 1: | |
| gap = 1 | |
| sorted_flag = True | |
| i = 0 | |
| while i + gap < n: | |
| self.comparisons += 1 | |
| self.accesses += 2 | |
| if a[i] > a[i + gap]: | |
| a[i], a[i + gap] = a[i + gap], a[i] | |
| self.swaps += 1 | |
| sorted_flag = False | |
| i += 1 | |
| self._log_step(a, f"Comb Sort - Gap {gap}") | |
| self._log_step(a, "Comb Sort - Complete") | |
| return self._make_result('Comb Sort', a, 'O(n²/2^p)', 'O(1)', False) | |
| def counting_sort(self, arr: List[int]) -> Dict: | |
| self.reset() | |
| if not arr: | |
| return self._make_result('Counting Sort', [], 'O(n + k)', 'O(k)', True) | |
| a = arr.copy() | |
| max_val = max(a) | |
| min_val = min(a) | |
| range_val = max_val - min_val + 1 | |
| count = [0] * range_val | |
| output = [0] * len(a) | |
| for num in a: | |
| self.accesses += 1 | |
| count[num - min_val] += 1 | |
| for i in range(1, len(count)): | |
| count[i] += count[i - 1] | |
| for i in range(len(a) - 1, -1, -1): | |
| self.accesses += 1 | |
| output[count[a[i] - min_val] - 1] = a[i] | |
| count[a[i] - min_val] -= 1 | |
| self.swaps += 1 | |
| self._log_step(output, "Counting Sort - Complete") | |
| return self._make_result('Counting Sort', output, 'O(n + k)', 'O(k)', True) | |
| def radix_sort(self, arr: List[int]) -> Dict: | |
| self.reset() | |
| if not arr: | |
| return self._make_result('Radix Sort', [], 'O(d(n+k))', 'O(n + k)', True) | |
| a = arr.copy() | |
| max_num = max(abs(x) for x in a) | |
| exp = 1 | |
| while max_num // exp > 0: | |
| counting = [[] for _ in range(10)] | |
| for num in a: | |
| self.accesses += 1 | |
| digit = (abs(num) // exp) % 10 | |
| counting[digit].append(num) | |
| a = [] | |
| for bucket in counting: | |
| a.extend(bucket) | |
| self.swaps += len(bucket) | |
| self._log_step(a, f"Radix Sort - Exp {exp}") | |
| exp *= 10 | |
| negatives = [x for x in a if x < 0] | |
| positives = [x for x in a if x >= 0] | |
| a = negatives + positives | |
| self._log_step(a, "Radix Sort - Complete") | |
| return self._make_result('Radix Sort', a, 'O(d(n+k))', 'O(n + k)', True) | |
| def bucket_sort(self, arr: List[float], bucket_count: int = 10) -> Dict: | |
| self.reset() | |
| if not arr: | |
| return self._make_result('Bucket Sort', [], 'O(n + k)', 'O(n + k)', True) | |
| a = arr.copy() | |
| min_val, max_val = min(a), max(a) | |
| buckets = [[] for _ in range(bucket_count)] | |
| for num in a: | |
| self.accesses += 1 | |
| idx = int((num - min_val) / (max_val - min_val) * (bucket_count - 1)) | |
| buckets[idx].append(num) | |
| sorted_arr = [] | |
| for b in buckets: | |
| sorted_arr.extend(sorted(b)) | |
| self.swaps += len(b) | |
| self._log_step(sorted_arr, "Bucket Sort - Complete") | |
| return self._make_result('Bucket Sort', sorted_arr, 'O(n + k)', 'O(n + k)', True) | |
| def tim_sort(self, arr: List[Any]) -> Dict: | |
| """Python's built-in Timsort for comparison""" | |
| self.reset() | |
| a = arr.copy() | |
| self.accesses += len(a) | |
| a.sort() | |
| self.swaps += len(a) # Approximation | |
| self._log_step(a, "Timsort - Complete") | |
| return self._make_result('Timsort (Python)', a, 'O(n log n)', 'O(n)', True) | |
| def compare_all(self, arr: List[Any]) -> List[Dict]: | |
| """Benchmark all sorting algorithms""" | |
| algorithms = [ | |
| ('Bubble Sort', self.bubble_sort), | |
| ('Selection Sort', self.selection_sort), | |
| ('Insertion Sort', self.insertion_sort), | |
| ('Merge Sort', self.merge_sort), | |
| ('Quick Sort', self.quick_sort), | |
| ('Heap Sort', self.heap_sort), | |
| ('Shell Sort', self.shell_sort), | |
| ('Cocktail Shaker', self.cocktail_shaker_sort), | |
| ('Comb Sort', self.comb_sort), | |
| ('Timsort', self.tim_sort), | |
| ] | |
| if arr and all(isinstance(x, int) for x in arr): | |
| algorithms.extend([ | |
| ('Counting Sort', self.counting_sort), | |
| ('Radix Sort', self.radix_sort), | |
| ('Bucket Sort', lambda a: self.bucket_sort([float(x) for x in a])), | |
| ]) | |
| results = [] | |
| for name, algo in algorithms: | |
| try: | |
| if len(arr) > 2000 and name in ['Bubble Sort', 'Selection Sort', 'Insertion Sort', 'Cocktail Shaker']: | |
| results.append({'name': name, 'time_ms': float('inf'), 'complexity': 'Skipped', 'stable': '-'}) | |
| continue | |
| res = algo(arr) | |
| results.append({ | |
| 'name': name, | |
| 'time_ms': res.get('execution_time_ms', 0), | |
| 'complexity': res.get('time_complexity', 'N/A'), | |
| 'space': res.get('space_complexity', 'N/A'), | |
| 'stable': 'Yes' if res.get('stable') else 'No', | |
| 'comparisons': res.get('comparisons', 0), | |
| }) | |
| except Exception as e: | |
| results.append({'name': name, 'error': str(e)}) | |
| return results | |