ussu321's picture
Upload 50 files
bd86de0 verified
Raw
History Blame Contribute Delete
15.2 kB
"""
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 [],
}
@profile_algorithm
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)
@profile_algorithm
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)
@profile_algorithm
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)
@profile_algorithm
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)
@profile_algorithm
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)
@profile_algorithm
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)
@profile_algorithm
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)
@profile_algorithm
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)
@profile_algorithm
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)
@profile_algorithm
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)
@profile_algorithm
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)
@profile_algorithm
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)
@profile_algorithm
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