gokaymeydan's picture
Upload 12 files
5c8f53e verified
# algorithms.py with metrics
import math
import time
def _snap(steps, record_steps, a, active_i, boundary=-1):
if record_steps:
steps.append(
{"array": a.copy(), "active_index": active_i, "sorted_boundary": boundary}
)
def insertion_sort(arr, record_steps: bool = True):
a = arr.copy()
steps = []
comparisons = 0
moves = 0
start = time.perf_counter()
for i in range(1, len(a)):
key = a[i]
j = i - 1
# at least one comparison if entering the while loop
while j >= 0:
comparisons += 1
if a[j] > key:
a[j + 1] = a[j]
moves += 1
_snap(steps, record_steps, a, j + 1, i)
j -= 1
else:
break
a[j + 1] = key
moves += 1
_snap(steps, record_steps, a, j + 1, i)
end = time.perf_counter()
metrics = {"comparisons": comparisons, "moves": moves, "seconds": end - start}
return steps, metrics
def merge_sort(arr, record_steps: bool = True):
a = arr.copy()
steps = []
comparisons = 0
moves = 0
start = time.perf_counter()
def merge(left, mid, right):
nonlocal comparisons, moves
# Create copies of subarrays
left_part = a[left : mid + 1]
right_part = a[mid + 1 : right + 1]
i = j = 0
k = left
# Merge while both parts have elements
while i < len(left_part) and j < len(right_part):
comparisons += 1 # one comparison each loop
if left_part[i] <= right_part[j]:
a[k] = left_part[i]
moves += 1
_snap(steps, record_steps, a, k, right)
i += 1
else:
a[k] = right_part[j]
moves += 1
_snap(steps, record_steps, a, k, right)
j += 1
k += 1
# Copy remaining elements of left_part
while i < len(left_part):
a[k] = left_part[i]
moves += 1
_snap(steps, record_steps, a, k, right)
i += 1
k += 1
# Copy remaining elements of right_part
while j < len(right_part):
a[k] = right_part[j]
moves += 1
_snap(steps, record_steps, a, k, right)
j += 1
k += 1
def sort(left, right):
if left >= right:
return
mid = (left + right) // 2
sort(left, mid)
sort(mid + 1, right)
merge(left, mid, right)
if len(a) > 0:
sort(0, len(a) - 1)
end = time.perf_counter()
metrics = {"comparisons": comparisons, "moves": moves, "seconds": end - start}
return steps, metrics
def quick_sort(arr, record_steps: bool = True):
a = arr.copy()
steps = []
comparisons = 0
moves = 0
def swap(i, j, *, active_i=None, sorted_b=None):
nonlocal moves
if i == j:
return
(
a[i],
a[j],
) = (
a[j],
a[i],
)
moves += 2
_snap(
steps,
record_steps,
a,
i if active_i is None else active_i,
-1 if sorted_b is None else sorted_b,
)
def partition(low, high):
nonlocal comparisons, moves
pivot = a[high]
i = low - 1
# compare high-1 and pivot
for j in range(low, high):
comparisons += 1
if a[j] <= pivot:
i += 1
swap(i, j, active_i=j)
# put pivot back to place
swap(i + 1, high, active_i=i + 1, sorted_b=i + 1)
return i + 1
def qs(low, high):
if low < high:
p = partition(low, high)
qs(low, p - 1)
qs(p + 1, high)
start = time.perf_counter()
if a:
qs(0, len(a) - 1)
seconds = time.perf_counter() - start
metrics = {"comparisons": comparisons, "moves": moves, "seconds": seconds}
return steps, metrics
def counting_sort(arr, k=None, record_steps: bool = True):
a = arr.copy()
steps = []
comparisons = 0
moves = 0
if not a:
return steps, {"comparisons": 0, "moves": 0, "seconds": 0.0}
# negatifleri desteklemiyorsak koruma (istersen offset ile destekleyebilirsin)
if min(a) < 0:
raise ValueError("Counting Sort: negatif değerler desteklenmiyor.")
# k (değer aralığı) verilmediyse max+1 al
if k is None:
k = max(a) + 1
start = time.perf_counter()
count = [0] * k
for v in a:
count[v] += 1
for i in range(1, k):
count[i] += count[i - 1]
out = [0] * len(a)
for v in reversed(a):
count[v] -= 1
out[count[v]] = v
for i, v in enumerate(out):
a[i] = v
moves += 1
_snap(steps, record_steps, a, i, i)
seconds = time.perf_counter() - start
return steps, {"comparisons": comparisons, "moves": moves, "seconds": seconds}
def radix_sort_lsd(arr, base=10, record_steps: bool = True):
a = arr.copy()
steps = []
comparisons = 0
moves = 0
if not a:
return steps, {"comparisons": 0, "moves": 0, "seconds": 0.0}
if base < 2:
raise ValueError("radix base must be >= 2")
start = time.perf_counter()
def digit(x, exp):
return (x // exp) % base
exp = 1
maxv = max(a)
# for each digit place
while maxv // exp > 0:
# stable counting sort by current digit
count = [0] * base
# count
for v in a:
d = digit(v, exp)
count[d] += 1
# prefix sums
for i in range(1, base):
count[i] += count[i - 1]
# build output(scan from right)
out = [0] * len(a)
for i in range(len(a) - 1, -1, -1):
v = a[i]
d = digit(v, exp)
count[d] -= 1
out[count[d]] = v
for i, v in enumerate(out):
a[i] = v
moves += 1
_snap(steps, record_steps, a, i, i)
exp *= base
seconds = time.perf_counter() - start
metrics = {"comparisons": comparisons, "moves": moves, "seconds": seconds}
return steps, metrics
# --- Heap Sort (max-heap, in-place) with metrics & step recording ---
# steps: her adımda {"array": a.copy(), "active_index": i, "sorted_boundary": b}
# - active_index: o karede yeni yazılan / swap’e giren indeks
# - sorted_boundary: heapsort’ta sıralı kuyruk (suffix) başlangıcı; j >= boundary -> "sorted"
# metrics:
# - comparisons: her a[l] > a[m] veya a[r] > a[m] kontrolü 1 karşılaştırma
# - moves: diziye her yazma 1; swap 2 move
def heap_sort(arr, record_steps: bool = True):
a = arr.copy()
steps = []
comparisons = 0
moves = 0
heap_sorted = -1
def snapshot(active_i, boundary):
_snap(steps, record_steps, a, active_i, boundary)
def swap(i, j):
nonlocal moves
if i == j:
return
a[i], a[j] = a[j], a[i]
moves += 2
snapshot(i, heap_sorted)
def heapify(n, i):
nonlocal comparisons
while True:
largest = i
l = 2 * i + 1
r = 2 * i + 2
if l < n:
comparisons += 1
if a[l] > a[largest]:
largest = l
if r < n:
comparisons += 1
if a[r] > a[largest]:
largest = r
if largest == i:
break
swap(i, largest)
i = largest
start = time.perf_counter()
n = len(a)
if n <= 1:
metrics = {"comparisons": 0, "moves": 0, "seconds": 0.0}
return steps, metrics
# build max heap
for i in range(n // 2 - 1, -1, -1):
heapify(n, i)
# extract max
for end in range(n - 1, 0, -1):
swap(0, end)
heap_sorted = end
heapify(end, 0)
seconds = time.perf_counter() - start
metrics = {"comparisons": comparisons, "moves": moves, "seconds": seconds}
return steps, metrics
def shell_sort(arr, record_steps: bool = True):
a = arr.copy()
steps = []
comparisons = 0
moves = 0
def snap(active_i, boundary=-1):
_snap(steps, record_steps, a, active_i, boundary)
start = time.perf_counter()
n = len(a)
if n <= 1:
return steps, {"comparisons": 0, "moves": 0, "seconds": 0.0}
gap = n // 2
while gap > 0:
for i in range(gap, n):
key = a[i]
j = i - gap
while j >= 0:
comparisons += 1
if a[j] > key:
a[j + gap] = a[j]
moves += 1
snap(j + gap)
j -= gap
else:
break
a[j + gap] = key
moves += 1
snap(j + gap)
gap //= 2
seconds = time.perf_counter() - start
metrics = {"comparisons": comparisons, "moves": moves, "seconds": seconds}
return steps, metrics
def bucket_sort(arr, num_buckets=None, record_steps: bool = True):
a = arr.copy()
steps = []
comparisons = 0
moves = 0
if not a:
return steps, {"comparisons": 0, "moves": 0, "seconds": 0.0}
n = len(a)
if num_buckets is None:
num_buckets = max(1, int(math.sqrt(n)))
start = time.perf_counter()
maxv = max(a)
# normlize values range between 0 and 1
if maxv == 0:
seconds = time.perf_counter() - start
for i, v in enumerate(a):
_snap(steps, record_steps, a, i, i)
return steps, {"comparisons": 0, "moves": 0, "seconds": seconds}
normalized = [x / (maxv + 1.0) for x in a]
# create buckets
buckets = [[] for _ in range(num_buckets)]
# split values to buckets
for v_norm, v_orig in zip(normalized, a):
idx = int(v_norm * num_buckets)
if idx >= num_buckets:
idx = num_buckets - 1
buckets[idx].append(v_orig)
# sort buckets
for b in buckets:
for i in range(1, len(b)):
key = b[i]
j = i - 1
while j >= 0:
comparisons += 1
if b[j] > key:
b[j + 1] = b[j]
j -= 1
else:
break
b[j + 1] = key
# save at main
write_i = 0
for b in buckets:
for v in b:
a[write_i] = v
moves += 1
_snap(steps, record_steps, a, write_i, write_i)
write_i += 1
seconds = time.perf_counter() - start
metrics = {"comparisons": comparisons, "moves": moves, "seconds": seconds}
return steps, metrics