Umstellen der Auswertungslogik
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
from utils.memory_cell import MemoryCell
|
||||
from utils.literal import Literal
|
||||
from utils import AlgoContext, Int
|
||||
|
||||
x = MemoryCell(int(input("Erste Zahl: ")))
|
||||
y = MemoryCell(int(input("Zweite Zahl: ")))
|
||||
ctx = AlgoContext()
|
||||
x = Int(int(input("Erste Zahl: ")), ctx)
|
||||
y = Int(int(input("Zweite Zahl: ")), ctx)
|
||||
|
||||
while x > Literal(0):
|
||||
while x > 0:
|
||||
if x < y:
|
||||
x, y = y, x
|
||||
x -= y
|
||||
print(y)
|
||||
|
||||
print(f"Insgesamt gab es {x.sub_count + y.sub_count} Subtraktionen.")
|
||||
print(f"Insgesamt gab es {ctx.subtractions} Subtraktionen.")
|
||||
|
||||
@@ -1,22 +1,25 @@
|
||||
import random
|
||||
import pygame
|
||||
from utils.game import Game
|
||||
from utils.memory_array import MemoryArray
|
||||
from utils.algo_context import AlgoContext
|
||||
from utils.algo_array import Array
|
||||
from bubble_sorting import bubble_sort_stepwise
|
||||
|
||||
WHITE = (255, 255, 255)
|
||||
BLUE = (0, 0, 255)
|
||||
BLUE = (0, 0, 255)
|
||||
|
||||
|
||||
class BubbleGame(Game):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("Bubble Game", fps=60, size=(400, 400))
|
||||
random.seed()
|
||||
l =list(range(1, 101))
|
||||
l = list(range(1, 101))
|
||||
random.shuffle(l)
|
||||
self.z = MemoryArray(l)
|
||||
self.ctx = AlgoContext()
|
||||
self.z = Array(l, self.ctx)
|
||||
self.finished = False
|
||||
self.sort_generator = bubble_sort_stepwise(self.z)
|
||||
self.sort_generator = bubble_sort_stepwise(self.z, self.ctx)
|
||||
|
||||
def update_game(self):
|
||||
if not self.finished:
|
||||
@@ -29,7 +32,7 @@ class BubbleGame(Game):
|
||||
def draw_game(self):
|
||||
self.screen.fill(WHITE)
|
||||
for i, cell in enumerate(self.z):
|
||||
x = 50 + i*3
|
||||
x = 50 + i * 3
|
||||
y = 350 - cell.value * 3
|
||||
pygame.draw.rect(self.screen, BLUE, (x, y, 3, 3))
|
||||
super().draw_game()
|
||||
@@ -38,4 +41,3 @@ class BubbleGame(Game):
|
||||
if __name__ == "__main__":
|
||||
b = BubbleGame()
|
||||
b.run()
|
||||
|
||||
|
||||
@@ -1,83 +1,82 @@
|
||||
from utils.memory_array import MemoryArray
|
||||
from utils.memory_cell import MemoryCell
|
||||
from utils.memory_manager import MemoryManager
|
||||
from utils.memory_range import mrange
|
||||
from utils.literal import Literal
|
||||
from utils.algo_context import AlgoContext
|
||||
from utils.algo_array import Array
|
||||
from utils.algo_range import irange
|
||||
|
||||
def bubble_sort_stepwise(z: MemoryArray):
|
||||
n = z.length()
|
||||
for i in mrange(n.pred()):
|
||||
for j in mrange(n.pred(), i, -1):
|
||||
if z[j.pred()] > z[j]:
|
||||
swap(z, j, j.pred())
|
||||
|
||||
def bubble_sort_stepwise(z: Array, ctx: AlgoContext):
|
||||
"""
|
||||
Bubble Sort – schrittweise Variante (Generator).
|
||||
|
||||
Gibt nach jedem Tausch den aktuellen Array-Zustand zurück.
|
||||
Wird von bubble_game.py für die Visualisierung verwendet.
|
||||
"""
|
||||
n = len(z)
|
||||
for i in irange(n - 1):
|
||||
for j in irange(n - 1, i, -1):
|
||||
if z[j - 1] > z[j]:
|
||||
z.swap(j - 1, j)
|
||||
yield z
|
||||
|
||||
|
||||
def bubble_sort2_stepwise(z: MemoryArray):
|
||||
n = MemoryCell(z.length())
|
||||
true = Literal(1)
|
||||
false = Literal(0)
|
||||
sortiert = MemoryCell()
|
||||
def bubble_sort2_stepwise(z: Array, ctx: AlgoContext):
|
||||
"""
|
||||
Optimierter Bubble Sort mit Frühausstieg – schrittweise Variante.
|
||||
|
||||
Bricht ab, wenn in einem Durchlauf kein Tausch stattgefunden hat.
|
||||
"""
|
||||
n = len(z)
|
||||
while True:
|
||||
sortiert.set(true)
|
||||
for i in mrange(n.pred()):
|
||||
if z[i] > z[i.succ()]:
|
||||
swap(z, i, i.succ())
|
||||
sortiert.set(false)
|
||||
swapped = False
|
||||
for i in irange(n - 1):
|
||||
if z[i] > z[i + 1]:
|
||||
z.swap(i, i + 1)
|
||||
swapped = True
|
||||
yield z
|
||||
n -= Literal(1)
|
||||
if sortiert == true or n <= Literal(1):
|
||||
n -= 1
|
||||
if not swapped or n <= 1:
|
||||
break
|
||||
|
||||
|
||||
def bubble_sort(z: MemoryArray):
|
||||
sort_generator = bubble_sort_stepwise(z)
|
||||
while True:
|
||||
try:
|
||||
next(sort_generator)
|
||||
except StopIteration:
|
||||
break
|
||||
def bubble_sort(z: Array, ctx: AlgoContext):
|
||||
"""Bubble Sort – vollständige Ausführung ohne Visualisierung."""
|
||||
for _ in bubble_sort_stepwise(z, ctx):
|
||||
pass
|
||||
|
||||
|
||||
def bubble_sort2(z: MemoryArray):
|
||||
sort_generator = bubble_sort2_stepwise(z)
|
||||
while True:
|
||||
try:
|
||||
next(sort_generator)
|
||||
except StopIteration:
|
||||
break
|
||||
def bubble_sort2(z: Array, ctx: AlgoContext):
|
||||
"""Optimierter Bubble Sort – vollständige Ausführung ohne Visualisierung."""
|
||||
for _ in bubble_sort2_stepwise(z, ctx):
|
||||
pass
|
||||
|
||||
|
||||
def sort_file(filename, sort_func):
|
||||
z = MemoryArray.create_array_from_file(filename)
|
||||
sort_func(z)
|
||||
return z
|
||||
|
||||
def analyze_complexity(sort_func, sizes, presorted=False):
|
||||
"""
|
||||
Analysiert die Komplexität einer Sortierfunktion.
|
||||
Analysiert die Komplexität einer Sortierfunktion über mehrere Eingabegrößen.
|
||||
|
||||
:param sort_func: Die Funktion, die analysiert wird.
|
||||
:param sizes: Eine Liste von Eingabegrößen für die Analyse.
|
||||
Parameters
|
||||
----------
|
||||
sort_func : callable
|
||||
Signatour: sort_func(z: Array, ctx: AlgoContext)
|
||||
sizes : list[int]
|
||||
Eingabegrößen für die Analyse.
|
||||
presorted : bool
|
||||
True → sortiertes Eingabe-Array (Best-Case-Analyse).
|
||||
"""
|
||||
ctx = AlgoContext()
|
||||
for size in sizes:
|
||||
MemoryManager.purge() # Speicher zurücksetzen
|
||||
ctx.reset()
|
||||
if presorted:
|
||||
random_array = MemoryArray.create_sorted_array(size)
|
||||
z = Array.sorted(size, ctx)
|
||||
else:
|
||||
random_array = MemoryArray.create_random_array(size, -100, 100)
|
||||
sort_func(random_array)
|
||||
MemoryManager.save_stats(size)
|
||||
z = Array.random(size, -100, 100, ctx)
|
||||
sort_func(z, ctx)
|
||||
ctx.save_stats(size)
|
||||
|
||||
MemoryManager.plot_stats(["cells", "compares", "writes"])
|
||||
ctx.plot_stats(["comparisons", "writes"])
|
||||
|
||||
def swap(z: MemoryArray, i: int, j: int):
|
||||
tmp = z[Literal(i)].value
|
||||
z[Literal(i)] = z[Literal(j)]
|
||||
z[Literal(j)].set(tmp)
|
||||
|
||||
if __name__ == '__main__':
|
||||
analyze_complexity(bubble_sort, [10, 20, 30, 40, 50, 60, 70, 80, 90, 100])
|
||||
# analyze_complexity(bubble_sort2, [10, 20, 30, 40, 50, 60, 70, 80, 90, 100])
|
||||
# analyze_complexity(bubble_sort, [10, 20, 30, 40, 50, 60, 70, 80, 90, 100], True)
|
||||
# analyze_complexity(bubble_sort2, [10, 20, 30, 40, 50, 60, 70, 80, 90, 100], True)
|
||||
analyze_complexity(bubble_sort, [10, 20, 30, 40, 50, 60, 70, 80, 90, 100])
|
||||
# analyze_complexity(bubble_sort2, [10, 20, 30, 40, 50, 60, 70, 80, 90, 100])
|
||||
# analyze_complexity(bubble_sort, [10, 20, 30, 40, 50, 60, 70, 80, 90, 100], True)
|
||||
# analyze_complexity(bubble_sort2, [10, 20, 30, 40, 50, 60, 70, 80, 90, 100], True)
|
||||
|
||||
@@ -1,22 +1,25 @@
|
||||
import random
|
||||
import pygame
|
||||
from utils.game import Game
|
||||
from utils.memory_array import MemoryArray
|
||||
from utils.algo_context import AlgoContext
|
||||
from utils.algo_array import Array
|
||||
from insert_sorting import insert_sort_stepwise
|
||||
|
||||
WHITE = (255, 255, 255)
|
||||
BLUE = (0, 0, 255)
|
||||
BLUE = (0, 0, 255)
|
||||
|
||||
|
||||
class InsertGame(Game):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("Insert Game", fps=60, size=(400, 400))
|
||||
random.seed()
|
||||
l =list(range(1, 101))
|
||||
l = list(range(1, 101))
|
||||
random.shuffle(l)
|
||||
self.z = MemoryArray(l)
|
||||
self.ctx = AlgoContext()
|
||||
self.z = Array(l, self.ctx)
|
||||
self.finished = False
|
||||
self.sort_generator = insert_sort_stepwise(self.z)
|
||||
self.sort_generator = insert_sort_stepwise(self.z, self.ctx)
|
||||
|
||||
def update_game(self):
|
||||
if not self.finished:
|
||||
@@ -29,7 +32,7 @@ class InsertGame(Game):
|
||||
def draw_game(self):
|
||||
self.screen.fill(WHITE)
|
||||
for i, cell in enumerate(self.z):
|
||||
x = 50 + i*3
|
||||
x = 50 + i * 3
|
||||
y = 350 - cell.value * 3
|
||||
pygame.draw.rect(self.screen, BLUE, (x, y, 3, 3))
|
||||
super().draw_game()
|
||||
@@ -38,4 +41,3 @@ class InsertGame(Game):
|
||||
if __name__ == "__main__":
|
||||
b = InsertGame()
|
||||
b.run()
|
||||
|
||||
|
||||
@@ -1,61 +1,49 @@
|
||||
from utils.memory_array import MemoryArray
|
||||
from utils.memory_cell import MemoryCell
|
||||
from utils.memory_manager import MemoryManager
|
||||
from utils.memory_range import mrange
|
||||
from utils.literal import Literal
|
||||
from utils.algo_context import AlgoContext
|
||||
from utils.algo_array import Array
|
||||
from utils.algo_int import Int
|
||||
from utils.algo_range import irange
|
||||
|
||||
def insert_sort_stepwise(z: MemoryArray):
|
||||
n = z.length()
|
||||
j = MemoryCell()
|
||||
elem = MemoryCell()
|
||||
for i in mrange(n):
|
||||
elem.set(z[i])
|
||||
j.set(i)
|
||||
while j > Literal(0) and z[j.pred()] > elem:
|
||||
z[j].set(z[j.pred()])
|
||||
j -= Literal(1)
|
||||
|
||||
def insert_sort_stepwise(z: Array, ctx: AlgoContext):
|
||||
"""
|
||||
Insertion Sort – schrittweise Variante (Generator).
|
||||
|
||||
Gibt nach jedem Einfügevorgang den aktuellen Array-Zustand zurück.
|
||||
"""
|
||||
n = len(z)
|
||||
elem = Int(0, ctx) # Zwischenregister für das einzufügende Element
|
||||
|
||||
for i in irange(n):
|
||||
elem.set(z[i]) # 1 read + 1 write
|
||||
j = Int(int(i), ctx)
|
||||
while j > 0 and z[j - 1] > elem:
|
||||
z[j] = z[j - 1] # 1 read + 1 write
|
||||
j -= 1
|
||||
yield z
|
||||
z[j].set(elem)
|
||||
z[j] = elem # 1 read + 1 write
|
||||
yield z
|
||||
|
||||
|
||||
def insert_sort(z: MemoryArray):
|
||||
sort_generator = insert_sort_stepwise(z)
|
||||
while True:
|
||||
try:
|
||||
next(sort_generator)
|
||||
except StopIteration:
|
||||
break
|
||||
|
||||
def sort_file(filename, sort_func):
|
||||
z = MemoryArray.create_array_from_file(filename)
|
||||
sort_func(z)
|
||||
return z
|
||||
def insert_sort(z: Array, ctx: AlgoContext):
|
||||
"""Insertion Sort – vollständige Ausführung ohne Visualisierung."""
|
||||
for _ in insert_sort_stepwise(z, ctx):
|
||||
pass
|
||||
|
||||
|
||||
def analyze_complexity(sort_func, sizes, presorted=False):
|
||||
"""
|
||||
Analysiert die Komplexität einer Sortierfunktion.
|
||||
|
||||
:param sort_func: Die Funktion, die analysiert wird.
|
||||
:param sizes: Eine Liste von Eingabegrößen für die Analyse.
|
||||
"""
|
||||
ctx = AlgoContext()
|
||||
for size in sizes:
|
||||
MemoryManager.purge() # Speicher zurücksetzen
|
||||
ctx.reset()
|
||||
if presorted:
|
||||
random_array = MemoryArray.create_sorted_array(size)
|
||||
z = Array.sorted(size, ctx)
|
||||
else:
|
||||
random_array = MemoryArray.create_random_array(size, -100, 100)
|
||||
sort_func(random_array)
|
||||
MemoryManager.save_stats(size)
|
||||
z = Array.random(size, -100, 100, ctx)
|
||||
sort_func(z, ctx)
|
||||
ctx.save_stats(size)
|
||||
|
||||
MemoryManager.plot_stats(["cells", "compares", "writes"])
|
||||
ctx.plot_stats(["comparisons", "writes"])
|
||||
|
||||
def swap(z: MemoryArray, i: int, j: int):
|
||||
tmp = z[Literal(i)].value
|
||||
z[Literal(i)] = z[Literal(j)]
|
||||
z[Literal(j)].set(tmp)
|
||||
|
||||
if __name__ == '__main__':
|
||||
analyze_complexity(insert_sort, [10, 20, 30, 40, 50, 60, 70, 80, 90, 100])
|
||||
#analyze_complexity(insert_sort, [10, 20, 30, 40, 50, 60, 70, 80, 90, 100], True)
|
||||
analyze_complexity(insert_sort, [10, 20, 30, 40, 50, 60, 70, 80, 90, 100])
|
||||
# analyze_complexity(insert_sort, [10, 20, 30, 40, 50, 60, 70, 80, 90, 100], True)
|
||||
|
||||
@@ -1,22 +1,25 @@
|
||||
import random
|
||||
import pygame
|
||||
from utils.game import Game
|
||||
from utils.memory_array import MemoryArray
|
||||
from utils.algo_context import AlgoContext
|
||||
from utils.algo_array import Array
|
||||
from select_sorting import select_sort_stepwise
|
||||
|
||||
WHITE = (255, 255, 255)
|
||||
BLUE = (0, 0, 255)
|
||||
BLUE = (0, 0, 255)
|
||||
|
||||
|
||||
class SelectGame(Game):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("Select Game", fps=60, size=(400, 400))
|
||||
random.seed()
|
||||
l =list(range(1, 101))
|
||||
l = list(range(1, 101))
|
||||
random.shuffle(l)
|
||||
self.z = MemoryArray(l)
|
||||
self.ctx = AlgoContext()
|
||||
self.z = Array(l, self.ctx)
|
||||
self.finished = False
|
||||
self.sort_generator = select_sort_stepwise(self.z)
|
||||
self.sort_generator = select_sort_stepwise(self.z, self.ctx)
|
||||
|
||||
def update_game(self):
|
||||
if not self.finished:
|
||||
@@ -29,7 +32,7 @@ class SelectGame(Game):
|
||||
def draw_game(self):
|
||||
self.screen.fill(WHITE)
|
||||
for i, cell in enumerate(self.z):
|
||||
x = 50 + i*3
|
||||
x = 50 + i * 3
|
||||
y = 350 - cell.value * 3
|
||||
pygame.draw.rect(self.screen, BLUE, (x, y, 3, 3))
|
||||
super().draw_game()
|
||||
@@ -38,4 +41,3 @@ class SelectGame(Game):
|
||||
if __name__ == "__main__":
|
||||
b = SelectGame()
|
||||
b.run()
|
||||
|
||||
|
||||
@@ -1,58 +1,47 @@
|
||||
from utils.memory_array import MemoryArray
|
||||
from utils.memory_cell import MemoryCell
|
||||
from utils.memory_manager import MemoryManager
|
||||
from utils.memory_range import mrange
|
||||
from utils.literal import Literal
|
||||
from utils.algo_context import AlgoContext
|
||||
from utils.algo_array import Array
|
||||
from utils.algo_int import Int
|
||||
from utils.algo_range import irange
|
||||
|
||||
def select_sort_stepwise(z: MemoryArray):
|
||||
n = z.length()
|
||||
cur_min = MemoryCell()
|
||||
for i in mrange(n):
|
||||
cur_min.set(i)
|
||||
for j in mrange(i.succ(), n):
|
||||
|
||||
def select_sort_stepwise(z: Array, ctx: AlgoContext):
|
||||
"""
|
||||
Selection Sort – schrittweise Variante (Generator).
|
||||
|
||||
Gibt nach jedem Platztausch den aktuellen Array-Zustand zurück.
|
||||
"""
|
||||
n = len(z)
|
||||
cur_min = Int(0, ctx) # Index des aktuellen Minimums
|
||||
|
||||
for i in irange(n):
|
||||
cur_min.set(Int(int(i), ctx))
|
||||
for j in irange(int(i) + 1, n):
|
||||
if z[j] < z[cur_min]:
|
||||
cur_min.set(j)
|
||||
swap(z, i, int(cur_min))
|
||||
cur_min.set(Int(int(j), ctx))
|
||||
z.swap(int(i), int(cur_min))
|
||||
yield z
|
||||
|
||||
|
||||
def select_sort(z: MemoryArray):
|
||||
sort_generator = select_sort_stepwise(z)
|
||||
while True:
|
||||
try:
|
||||
next(sort_generator)
|
||||
except StopIteration:
|
||||
break
|
||||
|
||||
def sort_file(filename, sort_func):
|
||||
z = MemoryArray.create_array_from_file(filename)
|
||||
sort_func(z)
|
||||
return z
|
||||
def select_sort(z: Array, ctx: AlgoContext):
|
||||
"""Selection Sort – vollständige Ausführung ohne Visualisierung."""
|
||||
for _ in select_sort_stepwise(z, ctx):
|
||||
pass
|
||||
|
||||
|
||||
def analyze_complexity(sort_func, sizes, presorted=False):
|
||||
"""
|
||||
Analysiert die Komplexität einer Sortierfunktion.
|
||||
|
||||
:param sort_func: Die Funktion, die analysiert wird.
|
||||
:param sizes: Eine Liste von Eingabegrößen für die Analyse.
|
||||
"""
|
||||
ctx = AlgoContext()
|
||||
for size in sizes:
|
||||
MemoryManager.purge() # Speicher zurücksetzen
|
||||
ctx.reset()
|
||||
if presorted:
|
||||
random_array = MemoryArray.create_sorted_array(size)
|
||||
z = Array.sorted(size, ctx)
|
||||
else:
|
||||
random_array = MemoryArray.create_random_array(size, -100, 100)
|
||||
sort_func(random_array)
|
||||
MemoryManager.save_stats(size)
|
||||
z = Array.random(size, -100, 100, ctx)
|
||||
sort_func(z, ctx)
|
||||
ctx.save_stats(size)
|
||||
|
||||
MemoryManager.plot_stats(["cells", "compares", "writes"])
|
||||
ctx.plot_stats(["comparisons", "writes"])
|
||||
|
||||
def swap(z: MemoryArray, i: int, j: int):
|
||||
tmp = z[Literal(i)].value
|
||||
z[Literal(i)] = z[Literal(j)]
|
||||
z[Literal(j)].set(tmp)
|
||||
|
||||
if __name__ == '__main__':
|
||||
analyze_complexity(select_sort, [10, 20, 30, 40, 50, 60, 70, 80, 90, 100])
|
||||
# analyze_complexity(select_sort, [10, 20, 30, 40, 50, 60, 70, 80, 90, 100], True)
|
||||
analyze_complexity(select_sort, [10, 20, 30, 40, 50, 60, 70, 80, 90, 100])
|
||||
# analyze_complexity(select_sort, [10, 20, 30, 40, 50, 60, 70, 80, 90, 100], True)
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
import random
|
||||
import pygame
|
||||
from utils.game import Game
|
||||
from utils.memory_array import MemoryArray
|
||||
from utils.literal import Literal
|
||||
from utils.algo_context import AlgoContext
|
||||
from utils.algo_array import Array
|
||||
from heap_sorting import heap_sort_stepwise
|
||||
|
||||
WHITE = (255, 255, 255)
|
||||
BLUE = (0, 0, 255)
|
||||
BLUE = (0, 0, 255)
|
||||
|
||||
|
||||
class HeapGame(Game):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("Heap Game", fps=20, size=(400, 400))
|
||||
random.seed()
|
||||
l =list(range(1, 101))
|
||||
l = list(range(1, 101))
|
||||
random.shuffle(l)
|
||||
self.z = MemoryArray(l)
|
||||
self.ctx = AlgoContext()
|
||||
self.z = Array(l, self.ctx)
|
||||
self.finished = False
|
||||
self.sort_generator = heap_sort_stepwise(self.z)
|
||||
self.sort_generator = heap_sort_stepwise(self.z, self.ctx)
|
||||
|
||||
def update_game(self):
|
||||
if not self.finished:
|
||||
@@ -30,7 +32,7 @@ class HeapGame(Game):
|
||||
def draw_game(self):
|
||||
self.screen.fill(WHITE)
|
||||
for i, cell in enumerate(self.z):
|
||||
x = 50 + i*3
|
||||
x = 50 + i * 3
|
||||
y = 350 - cell.value * 3
|
||||
pygame.draw.rect(self.screen, BLUE, (x, y, 3, 3))
|
||||
super().draw_game()
|
||||
@@ -39,4 +41,3 @@ class HeapGame(Game):
|
||||
if __name__ == "__main__":
|
||||
sort_game = HeapGame()
|
||||
sort_game.run()
|
||||
|
||||
|
||||
@@ -1,93 +1,85 @@
|
||||
from utils.memory_array import MemoryArray
|
||||
from utils.memory_cell import MemoryCell
|
||||
from utils.memory_manager import MemoryManager
|
||||
from utils.memory_range import mrange
|
||||
from utils.literal import Literal
|
||||
|
||||
def heap_sort_stepwise(z: MemoryArray):
|
||||
n = z.length()
|
||||
yield from make_max_heap(z)
|
||||
with MemoryCell(n) as heapsize:
|
||||
for i in mrange(n, 1, -1):
|
||||
swap(z, 0, i.pred())
|
||||
yield z
|
||||
heapsize.set(heapsize.pred())
|
||||
yield from max_heapyfy(z, Literal(1), heapsize)
|
||||
from utils.algo_context import AlgoContext
|
||||
from utils.algo_array import Array
|
||||
|
||||
|
||||
def heap_sort(z: MemoryArray):
|
||||
sort_generator = heap_sort_stepwise(z)
|
||||
while True:
|
||||
try:
|
||||
next(sort_generator)
|
||||
except StopIteration:
|
||||
break
|
||||
# Heap verwendet 1-basierte Indizierung intern;
|
||||
# adjust_index() rechnet auf 0-basierte Array-Positionen um.
|
||||
|
||||
def left_child(i: int) -> int:
|
||||
return 2 * i
|
||||
|
||||
def right_child(i: int) -> int:
|
||||
return 2 * i + 1
|
||||
|
||||
def adjust_index(i: int) -> int:
|
||||
"""Konvertiert 1-basierten Heap-Index in 0-basierten Array-Index."""
|
||||
return i - 1
|
||||
|
||||
|
||||
def left_child(i: Literal):
|
||||
return Literal(2 * int(i))
|
||||
def heap_sort_stepwise(z: Array, ctx: AlgoContext):
|
||||
"""
|
||||
Heapsort – schrittweise Variante (Generator).
|
||||
|
||||
Baut zunächst einen Max-Heap auf, dann sortiert er durch Tauschen.
|
||||
"""
|
||||
n = len(z)
|
||||
yield from make_max_heap(z, n, ctx)
|
||||
heapsize = n
|
||||
for i in range(n, 1, -1):
|
||||
z.swap(0, i - 1)
|
||||
yield z
|
||||
heapsize -= 1
|
||||
yield from max_heapify(z, 1, heapsize, ctx)
|
||||
|
||||
|
||||
def right_child(i: Literal):
|
||||
return Literal(2 * int(i) + 1)
|
||||
def heap_sort(z: Array, ctx: AlgoContext):
|
||||
"""Heapsort – vollständige Ausführung ohne Visualisierung."""
|
||||
for _ in heap_sort_stepwise(z, ctx):
|
||||
pass
|
||||
|
||||
|
||||
def adjust_index(i: Literal):
|
||||
return i.pred()
|
||||
def make_max_heap(z: Array, n: int, ctx: AlgoContext):
|
||||
"""Baut einen Max-Heap in-place auf."""
|
||||
for i in range(n // 2, 0, -1):
|
||||
yield from max_heapify(z, i, n, ctx)
|
||||
|
||||
|
||||
def make_max_heap(z: MemoryArray):
|
||||
n = z.length()
|
||||
for i in mrange(int(n) // 2, 0, -1):
|
||||
yield from max_heapyfy(z, i, n)
|
||||
def max_heapify(z: Array, i: int, heapsize: int, ctx: AlgoContext):
|
||||
"""
|
||||
Stellt die Max-Heap-Eigenschaft für den Teilbaum bei Index i wieder her.
|
||||
|
||||
|
||||
def max_heapyfy(z: MemoryArray, i: Literal, heapsize: Literal):
|
||||
i und heapsize sind plain int (1-basiert). Vergleiche auf Array-Inhalten
|
||||
werden über Int automatisch gezählt.
|
||||
"""
|
||||
l = left_child(i)
|
||||
r = right_child(i)
|
||||
with MemoryCell(i) as max_value:
|
||||
if l <= heapsize and z[adjust_index(l)] > z[adjust_index(i)]:
|
||||
max_value.set(l)
|
||||
if r <= heapsize and z[adjust_index(r)] > z[adjust_index(max_value)]:
|
||||
max_value.set(r)
|
||||
if max_value != i:
|
||||
swap(z, int(i)-1, int(max_value)-1)
|
||||
yield z
|
||||
yield from max_heapyfy(z, max_value, heapsize)
|
||||
max_val = i
|
||||
|
||||
if l <= heapsize and z[adjust_index(l)] > z[adjust_index(i)]:
|
||||
max_val = l
|
||||
if r <= heapsize and z[adjust_index(r)] > z[adjust_index(max_val)]:
|
||||
max_val = r
|
||||
|
||||
def sort_file(filename, sort_func):
|
||||
z = MemoryArray.create_array_from_file(filename)
|
||||
sort_func(z)
|
||||
return z
|
||||
if max_val != i:
|
||||
z.swap(adjust_index(i), adjust_index(max_val))
|
||||
yield z
|
||||
yield from max_heapify(z, max_val, heapsize, ctx)
|
||||
|
||||
|
||||
def analyze_complexity(sort_func, sizes, presorted=False):
|
||||
"""
|
||||
Analysiert die Komplexität einer Sortierfunktion.
|
||||
|
||||
:param sort_func: Die Funktion, die analysiert wird.
|
||||
:param sizes: Eine Liste von Eingabegrößen für die Analyse.
|
||||
"""
|
||||
ctx = AlgoContext()
|
||||
for size in sizes:
|
||||
MemoryManager.purge() # Speicher zurücksetzen
|
||||
ctx.reset()
|
||||
if presorted:
|
||||
random_array = MemoryArray.create_sorted_array(size)
|
||||
z = Array.sorted(size, ctx)
|
||||
else:
|
||||
random_array = MemoryArray.create_random_array(size, -100, 100)
|
||||
sort_func(random_array)
|
||||
MemoryManager.save_stats(size)
|
||||
z = Array.random(size, -100, 100, ctx)
|
||||
sort_func(z, ctx)
|
||||
ctx.save_stats(size)
|
||||
|
||||
MemoryManager.plot_stats(["cells", "compares", "writes"])
|
||||
|
||||
|
||||
def swap(z: MemoryArray, i: int, j: int):
|
||||
tmp = z[Literal(i)].value
|
||||
z[Literal(i)] = z[Literal(j)]
|
||||
z[Literal(j)].set(tmp)
|
||||
ctx.plot_stats(["comparisons", "writes"])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sizes = range(10, 101, 10)
|
||||
analyze_complexity(heap_sort, sizes)
|
||||
# analyze_complexity(quick_sort, sizes, True)
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
import random
|
||||
import pygame
|
||||
from utils.game import Game
|
||||
from utils.memory_array import MemoryArray
|
||||
from utils.literal import Literal
|
||||
from utils.algo_context import AlgoContext
|
||||
from utils.algo_array import Array
|
||||
from quick_sorting import quick_sort_stepwise
|
||||
|
||||
WHITE = (255, 255, 255)
|
||||
BLUE = (0, 0, 255)
|
||||
BLUE = (0, 0, 255)
|
||||
|
||||
|
||||
class QuickGame(Game):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("Quick Game", fps=10, size=(400, 400))
|
||||
random.seed()
|
||||
l =list(range(1, 101))
|
||||
l = list(range(1, 101))
|
||||
random.shuffle(l)
|
||||
self.z = MemoryArray(l)
|
||||
self.ctx = AlgoContext()
|
||||
self.z = Array(l, self.ctx)
|
||||
self.finished = False
|
||||
self.sort_generator = quick_sort_stepwise(self.z, Literal(0), Literal(self.z.length().pred()))
|
||||
self.sort_generator = quick_sort_stepwise(self.z, self.ctx)
|
||||
|
||||
def update_game(self):
|
||||
if not self.finished:
|
||||
@@ -30,7 +32,7 @@ class QuickGame(Game):
|
||||
def draw_game(self):
|
||||
self.screen.fill(WHITE)
|
||||
for i, cell in enumerate(self.z):
|
||||
x = 50 + i*3
|
||||
x = 50 + i * 3
|
||||
y = 350 - cell.value * 3
|
||||
pygame.draw.rect(self.screen, BLUE, (x, y, 3, 3))
|
||||
super().draw_game()
|
||||
@@ -39,4 +41,3 @@ class QuickGame(Game):
|
||||
if __name__ == "__main__":
|
||||
sort_game = QuickGame()
|
||||
sort_game.run()
|
||||
|
||||
|
||||
@@ -1,81 +1,79 @@
|
||||
from utils.memory_array import MemoryArray
|
||||
from utils.memory_cell import MemoryCell
|
||||
from utils.memory_manager import MemoryManager
|
||||
from utils.memory_range import mrange
|
||||
from utils.literal import Literal
|
||||
|
||||
def quick_sort_stepwise(z: MemoryArray, l: Literal, r: Literal):
|
||||
if l < r:
|
||||
q = partition(z, l, r)
|
||||
yield z
|
||||
yield from quick_sort_stepwise(z, l, q.pred())
|
||||
yield from quick_sort_stepwise(z, q.succ(), r)
|
||||
yield z
|
||||
from utils.algo_context import AlgoContext
|
||||
from utils.algo_array import Array
|
||||
from utils.algo_int import Int
|
||||
|
||||
|
||||
def partition(z: MemoryArray, l: Literal, r: Literal):
|
||||
with MemoryCell(z[r]) as pivot, MemoryCell(l) as i, MemoryCell(r.pred()) as j:
|
||||
while i < j:
|
||||
while z[i] < pivot:
|
||||
i.set(i.succ())
|
||||
while j > l and z[j] >= pivot:
|
||||
j.set(j.pred())
|
||||
if i < j:
|
||||
swap(z, int(i), int(j))
|
||||
i.set(i.succ())
|
||||
j.set(j.pred())
|
||||
if i == j and z[i] < pivot:
|
||||
i.set(i.succ())
|
||||
if z[i] != pivot:
|
||||
swap(z, int(i), int(r))
|
||||
return Literal(i)
|
||||
def quick_sort_stepwise(z: Array, ctx: AlgoContext, l: int = 0, r: int = None):
|
||||
"""
|
||||
Quicksort – schrittweise Variante (Generator).
|
||||
|
||||
|
||||
def quick_sort(z: MemoryArray, l: Literal = None, r: Literal = None):
|
||||
if l is None:
|
||||
l = Literal(0)
|
||||
l, r sind 0-basierte Grenzen (plain int). Alle Vergleiche auf Array-Inhalten
|
||||
werden über Int automatisch gezählt.
|
||||
"""
|
||||
if r is None:
|
||||
r = z.length().pred()
|
||||
sort_generator = quick_sort_stepwise(z, l, r)
|
||||
while True:
|
||||
try:
|
||||
next(sort_generator)
|
||||
except StopIteration:
|
||||
break
|
||||
r = len(z) - 1
|
||||
if l < r:
|
||||
q = partition(z, l, r, ctx)
|
||||
yield z
|
||||
yield from quick_sort_stepwise(z, ctx, l, q - 1)
|
||||
yield from quick_sort_stepwise(z, ctx, q + 1, r)
|
||||
yield z
|
||||
|
||||
|
||||
def sort_file(filename, sort_func):
|
||||
z = MemoryArray.create_array_from_file(filename)
|
||||
sort_func(z)
|
||||
return z
|
||||
def partition(z: Array, l: int, r: int, ctx: AlgoContext) -> int:
|
||||
"""
|
||||
Lomuto-Partitionierung.
|
||||
|
||||
Wählt z[r] als Pivot. Gibt den endgültigen Pivot-Index zurück.
|
||||
"""
|
||||
pivot = Int(z[r].value, ctx) # Pivot-Wert kopieren
|
||||
ctx.reads += 1
|
||||
i = Int(l, ctx)
|
||||
j = Int(r - 1, ctx)
|
||||
|
||||
while i < j:
|
||||
while int(i) <= int(j) and z[i] < pivot:
|
||||
i += 1
|
||||
while int(j) >= l and z[j] >= pivot:
|
||||
j -= 1
|
||||
if i < j:
|
||||
z.swap(int(i), int(j))
|
||||
i += 1
|
||||
j -= 1
|
||||
|
||||
if i == j and z[i] < pivot:
|
||||
i += 1
|
||||
if z[i] != pivot:
|
||||
z.swap(int(i), r)
|
||||
|
||||
return int(i)
|
||||
|
||||
|
||||
def quick_sort(z: Array, ctx: AlgoContext, l: int = None, r: int = None):
|
||||
"""Quicksort – vollständige Ausführung ohne Visualisierung."""
|
||||
if l is None:
|
||||
l = 0
|
||||
if r is None:
|
||||
r = len(z) - 1
|
||||
for _ in quick_sort_stepwise(z, ctx, l, r):
|
||||
pass
|
||||
|
||||
|
||||
def analyze_complexity(sort_func, sizes, presorted=False):
|
||||
"""
|
||||
Analysiert die Komplexität einer Sortierfunktion.
|
||||
|
||||
:param sort_func: Die Funktion, die analysiert wird.
|
||||
:param sizes: Eine Liste von Eingabegrößen für die Analyse.
|
||||
"""
|
||||
ctx = AlgoContext()
|
||||
for size in sizes:
|
||||
MemoryManager.purge() # Speicher zurücksetzen
|
||||
ctx.reset()
|
||||
if presorted:
|
||||
random_array = MemoryArray.create_sorted_array(size)
|
||||
z = Array.sorted(size, ctx)
|
||||
else:
|
||||
random_array = MemoryArray.create_random_array(size, -100, 100)
|
||||
sort_func(random_array)
|
||||
MemoryManager.save_stats(size)
|
||||
z = Array.random(size, -100, 100, ctx)
|
||||
sort_func(z, ctx)
|
||||
ctx.save_stats(size)
|
||||
|
||||
MemoryManager.plot_stats(["cells", "compares", "writes"])
|
||||
|
||||
|
||||
def swap(z: MemoryArray, i: int, j: int):
|
||||
tmp = z[Literal(i)].value
|
||||
z[Literal(i)] = z[Literal(j)]
|
||||
z[Literal(j)].set(tmp)
|
||||
ctx.plot_stats(["comparisons", "writes"])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sizes = range(10, 101, 5)
|
||||
#analyze_complexity(quick_sort, sizes)
|
||||
analyze_complexity(quick_sort, sizes, True)
|
||||
analyze_complexity(quick_sort, sizes)
|
||||
# analyze_complexity(quick_sort, sizes, True)
|
||||
|
||||
@@ -1,60 +1,53 @@
|
||||
from utils.memory_array import MemoryArray
|
||||
from utils.memory_cell import MemoryCell
|
||||
from utils.memory_manager import MemoryManager
|
||||
from utils.memory_range import mrange
|
||||
from utils.literal import Literal
|
||||
from utils.algo_context import AlgoContext
|
||||
from utils.algo_array import Array
|
||||
from utils.algo_range import irange
|
||||
|
||||
|
||||
def count_sort(a: Array, b: Array, k: int, ctx: AlgoContext):
|
||||
"""
|
||||
Counting Sort.
|
||||
|
||||
def count_sort(a: MemoryArray, b: MemoryArray, k: int):
|
||||
c = MemoryArray(Literal(k + 1))
|
||||
for i in mrange(Literal(k + 1)):
|
||||
c[i].set(Literal(0))
|
||||
a – Eingabe-Array mit Werten aus [0, k]
|
||||
b – Ausgabe-Array (gleiche Länge wie a)
|
||||
k – maximaler Wert in a
|
||||
"""
|
||||
c = Array([0] * (k + 1), ctx) # Zählarray
|
||||
|
||||
for j in mrange(a.length()):
|
||||
c[a[j]].set(c[a[j]].succ())
|
||||
# Häufigkeiten zählen
|
||||
for j in irange(len(a)):
|
||||
c[a[j]] = c[a[j]] + 1
|
||||
|
||||
for i in mrange(Literal(1), Literal(k + 1)):
|
||||
c[i].set(int(c[i]) + int(c[i.pred()]))
|
||||
|
||||
for j in mrange(a.length().pred(), Literal(-1), Literal(-1)):
|
||||
b[c[a[j]].pred()].set(a[j])
|
||||
c[a[j]].set(c[a[j]].pred())
|
||||
# Kumulierte Summen bilden
|
||||
for i in irange(1, k + 1):
|
||||
c[i] = c[i] + c[i - 1]
|
||||
|
||||
# Stabil in b einsortieren (rückwärts für Stabilität)
|
||||
for j in irange(len(a) - 1, -1, -1):
|
||||
b[c[a[j]] - 1] = a[j]
|
||||
c[a[j]] = c[a[j]] - 1
|
||||
|
||||
|
||||
def analyze_complexity(sizes, presorted=False):
|
||||
"""
|
||||
Analysiert die Komplexität einer Sortierfunktion.
|
||||
|
||||
:param sizes: Eine Liste von Eingabegrößen für die Analyse.
|
||||
"""
|
||||
ctx = AlgoContext()
|
||||
for size in sizes:
|
||||
MemoryManager.purge() # Speicher zurücksetzen
|
||||
ctx.reset()
|
||||
if presorted:
|
||||
random_array = MemoryArray.create_sorted_array(size, 0, 100)
|
||||
z = Array.sorted(size, ctx)
|
||||
else:
|
||||
random_array = MemoryArray.create_random_array(size, 0, 100)
|
||||
dest_array = MemoryArray(Literal(size))
|
||||
count_sort(random_array, dest_array, 100)
|
||||
MemoryManager.save_stats(size)
|
||||
z = Array.random(size, 0, 100, ctx)
|
||||
dest = Array([0] * size, ctx)
|
||||
count_sort(z, dest, 100, ctx)
|
||||
ctx.save_stats(size)
|
||||
|
||||
MemoryManager.plot_stats(["cells", "compares", "writes"])
|
||||
|
||||
|
||||
def swap(z: MemoryArray, i: int, j: int):
|
||||
tmp = z[Literal(i)].value
|
||||
z[Literal(i)] = z[Literal(j)]
|
||||
z[Literal(j)].set(tmp)
|
||||
ctx.plot_stats(["reads", "writes"])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
# Test the count_sort function
|
||||
a = MemoryArray([2, 5, 3, 0, 2, 3, 0, 3])
|
||||
b = MemoryArray(Literal(len(a)))
|
||||
count_sort(a, b, 5)
|
||||
ctx = AlgoContext()
|
||||
a = Array([2, 5, 3, 0, 2, 3, 0, 3], ctx)
|
||||
b = Array([0] * len(a), ctx)
|
||||
count_sort(a, b, 5, ctx)
|
||||
print(b)
|
||||
|
||||
sizes = range(10, 101, 10)
|
||||
analyze_complexity(sizes)
|
||||
# analyze_complexity(sizes, True)
|
||||
|
||||
@@ -1,26 +1,22 @@
|
||||
from utils.memory_manager import MemoryManager
|
||||
from utils.memory_array import MemoryArray
|
||||
from utils.literal import Literal
|
||||
from utils.algo_context import AlgoContext
|
||||
from utils.algo_array import Array
|
||||
from vorlesung.L05_binaere_baeume.avl_tree import AVLTree
|
||||
|
||||
|
||||
def analyze_complexity(sizes):
|
||||
"""
|
||||
Analysiert die Komplexität
|
||||
|
||||
:param sizes: Eine Liste von Eingabegrößen für die Analyse.
|
||||
"""
|
||||
ctx = AlgoContext()
|
||||
for size in sizes:
|
||||
MemoryManager.purge() # Speicher zurücksetzen
|
||||
tree = AVLTree()
|
||||
random_array = MemoryArray.create_random_array(size, -100, 100)
|
||||
for i in range(size-1):
|
||||
tree.insert(int(random_array[Literal(i)]))
|
||||
MemoryManager.reset()
|
||||
tree.insert(int(random_array[Literal(size-1)]))
|
||||
MemoryManager.save_stats(size)
|
||||
z = Array.random(size, -100, 100, ctx)
|
||||
tree = AVLTree(ctx)
|
||||
for i in range(size - 1):
|
||||
tree.insert(z[i].value)
|
||||
ctx.reset()
|
||||
tree.insert(z[size - 1].value)
|
||||
ctx.save_stats(size)
|
||||
|
||||
ctx.plot_stats(["comparisons"])
|
||||
|
||||
MemoryManager.plot_stats(["cells", "compares"])
|
||||
|
||||
if __name__ == "__main__":
|
||||
sizes = range(1, 1001, 2)
|
||||
analyze_complexity(sizes)
|
||||
analyze_complexity(sizes)
|
||||
|
||||
@@ -1,26 +1,22 @@
|
||||
from utils.memory_manager import MemoryManager
|
||||
from utils.memory_array import MemoryArray
|
||||
from utils.literal import Literal
|
||||
from utils.algo_context import AlgoContext
|
||||
from utils.algo_array import Array
|
||||
from vorlesung.L05_binaere_baeume.bin_tree import BinaryTree
|
||||
|
||||
|
||||
def analyze_complexity(sizes):
|
||||
"""
|
||||
Analysiert die Komplexität
|
||||
|
||||
:param sizes: Eine Liste von Eingabegrößen für die Analyse.
|
||||
"""
|
||||
ctx = AlgoContext()
|
||||
for size in sizes:
|
||||
MemoryManager.purge() # Speicher zurücksetzen
|
||||
tree = BinaryTree()
|
||||
random_array = MemoryArray.create_random_array(size, -100, 100)
|
||||
for i in range(size-1):
|
||||
tree.insert(int(random_array[Literal(i)]))
|
||||
MemoryManager.reset()
|
||||
tree.insert(int(random_array[Literal(size-1)]))
|
||||
MemoryManager.save_stats(size)
|
||||
z = Array.random(size, -100, 100, ctx)
|
||||
tree = BinaryTree(ctx)
|
||||
for i in range(size - 1):
|
||||
tree.insert(z[i].value)
|
||||
ctx.reset()
|
||||
tree.insert(z[size - 1].value)
|
||||
ctx.save_stats(size)
|
||||
|
||||
ctx.plot_stats(["comparisons"])
|
||||
|
||||
MemoryManager.plot_stats(["cells", "compares"])
|
||||
|
||||
if __name__ == "__main__":
|
||||
sizes = range(1, 1001, 2)
|
||||
analyze_complexity(sizes)
|
||||
analyze_complexity(sizes)
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
from utils.memory_array import MemoryArray
|
||||
from vorlesung.L05_binaere_baeume.avl_tree_node import AVLTreeNode
|
||||
from vorlesung.L05_binaere_baeume.bin_tree import BinaryTree
|
||||
import logging
|
||||
from utils.algo_context import AlgoContext
|
||||
from utils.algo_array import Array
|
||||
|
||||
|
||||
class AVLTree(BinaryTree):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
def __init__(self, ctx: AlgoContext):
|
||||
super().__init__(ctx)
|
||||
|
||||
def new_node(self, value):
|
||||
return AVLTreeNode(value)
|
||||
|
||||
return AVLTreeNode(value, self.ctx)
|
||||
|
||||
def balance(self, node: AVLTreeNode):
|
||||
node.update_balance()
|
||||
@@ -47,7 +47,6 @@ class AVLTree(BinaryTree):
|
||||
self.balance(parent)
|
||||
return node, parent
|
||||
|
||||
|
||||
def delete(self, value):
|
||||
node, parent = super().delete(value)
|
||||
if node:
|
||||
@@ -58,37 +57,25 @@ class AVLTree(BinaryTree):
|
||||
def graph_filename(self):
|
||||
return "AVLTree"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
ctx = AlgoContext()
|
||||
tree = AVLTree(ctx)
|
||||
|
||||
values = Array.from_file("data/seq2.txt", ctx)
|
||||
for cell in values:
|
||||
tree.insert(cell.value)
|
||||
|
||||
def print_node(node, indent=0, level=0):
|
||||
print((indent * 3) * " ", node.value)
|
||||
|
||||
tree = AVLTree()
|
||||
#values = [5, 3, 7, 2, 4, 6, 5, 8]
|
||||
values = MemoryArray.create_array_from_file("data/seq2.txt")
|
||||
|
||||
for value in values:
|
||||
tree.insert(value)
|
||||
|
||||
|
||||
print("In-order traversal:")
|
||||
tree.in_order_traversal(print_node)
|
||||
print("\nLevel-order traversal:")
|
||||
tree.level_order_traversal(print_node)
|
||||
print("\nTree structure traversal:")
|
||||
tree.tree_structure_traversal(print_node)
|
||||
print("\nGraph traversal:")
|
||||
tree.graph_traversal()
|
||||
|
||||
tree.insert(9)
|
||||
tree.graph_traversal()
|
||||
|
||||
print("\nDeleting 5:")
|
||||
tree.delete(5)
|
||||
|
||||
print("In-order traversal after deletion:")
|
||||
tree.in_order_traversal(print_node)
|
||||
print("\nLevel-order traversal after deletion:")
|
||||
tree.level_order_traversal(print_node)
|
||||
print("\nTree structure traversal after deletion:")
|
||||
tree.tree_structure_traversal(print_node)
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import random
|
||||
import pygame
|
||||
from utils.game import Game
|
||||
from utils.algo_context import AlgoContext
|
||||
from avl_tree import AVLTree
|
||||
|
||||
WHITE = (255, 255, 255)
|
||||
BLUE = (0, 0, 255)
|
||||
BLACK = (0, 0, 0)
|
||||
WIDTH = 800
|
||||
HEIGHT = 400
|
||||
MARGIN = 20
|
||||
BLUE = (0, 0, 255)
|
||||
BLACK = (0, 0, 0)
|
||||
WIDTH, HEIGHT, MARGIN = 800, 400, 20
|
||||
|
||||
|
||||
class AVLTreeGame(Game):
|
||||
|
||||
@@ -18,8 +18,12 @@ class AVLTreeGame(Game):
|
||||
self.z = list(range(1, 501))
|
||||
random.shuffle(self.z)
|
||||
self.finished = False
|
||||
self.tree = AVLTree()
|
||||
self.tree.get_height = lambda node: 0 if node is None else 1 + max(self.tree.get_height(node.left), self.tree.get_height(node.right))
|
||||
self.ctx = AlgoContext()
|
||||
self.tree = AVLTree(self.ctx)
|
||||
self.tree.get_height = lambda node: (
|
||||
0 if node is None
|
||||
else 1 + max(self.tree.get_height(node.left), self.tree.get_height(node.right))
|
||||
)
|
||||
self.height = self.tree.get_height(self.tree.root)
|
||||
self.generator = None
|
||||
|
||||
@@ -43,7 +47,7 @@ class AVLTreeGame(Game):
|
||||
super().draw_game()
|
||||
|
||||
def draw_tree(self, node, x, y, x_offset):
|
||||
y_offset = (HEIGHT - (2 * MARGIN)) / self.height
|
||||
y_offset = (HEIGHT - 2 * MARGIN) / self.height
|
||||
if node is not None:
|
||||
pygame.draw.circle(self.screen, BLUE, (x, y), 2)
|
||||
if node.left is not None:
|
||||
@@ -53,7 +57,7 @@ class AVLTreeGame(Game):
|
||||
pygame.draw.line(self.screen, BLACK, (x, y), (x + x_offset, y + y_offset))
|
||||
self.draw_tree(node.right, x + x_offset, y + y_offset, x_offset // 2)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
tree_game = AVLTreeGame()
|
||||
tree_game.run()
|
||||
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
from vorlesung.L05_binaere_baeume.bin_tree_node import BinaryTreeNode
|
||||
from utils.algo_context import AlgoContext
|
||||
|
||||
|
||||
class AVLTreeNode(BinaryTreeNode):
|
||||
def __init__(self, value):
|
||||
super().__init__(value)
|
||||
|
||||
def __init__(self, value, ctx: AlgoContext):
|
||||
super().__init__(value, ctx)
|
||||
self.parent = None
|
||||
self.balance = 0
|
||||
self.balance = 0 # plain int – Metadaten, kein Zähler
|
||||
|
||||
def __repr__(self):
|
||||
return f"TreeNode(id={id(self)} value={self.value}, left={self.left}, right={self.right})"
|
||||
@@ -13,7 +16,7 @@ class AVLTreeNode(BinaryTreeNode):
|
||||
dot.node(str(id(self)), label=str(self.value), pos=f"{col},{-row}!", xlabel=str(self.balance))
|
||||
|
||||
def update_balance(self):
|
||||
left_height = self.left.height() if self.left else 0
|
||||
left_height = self.left.height() if self.left else 0
|
||||
right_height = self.right.height() if self.right else 0
|
||||
self.balance = right_height - left_height
|
||||
|
||||
@@ -58,4 +61,3 @@ class AVLTreeNode(BinaryTreeNode):
|
||||
def left_right_rotate(self):
|
||||
self.left = self.left.left_rotate()
|
||||
return self.right_rotate()
|
||||
|
||||
|
||||
@@ -1,61 +1,53 @@
|
||||
import random
|
||||
|
||||
from utils.memory_array import MemoryArray
|
||||
from utils.memory_cell import MemoryCell
|
||||
from utils.memory_manager import MemoryManager
|
||||
from utils.memory_range import mrange
|
||||
from utils.literal import Literal
|
||||
from utils.algo_context import AlgoContext
|
||||
from utils.algo_array import Array
|
||||
from utils.algo_int import Int
|
||||
|
||||
|
||||
def binary_search(z: MemoryArray, s: MemoryCell, l: Literal = None, r: Literal = None):
|
||||
def binary_search(z: Array, s: Int, l: int = None, r: int = None):
|
||||
"""
|
||||
Perform a binary search on the sorted array z for the value x.
|
||||
Binäre Suche auf dem sortierten Array z nach dem Wert s.
|
||||
|
||||
l, r – 0-basierte Grenzen (plain int, optional).
|
||||
Gibt den Index als plain int zurück, oder None wenn nicht gefunden.
|
||||
"""
|
||||
if l is None:
|
||||
l = Literal(0)
|
||||
l = 0
|
||||
if r is None:
|
||||
r = Literal(z.length().pred())
|
||||
r = len(z) - 1
|
||||
if l > r:
|
||||
return None
|
||||
with MemoryCell(l) as m:
|
||||
m += r
|
||||
m //= Literal(2)
|
||||
if s < z[m]:
|
||||
return binary_search(z, s, l, m.pred())
|
||||
elif s > z[m]:
|
||||
return binary_search(z, s, m.succ(), r)
|
||||
else:
|
||||
return m
|
||||
|
||||
m = Int((l + r) // 2, s._ctx)
|
||||
if s < z[m]:
|
||||
return binary_search(z, s, l, int(m) - 1)
|
||||
elif s > z[m]:
|
||||
return binary_search(z, s, int(m) + 1, r)
|
||||
else:
|
||||
return int(m)
|
||||
|
||||
|
||||
def analyze_complexity(sizes):
|
||||
"""
|
||||
Analysiert die Komplexität
|
||||
|
||||
:param sizes: Eine Liste von Eingabegrößen für die Analyse.
|
||||
"""
|
||||
ctx = AlgoContext()
|
||||
for size in sizes:
|
||||
MemoryManager.purge() # Speicher zurücksetzen
|
||||
random_array = MemoryArray.create_sorted_array(size)
|
||||
search_value = random.randint(-100, 100)
|
||||
binary_search(random_array, MemoryCell(search_value))
|
||||
MemoryManager.save_stats(size)
|
||||
|
||||
MemoryManager.plot_stats(["cells", "compares", "adds"])
|
||||
|
||||
ctx.reset()
|
||||
z = Array.sorted(size, ctx)
|
||||
search_value = Int(random.randint(0, size - 1), ctx)
|
||||
binary_search(z, search_value)
|
||||
ctx.save_stats(size)
|
||||
|
||||
ctx.plot_stats(["comparisons", "additions"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Example usage
|
||||
arr = MemoryArray([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
|
||||
search_value = MemoryCell(8)
|
||||
result = binary_search(arr, search_value)
|
||||
ctx = AlgoContext()
|
||||
arr = Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], ctx)
|
||||
s = Int(8, ctx)
|
||||
result = binary_search(arr, s)
|
||||
if result is not None:
|
||||
print(f"Value {search_value} found at index {result}.")
|
||||
print(f"Value {s} found at index {result}.")
|
||||
else:
|
||||
print(f"Value {search_value} not found in the array.")
|
||||
|
||||
print(f"Value {s} not found.")
|
||||
|
||||
sizes = range(1, 1001, 2)
|
||||
analyze_complexity(sizes)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from vorlesung.L05_binaere_baeume.bin_tree_node import BinaryTreeNode
|
||||
from utils.algo_context import AlgoContext
|
||||
from utils.project_dir import get_path
|
||||
from datetime import datetime
|
||||
import graphviz
|
||||
@@ -6,12 +7,13 @@ import graphviz
|
||||
|
||||
class BinaryTree:
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, ctx: AlgoContext):
|
||||
self.root = None
|
||||
self.size = 0
|
||||
self.ctx = ctx
|
||||
|
||||
def new_node(self, value):
|
||||
return BinaryTreeNode(value)
|
||||
return BinaryTreeNode(value, self.ctx)
|
||||
|
||||
def insert(self, value):
|
||||
self.size += 1
|
||||
@@ -50,9 +52,6 @@ class BinaryTree:
|
||||
return None
|
||||
|
||||
def delete(self, value):
|
||||
# Der Wert wird im Baum gesucht und der erste Treffer gelöscht
|
||||
# Rückgabe falls der Wert gefunden wird:
|
||||
# der Knoten, der den zu löschenden Knoten ersetzt und der Elternknoten des gelöschten Knotens
|
||||
parent = None
|
||||
current = self.root
|
||||
value = self.new_node(value)
|
||||
@@ -64,38 +63,25 @@ class BinaryTree:
|
||||
parent = current
|
||||
current = current.right
|
||||
else:
|
||||
# Knoten gefunden
|
||||
break
|
||||
else:
|
||||
# Wert nicht gefunden
|
||||
return None, None
|
||||
return self.delete_node(current, parent)
|
||||
|
||||
def delete_node(self, current, parent):
|
||||
# Der übergebene Knoten wird
|
||||
# Rückgabe ist ein Tupel:
|
||||
# der Knoten, der den zu löschenden Knoten ersetzt und der Elternknoten des gelöschten Knotens
|
||||
self.size -= 1
|
||||
# Fall 3: Es gibt zwei Kinder: wir suchen den Nachfolger
|
||||
# Fall 3: zwei Kinder → Nachfolger suchen
|
||||
if current.left and current.right:
|
||||
parent = current
|
||||
successor = current.right
|
||||
while successor.left:
|
||||
parent = successor
|
||||
successor = successor.left
|
||||
# Wert des Nachfolgers wird in den Knoten geschrieben, der gelöscht werden soll
|
||||
current.value = successor.value
|
||||
# Ab jetzt muss successor gelöscht werden; parent ist bereits richtig gesetzt
|
||||
current.set(successor) # Wert kopieren (1 read + 1 write)
|
||||
current = successor
|
||||
|
||||
# Ermitteln des einen Kindes (falls es eines gibt), sonst None
|
||||
# Das eine Kind ist der Ersatz für den Knoten, der gelöscht werden soll
|
||||
if current.left:
|
||||
child = current.left
|
||||
else:
|
||||
child = current.right
|
||||
child = current.left if current.left else current.right
|
||||
|
||||
# Falls es keinen Elternknoten gibt, ist der Ersatzknoten die Wurzel
|
||||
if not parent:
|
||||
self.root = child
|
||||
return child, None
|
||||
@@ -106,17 +92,13 @@ class BinaryTree:
|
||||
parent.right = child
|
||||
return child, parent
|
||||
|
||||
|
||||
def in_order_traversal(self, callback):
|
||||
|
||||
def in_order_traversal_recursive(callback, current):
|
||||
def _rec(callback, current):
|
||||
if current is not None:
|
||||
in_order_traversal_recursive(callback, current.left)
|
||||
_rec(callback, current.left)
|
||||
callback(current)
|
||||
in_order_traversal_recursive(callback, current.right)
|
||||
|
||||
in_order_traversal_recursive(callback, self.root)
|
||||
|
||||
_rec(callback, current.right)
|
||||
_rec(callback, self.root)
|
||||
|
||||
def level_order_traversal(self, callback):
|
||||
if self.root is None:
|
||||
@@ -125,23 +107,19 @@ class BinaryTree:
|
||||
while queue:
|
||||
current, level = queue.pop(0)
|
||||
callback(current, level)
|
||||
if current.left is not None:
|
||||
queue.append((current.left, level + 1))
|
||||
if current.right is not None:
|
||||
queue.append((current.right, level + 1))
|
||||
if current.left is not None: queue.append((current.left, level + 1))
|
||||
if current.right is not None: queue.append((current.right, level + 1))
|
||||
|
||||
def tree_structure_traversal(self, callback):
|
||||
|
||||
def tree_structure_traversal_recursive(callback, current, level):
|
||||
def _rec(callback, current, level):
|
||||
nonlocal line
|
||||
if current:
|
||||
tree_structure_traversal_recursive(callback, current.left, level + 1)
|
||||
_rec(callback, current.left, level + 1)
|
||||
callback(current, level, line)
|
||||
line += 1
|
||||
tree_structure_traversal_recursive(callback, current.right, level + 1)
|
||||
|
||||
_rec(callback, current.right, level + 1)
|
||||
line = 0
|
||||
tree_structure_traversal_recursive(callback, self.root, 0)
|
||||
_rec(callback, self.root, 0)
|
||||
|
||||
def graph_filename(self):
|
||||
return "BinaryTree"
|
||||
@@ -152,55 +130,44 @@ class BinaryTree:
|
||||
if node is not None:
|
||||
node.graphviz_rep(level, line, dot)
|
||||
|
||||
def graph_traversal_recursive(current):
|
||||
def _rec(current):
|
||||
nonlocal dot
|
||||
if current is not None:
|
||||
if current.left:
|
||||
dot.edge(str(id(current)), str(id(current.left)))
|
||||
graph_traversal_recursive(current.left)
|
||||
_rec(current.left)
|
||||
if current.right:
|
||||
dot.edge(str(id(current)), str(id(current.right)))
|
||||
graph_traversal_recursive(current.right)
|
||||
_rec(current.right)
|
||||
|
||||
dot = graphviz.Digraph( name="BinaryTree",
|
||||
engine="neato",
|
||||
node_attr={"shape": "circle", "fontname": "Arial"},
|
||||
format="pdf" )
|
||||
dot = graphviz.Digraph(
|
||||
name="BinaryTree",
|
||||
engine="neato",
|
||||
node_attr={"shape": "circle", "fontname": "Arial"},
|
||||
format="pdf")
|
||||
self.tree_structure_traversal(define_node)
|
||||
graph_traversal_recursive(self.root)
|
||||
_rec(self.root)
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
filename = f"{self.graph_filename()}_{timestamp}.gv"
|
||||
filename = get_path(filename)
|
||||
dot.render(filename)
|
||||
dot.render(get_path(f"{self.graph_filename()}_{timestamp}.gv"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
tree = BinaryTree()
|
||||
values = [5, 3, 7, 2, 4, 6, 5, 8]
|
||||
|
||||
for value in values:
|
||||
tree.insert(value)
|
||||
ctx = AlgoContext()
|
||||
tree = BinaryTree(ctx)
|
||||
for v in [5, 3, 7, 2, 4, 6, 5, 8]:
|
||||
tree.insert(v)
|
||||
|
||||
def print_node(node, indent=0, line=None):
|
||||
print((indent * 3) * " ", node.value)
|
||||
|
||||
|
||||
print("In-order traversal:")
|
||||
tree.in_order_traversal(print_node)
|
||||
print("\nLevel-order traversal:")
|
||||
tree.level_order_traversal(print_node)
|
||||
print("\nTree structure traversal:")
|
||||
tree.tree_structure_traversal(print_node)
|
||||
print("\nGraph traversal:")
|
||||
tree.graph_traversal()
|
||||
|
||||
print("\nDeleting 5:")
|
||||
tree.delete(5)
|
||||
|
||||
print("In-order traversal after deletion:")
|
||||
tree.in_order_traversal(print_node)
|
||||
print("\nLevel-order traversal after deletion:")
|
||||
tree.level_order_traversal(print_node)
|
||||
print("\nTree structure traversal after deletion:")
|
||||
tree.tree_structure_traversal(print_node)
|
||||
|
||||
|
||||
print("\n", ctx)
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import random
|
||||
import pygame
|
||||
from utils.game import Game
|
||||
from utils.algo_context import AlgoContext
|
||||
from bin_tree import BinaryTree
|
||||
|
||||
WHITE = (255, 255, 255)
|
||||
BLUE = (0, 0, 255)
|
||||
BLACK = (0, 0, 0)
|
||||
WIDTH = 800
|
||||
HEIGHT = 400
|
||||
MARGIN = 20
|
||||
BLUE = (0, 0, 255)
|
||||
BLACK = (0, 0, 0)
|
||||
WIDTH, HEIGHT, MARGIN = 800, 400, 20
|
||||
|
||||
|
||||
class BinTreeGame(Game):
|
||||
|
||||
@@ -18,14 +18,17 @@ class BinTreeGame(Game):
|
||||
self.z = list(range(1, 101))
|
||||
random.shuffle(self.z)
|
||||
self.finished = False
|
||||
self.tree = BinaryTree()
|
||||
self.tree.get_height = lambda node: 0 if node is None else 1 + max(self.tree.get_height(node.left), self.tree.get_height(node.right))
|
||||
self.ctx = AlgoContext()
|
||||
self.tree = BinaryTree(self.ctx)
|
||||
self.tree.get_height = lambda node: (
|
||||
0 if node is None
|
||||
else 1 + max(self.tree.get_height(node.left), self.tree.get_height(node.right))
|
||||
)
|
||||
self.height = self.tree.get_height(self.tree.root)
|
||||
|
||||
def update_game(self):
|
||||
if not self.finished:
|
||||
i = self.z.pop()
|
||||
self.tree.insert(i)
|
||||
self.tree.insert(self.z.pop())
|
||||
self.height = self.tree.get_height(self.tree.root)
|
||||
if len(self.z) == 0:
|
||||
self.finished = True
|
||||
@@ -38,7 +41,7 @@ class BinTreeGame(Game):
|
||||
super().draw_game()
|
||||
|
||||
def draw_tree(self, node, x, y, x_offset):
|
||||
y_offset = (HEIGHT - (2 * MARGIN)) / self.height
|
||||
y_offset = (HEIGHT - 2 * MARGIN) / self.height
|
||||
if node is not None:
|
||||
pygame.draw.circle(self.screen, BLUE, (x, y), 2)
|
||||
if node.left is not None:
|
||||
@@ -48,7 +51,7 @@ class BinTreeGame(Game):
|
||||
pygame.draw.line(self.screen, BLACK, (x, y), (x + x_offset, y + y_offset))
|
||||
self.draw_tree(node.right, x + x_offset, y + y_offset, x_offset // 2)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
tree_game = BinTreeGame()
|
||||
tree_game.run()
|
||||
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
from utils.memory_cell import MemoryCell
|
||||
from utils.algo_int import Int
|
||||
from utils.algo_context import AlgoContext
|
||||
|
||||
class BinaryTreeNode(MemoryCell):
|
||||
|
||||
def __init__(self, value):
|
||||
super().__init__(value)
|
||||
class BinaryTreeNode(Int):
|
||||
"""
|
||||
Knoten eines binären Suchbaums.
|
||||
|
||||
Erbt von Int – Vergleiche zwischen Knoten werden automatisch im
|
||||
AlgoContext gezählt.
|
||||
"""
|
||||
|
||||
def __init__(self, value, ctx: AlgoContext):
|
||||
super().__init__(value, ctx)
|
||||
self.left = None
|
||||
self.right = None
|
||||
|
||||
def height(self):
|
||||
left_height = self.left.height() if self.left else 0
|
||||
left_height = self.left.height() if self.left else 0
|
||||
right_height = self.right.height() if self.right else 0
|
||||
return 1 + max(left_height, right_height)
|
||||
|
||||
@@ -19,4 +27,4 @@ class BinaryTreeNode(MemoryCell):
|
||||
return str(self.value)
|
||||
|
||||
def graphviz_rep(self, row, col, dot):
|
||||
dot.node(str(id(self)), label=str(self.value), pos=f"{col},{-row}!")
|
||||
dot.node(str(id(self)), label=str(self.value), pos=f"{col},{-row}!")
|
||||
|
||||
@@ -1,58 +1,63 @@
|
||||
from utils.memory_manager import MemoryManager
|
||||
from utils.memory_array import MemoryArray
|
||||
from utils.literal import Literal
|
||||
from utils.algo_context import AlgoContext
|
||||
from utils.algo_array import Array
|
||||
from b_tree import BTree
|
||||
from b_tree_node import BTreeNode
|
||||
|
||||
class MemoryManagerBTree(MemoryManager):
|
||||
"""
|
||||
Diese Klasse erweitert den MemoryManager, um spezifische Statistiken für B-Bäume zu speichern.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def count_loads():
|
||||
return sum([cell.loaded_count for cell in MemoryManager().cells if isinstance(cell, BTreeNode)])
|
||||
|
||||
@staticmethod
|
||||
def count_saves():
|
||||
return sum([cell.saved_count for cell in MemoryManager().cells if isinstance(cell, BTreeNode)])
|
||||
|
||||
@staticmethod
|
||||
def save_stats(count):
|
||||
data = { "cells": MemoryManager.count_cells(),
|
||||
"reads": MemoryManager.count_reads(),
|
||||
"writes": MemoryManager.count_writes(),
|
||||
"compares": MemoryManager.count_compares(),
|
||||
"adds": MemoryManager.count_adds(),
|
||||
"subs": MemoryManager.count_subs(),
|
||||
"muls": MemoryManager.count_muls(),
|
||||
"divs": MemoryManager.count_divs(),
|
||||
"bitops": MemoryManager.count_bitops(),
|
||||
"loads": MemoryManagerBTree.count_loads(),
|
||||
"saves": MemoryManagerBTree.count_saves() }
|
||||
MemoryManager.stats[count] = data
|
||||
def count_loads(root: BTreeNode) -> int:
|
||||
"""Summiert load()-Aufrufe über alle Knoten des Baums."""
|
||||
if root is None:
|
||||
return 0
|
||||
total = root.loaded_count
|
||||
for child in root.children:
|
||||
if child is not None:
|
||||
total += count_loads(child)
|
||||
return total
|
||||
|
||||
|
||||
def count_saves(root: BTreeNode) -> int:
|
||||
"""Summiert save()-Aufrufe über alle Knoten des Baums."""
|
||||
if root is None:
|
||||
return 0
|
||||
total = root.saved_count
|
||||
for child in root.children:
|
||||
if child is not None:
|
||||
total += count_saves(child)
|
||||
return total
|
||||
|
||||
|
||||
def analyze_complexity(sizes):
|
||||
"""
|
||||
Analysiert die Komplexität
|
||||
ctx = AlgoContext()
|
||||
stats: dict[int, dict] = {}
|
||||
|
||||
:param sizes: Eine Liste von Eingabegrößen für die Analyse.
|
||||
"""
|
||||
for size in sizes:
|
||||
MemoryManager.purge() # Speicher zurücksetzen
|
||||
tree = BTree(5)
|
||||
random_array = MemoryArray.create_random_array(size, -100, 100)
|
||||
for i in range(size-1):
|
||||
tree.insert(int(random_array[Literal(i)]))
|
||||
MemoryManager.reset()
|
||||
tree.insert(int(random_array[Literal(size-1)]))
|
||||
MemoryManagerBTree.save_stats(size)
|
||||
ctx.reset()
|
||||
z = Array.random(size, -100, 100, ctx)
|
||||
tree = BTree(5, ctx)
|
||||
for i in range(size - 1):
|
||||
tree.insert(z[i])
|
||||
ctx.reset()
|
||||
tree.insert(z[size - 1])
|
||||
stats[size] = {
|
||||
"comparisons": ctx.comparisons,
|
||||
"writes": ctx.writes,
|
||||
"loads": count_loads(tree.root),
|
||||
"saves": count_saves(tree.root),
|
||||
}
|
||||
|
||||
# Einfaches Liniendiagramm über alle gespeicherten Metriken
|
||||
import matplotlib.pyplot as plt
|
||||
x = list(stats.keys())
|
||||
fig, axes = plt.subplots(len(stats[x[0]]), 1, figsize=(8, 12), sharex=True)
|
||||
for ax, label in zip(axes, stats[x[0]].keys()):
|
||||
ax.plot(x, [stats[k][label] for k in x], label=label)
|
||||
ax.set_ylabel(label)
|
||||
ax.legend()
|
||||
plt.xlabel("n")
|
||||
plt.tight_layout()
|
||||
plt.show()
|
||||
|
||||
MemoryManager.plot_stats(["cells", "compares", "loads", "saves"])
|
||||
|
||||
if __name__ == "__main__":
|
||||
sizes = range(1, 1001, 2)
|
||||
analyze_complexity(sizes)
|
||||
analyze_complexity(sizes)
|
||||
|
||||
@@ -1,23 +1,29 @@
|
||||
from utils.literal import Literal
|
||||
from utils.memory_cell import MemoryCell
|
||||
from utils.memory_array import MemoryArray
|
||||
from utils.algo_context import AlgoContext
|
||||
from utils.algo_array import Array
|
||||
from utils.algo_int import Int
|
||||
from b_tree_node import BTreeNode
|
||||
|
||||
|
||||
class BTree:
|
||||
def __init__(self, m: int):
|
||||
|
||||
def __init__(self, m: int, ctx: AlgoContext):
|
||||
self.m = m
|
||||
self.root = BTreeNode(m)
|
||||
self.ctx = ctx
|
||||
self.root = BTreeNode(m, ctx)
|
||||
|
||||
def _new_node(self):
|
||||
return BTreeNode(self.m, self.ctx)
|
||||
|
||||
def search(self, value, start: BTreeNode = None) -> BTreeNode | None:
|
||||
if not start:
|
||||
start = self.root
|
||||
start.load()
|
||||
if not isinstance(value, Int):
|
||||
value = Int(value, self.ctx)
|
||||
i = 0
|
||||
if not isinstance(value, MemoryCell):
|
||||
value = MemoryCell(value)
|
||||
while i < start.n and value > start.value[Literal(i)]:
|
||||
while i < start.n and value > start.value[i]:
|
||||
i += 1
|
||||
if i < start.n and value == start.value[Literal(i)]:
|
||||
if i < start.n and value == start.value[i]:
|
||||
return start
|
||||
if start.leaf:
|
||||
return None
|
||||
@@ -26,11 +32,11 @@ class BTree:
|
||||
def split_child(self, parent: BTreeNode, i: int):
|
||||
child = parent.children[i]
|
||||
child.load()
|
||||
h = BTreeNode(self.m)
|
||||
h = self._new_node()
|
||||
h.leaf = child.leaf
|
||||
h.n = self.m - 1
|
||||
for j in range(self.m - 1):
|
||||
h.value[Literal(j)] = child.value[Literal(j + self.m)]
|
||||
h.value[j] = child.value[j + self.m]
|
||||
if not h.leaf:
|
||||
for j in range(self.m):
|
||||
h.children[j] = child.children[j + self.m]
|
||||
@@ -41,18 +47,18 @@ class BTree:
|
||||
h.save()
|
||||
for j in range(parent.n, i, -1):
|
||||
parent.children[j + 1] = parent.children[j]
|
||||
parent.value[Literal(j)] = parent.value[Literal(j - 1)]
|
||||
parent.value[j] = parent.value[j - 1]
|
||||
parent.children[i + 1] = h
|
||||
parent.value[Literal(i)] = child.value[Literal(self.m - 1)]
|
||||
parent.value[i] = child.value[self.m - 1]
|
||||
parent.n += 1
|
||||
parent.save()
|
||||
|
||||
def insert(self, value):
|
||||
if not isinstance(value, MemoryCell):
|
||||
value = MemoryCell(value)
|
||||
if not isinstance(value, Int):
|
||||
value = Int(value, self.ctx)
|
||||
r = self.root
|
||||
if r.n == 2 * self.m - 1:
|
||||
h = BTreeNode(self.m)
|
||||
h = self._new_node()
|
||||
self.root = h
|
||||
h.leaf = False
|
||||
h.n = 0
|
||||
@@ -62,44 +68,40 @@ class BTree:
|
||||
else:
|
||||
self.insert_in_node(r, value)
|
||||
|
||||
def insert_in_node(self, start: BTreeNode, value):
|
||||
def insert_in_node(self, start: BTreeNode, value: Int):
|
||||
start.load()
|
||||
i = start.n
|
||||
if start.leaf:
|
||||
while i >= 1 and value < start.value[Literal(i-1)]:
|
||||
start.value[Literal(i)] = start.value[Literal(i-1)]
|
||||
while i >= 1 and value < start.value[i - 1]:
|
||||
start.value[i] = start.value[i - 1]
|
||||
i -= 1
|
||||
start.value[Literal(i)].set(value)
|
||||
start.value[i] = value
|
||||
start.n += 1
|
||||
start.save()
|
||||
else:
|
||||
j = 0
|
||||
while j < start.n and value > start.value[Literal(j)]:
|
||||
while j < start.n and value > start.value[j]:
|
||||
j += 1
|
||||
if start.children[j].n == 2 * self.m - 1:
|
||||
self.split_child(start, j)
|
||||
if value > start.value[Literal(j)]:
|
||||
if value > start.value[j]:
|
||||
j += 1
|
||||
self.insert_in_node(start.children[j], value)
|
||||
|
||||
def traversal(self, callback):
|
||||
def traversal_recursive(node, callback):
|
||||
def _rec(node, callback):
|
||||
i = 0
|
||||
while i < node.n:
|
||||
if not node.leaf:
|
||||
traversal_recursive(node.children[i], callback)
|
||||
callback(node.value[Literal(i)])
|
||||
_rec(node.children[i], callback)
|
||||
callback(node.value[i])
|
||||
i += 1
|
||||
if not node.leaf:
|
||||
traversal_recursive(node.children[i], callback)
|
||||
|
||||
traversal_recursive(self.root, callback)
|
||||
_rec(node.children[i], callback)
|
||||
_rec(self.root, callback)
|
||||
|
||||
def walk(self):
|
||||
def print_key(key):
|
||||
print(key, end=" ")
|
||||
|
||||
self.traversal(print_key)
|
||||
self.traversal(lambda key: print(key, end=" "))
|
||||
|
||||
def height(self, start: BTreeNode = None):
|
||||
if not start:
|
||||
@@ -110,8 +112,9 @@ class BTree:
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
a = MemoryArray.create_array_from_file("data/seq3.txt")
|
||||
tree = BTree(3)
|
||||
ctx = AlgoContext()
|
||||
a = Array.from_file("data/seq3.txt", ctx)
|
||||
tree = BTree(3, ctx)
|
||||
for cell in a:
|
||||
tree.insert(cell)
|
||||
print(f"Height: {tree.height()}")
|
||||
|
||||
@@ -1,24 +1,27 @@
|
||||
from utils.literal import Literal
|
||||
from utils.memory_cell import MemoryCell
|
||||
from utils.memory_array import MemoryArray
|
||||
from utils.algo_context import AlgoContext
|
||||
from utils.algo_array import Array
|
||||
|
||||
class BTreeNode(MemoryCell):
|
||||
|
||||
def __init__(self, m: int):
|
||||
super().__init__()
|
||||
class BTreeNode:
|
||||
"""
|
||||
Knoten eines B-Baums.
|
||||
|
||||
value – Array der Schlüssel (Kapazität: 2m-1)
|
||||
n – Anzahl aktuell gespeicherter Schlüssel
|
||||
leaf – True wenn Blattknoten
|
||||
loaded_count / saved_count – Disk-I/O-Zähler für externe Komplexitätsanalyse
|
||||
"""
|
||||
|
||||
def __init__(self, m: int, ctx: AlgoContext):
|
||||
self.m = m
|
||||
self.ctx = ctx
|
||||
self.n = 0
|
||||
self.leaf = True
|
||||
self.value = MemoryArray(Literal(2 * m - 1))
|
||||
self.value = Array([0] * (2 * m - 1), ctx)
|
||||
self.children = [None] * (2 * m)
|
||||
self.loaded_count = 0
|
||||
self.saved_count = 0
|
||||
|
||||
def reset_counters(self):
|
||||
super().reset_counters()
|
||||
self.loaded_count = 0
|
||||
self.saved_count = 0
|
||||
|
||||
def load(self):
|
||||
self.loaded_count += 1
|
||||
|
||||
@@ -26,4 +29,4 @@ class BTreeNode(MemoryCell):
|
||||
self.saved_count += 1
|
||||
|
||||
def __str__(self):
|
||||
return "(" + " ".join([str(self.value[Literal(i)]) for i in range(self.n)]) + ")"
|
||||
return "(" + " ".join([str(self.value[i]) for i in range(self.n)]) + ")"
|
||||
|
||||
@@ -1,58 +1,53 @@
|
||||
import math
|
||||
import random
|
||||
from utils.literal import Literal
|
||||
from utils.memory_cell import MemoryCell
|
||||
from utils.memory_array import MemoryArray
|
||||
from utils.memory_manager import MemoryManager
|
||||
from utils.algo_context import AlgoContext
|
||||
from utils.algo_int import Int
|
||||
from utils.algo_array import Array
|
||||
from vorlesung.L07_hashtable.hashtable import HashTableOpenAddressing
|
||||
|
||||
#Goldener Schnitt
|
||||
a = Literal((math.sqrt(5) - 1) / 2)
|
||||
# Goldener Schnitt (Konstante, nicht instrumentiert)
|
||||
_A = (math.sqrt(5) - 1) / 2
|
||||
|
||||
# Hashfunktion nach multiplikativer Methode
|
||||
def h(x: MemoryCell, m: Literal) -> Literal:
|
||||
with MemoryCell(int(x * a)) as integer_part, MemoryCell(x * a) as full_product:
|
||||
with MemoryCell(full_product - integer_part) as fractional_part:
|
||||
return Literal(abs(int(fractional_part * m)))
|
||||
|
||||
# Quadratische Sondierung
|
||||
def f(x: MemoryCell, i: Literal, m: Literal) -> Literal:
|
||||
c1 = 1
|
||||
c2 = 5
|
||||
with MemoryCell(h(x, m)) as initial_hash, MemoryCell(c2 * int(i) * int(i)) as quadratic_offset:
|
||||
with MemoryCell(initial_hash + quadratic_offset) as probe_position:
|
||||
probe_position += Literal(c1 * int(i)) # Linear component
|
||||
return probe_position % m
|
||||
def h(x: Int, m: Int) -> Int:
|
||||
"""Hashfunktion nach multiplikativer Methode."""
|
||||
full = x.value * _A
|
||||
return Int(int(abs(full - int(full)) * int(m)), x._ctx)
|
||||
|
||||
# Symmetrische quadratische Sondierung
|
||||
def fs(x: MemoryCell, i: Literal, m: Literal) -> Literal:
|
||||
with MemoryCell(h(x, m)) as base_hash, MemoryCell(int(i) * int(i)) as square:
|
||||
if int(i) % 2 == 0: # gerades i: Vorwärtssondierung
|
||||
with MemoryCell(base_hash + square) as position:
|
||||
return position % m
|
||||
else: # ungerades i: Rückwärtssondierung
|
||||
with MemoryCell(base_hash - square) as position:
|
||||
return position % m
|
||||
|
||||
def f(x: Int, i: Int, m: Int) -> Int:
|
||||
"""Quadratische Sondierung."""
|
||||
c1, c2 = 1, 5
|
||||
base = int(h(x, m))
|
||||
probe = base + c1 * int(i) + c2 * int(i) ** 2
|
||||
return Int(probe % int(m), x._ctx)
|
||||
|
||||
|
||||
def fs(x: Int, i: Int, m: Int) -> Int:
|
||||
"""Symmetrische quadratische Sondierung."""
|
||||
base = int(h(x, m))
|
||||
sq = int(i) ** 2
|
||||
if int(i) % 2 == 0:
|
||||
probe = base + sq
|
||||
else:
|
||||
probe = base - sq
|
||||
return Int(probe % int(m), x._ctx)
|
||||
|
||||
|
||||
def analyze_complexity(sizes):
|
||||
"""
|
||||
Analysiert die Komplexität
|
||||
|
||||
:param sizes: Eine Liste von Eingabegrößen für die Analyse.
|
||||
"""
|
||||
ctx = AlgoContext()
|
||||
for size in sizes:
|
||||
MemoryManager.purge() # Speicher zurücksetzen
|
||||
ht = HashTableOpenAddressing(size, f)
|
||||
random_array = MemoryArray.create_random_array(size, -100, 100)
|
||||
for cell in random_array:
|
||||
ctx.reset()
|
||||
ht = HashTableOpenAddressing(size, f, ctx)
|
||||
z = Array.random(size, -100, 100, ctx)
|
||||
for cell in z:
|
||||
ht.insert(cell)
|
||||
MemoryManager.reset()
|
||||
cell = random.choice(random_array.cells)
|
||||
ht.search(cell)
|
||||
MemoryManager.save_stats(size)
|
||||
ctx.reset()
|
||||
target = z[random.randint(0, size - 1)]
|
||||
ht.search(target)
|
||||
ctx.save_stats(size)
|
||||
|
||||
MemoryManager.plot_stats(["cells", "compares"])
|
||||
ctx.plot_stats(["comparisons"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -1,76 +1,75 @@
|
||||
from collections.abc import Callable
|
||||
from utils.literal import Literal
|
||||
from utils.memory_array import MemoryArray
|
||||
from utils.memory_cell import MemoryCell
|
||||
from utils.memory_range import mrange
|
||||
from utils.algo_context import AlgoContext
|
||||
from utils.algo_array import Array
|
||||
from utils.algo_int import Int
|
||||
from utils.algo_range import irange
|
||||
|
||||
|
||||
UNUSED_MARK = "UNUSED"
|
||||
UNUSED_MARK = "UNUSED"
|
||||
DELETED_MARK = "DELETED"
|
||||
|
||||
|
||||
class HashTableOpenAddressing:
|
||||
def __init__(self, m: Literal, f: Callable[[MemoryCell, Literal, Literal], Literal]):
|
||||
if not isinstance(m, Literal):
|
||||
m = Literal(m)
|
||||
self.m = m
|
||||
"""
|
||||
Hashtabelle mit offener Adressierung.
|
||||
|
||||
f – Sondierungsfunktion f(x: Int, i: Int, m: Int) -> Int
|
||||
Liefert die Tabellenposition für Schlüssel x beim i-ten Versuch.
|
||||
"""
|
||||
|
||||
def __init__(self, m: int, f: Callable[[Int, Int, Int], Int], ctx: AlgoContext):
|
||||
self.ctx = ctx
|
||||
self.m = Int(m, ctx)
|
||||
self.f = f
|
||||
self.table = MemoryArray(m)
|
||||
for i in mrange(m):
|
||||
self.table[i].value = UNUSED_MARK
|
||||
self.table = Array([UNUSED_MARK] * m, ctx)
|
||||
|
||||
def insert(self, x: MemoryCell):
|
||||
with MemoryCell(0) as i:
|
||||
while i < self.m:
|
||||
j = self.f(x, i, self.m)
|
||||
if self.is_free(j):
|
||||
self.table[j].set(x)
|
||||
return True
|
||||
i.set(i.succ())
|
||||
def insert(self, x: Int) -> bool:
|
||||
i = Int(0, self.ctx)
|
||||
while i < self.m:
|
||||
j = self.f(x, i, self.m)
|
||||
if self.is_free(j):
|
||||
self.table[j] = x
|
||||
return True
|
||||
i += 1
|
||||
return False
|
||||
|
||||
def search(self, x: MemoryCell):
|
||||
with MemoryCell(0) as i:
|
||||
while i < self.m:
|
||||
j = self.f(x, i, self.m)
|
||||
if self.is_unused(j):
|
||||
return False
|
||||
if self.table[j] == x:
|
||||
return True
|
||||
i.set(i.succ())
|
||||
def search(self, x: Int) -> bool:
|
||||
i = Int(0, self.ctx)
|
||||
while i < self.m:
|
||||
j = self.f(x, i, self.m)
|
||||
if self.is_unused(j):
|
||||
return False
|
||||
if self.table[j] == x:
|
||||
return True
|
||||
i += 1
|
||||
return False
|
||||
|
||||
def delete(self, x: MemoryCell):
|
||||
with MemoryCell(0) as i:
|
||||
while i < self.m:
|
||||
j = self.f(x, i, self.m)
|
||||
if self.is_unused(j):
|
||||
return False
|
||||
if self.table[j] == x:
|
||||
self.table[j].value = DELETED_MARK
|
||||
return True
|
||||
i.set(i.succ())
|
||||
def delete(self, x: Int) -> bool:
|
||||
i = Int(0, self.ctx)
|
||||
while i < self.m:
|
||||
j = self.f(x, i, self.m)
|
||||
if self.is_unused(j):
|
||||
return False
|
||||
if self.table[j] == x:
|
||||
self.table[j].set(DELETED_MARK) # Tombstone setzen (1 write)
|
||||
return True
|
||||
i += 1
|
||||
return False
|
||||
|
||||
def __str__(self):
|
||||
return str(self.table)
|
||||
|
||||
def alpha(self):
|
||||
with MemoryCell(0) as i:
|
||||
used = 0
|
||||
while i < self.m:
|
||||
used += 0 if self.is_free(i) else 1
|
||||
i.set(i.succ())
|
||||
def alpha(self) -> float:
|
||||
"""Belegungsfaktor der Tabelle."""
|
||||
used = sum(0 if self.is_free(Int(i, self.ctx)) else 1
|
||||
for i in range(int(self.m)))
|
||||
return used / int(self.m)
|
||||
|
||||
def is_unused(self, i: Literal):
|
||||
if self.table[i].value == UNUSED_MARK:
|
||||
return True
|
||||
return False
|
||||
def is_unused(self, i: Int) -> bool:
|
||||
return self.table[i].value == UNUSED_MARK
|
||||
|
||||
def is_deleted(self, i: Literal):
|
||||
if self.table[i].value == DELETED_MARK:
|
||||
return True
|
||||
return False
|
||||
def is_deleted(self, i: Int) -> bool:
|
||||
return self.table[i].value == DELETED_MARK
|
||||
|
||||
def is_free(self, i: Literal):
|
||||
def is_free(self, i: Int) -> bool:
|
||||
return self.is_unused(i) or self.is_deleted(i)
|
||||
|
||||
Reference in New Issue
Block a user