Compare commits
19
Commits
1b50c6e5d9
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
242b7fdc34 | ||
|
|
00466b6ad7 | ||
|
|
4bad896096 | ||
|
|
82a6d35942 | ||
|
|
50cf0b9b52 | ||
|
|
e0cbf69759 | ||
|
|
2c98bb546d | ||
|
|
3a792644cf | ||
|
|
5bb8b62fa9 | ||
|
|
8ab7c0a835 | ||
|
|
2790f31141 | ||
|
|
5ec825647c | ||
|
|
93c4fb91e5 | ||
|
|
5f39143bdf | ||
|
|
8bb627a500 | ||
|
|
2bf815757f | ||
|
|
74f4161fe7 | ||
|
|
e8feb3ca60 | ||
|
|
fe69f32484 |
No files matched your search
@@ -250,7 +250,9 @@ class AlgoDatArray:
|
|||||||
|
|
||||||
def __init__(self, size):
|
def __init__(self, size):
|
||||||
self.size = size
|
self.size = size
|
||||||
self.array = [AlgoDatValue(None)] * size
|
self.array = []
|
||||||
|
for i in range(size):
|
||||||
|
self.array.append(AlgoDatValue(None))
|
||||||
|
|
||||||
def set(self, index, value):
|
def set(self, index, value):
|
||||||
assert isinstance(value, AlgoDatValue)
|
assert isinstance(value, AlgoDatValue)
|
||||||
@@ -324,5 +326,21 @@ def read_int_sequence(filename: str) -> AlgoDatArray:
|
|||||||
l = list(map(int, file.read().split()))
|
l = list(map(int, file.read().split()))
|
||||||
a = AlgoDatArray(len(l))
|
a = AlgoDatArray(len(l))
|
||||||
for i in range(len(l)):
|
for i in range(len(l)):
|
||||||
a.set(i, AlgoDatValue(l[i]))
|
a[i].value = l[i]
|
||||||
|
return a
|
||||||
|
|
||||||
|
def write_int_sequence(z: AlgoDatArray, filename: str):
|
||||||
|
with open(filename, "w") as file:
|
||||||
|
for i in range(len(z)):
|
||||||
|
file.write(str(z[i].value)+ '\n')
|
||||||
|
|
||||||
|
|
||||||
|
def read_int_sequence_limited(filename: str, limit: int) -> AlgoDatArray:
|
||||||
|
"""Reads a sequence of integers from a file and returns an AlgoDatArray object."""
|
||||||
|
with open(filename, "r") as file:
|
||||||
|
l = list(map(int, file.read().split()))
|
||||||
|
size = min(len(l), limit)
|
||||||
|
a = AlgoDatArray(size)
|
||||||
|
for i in range(size):
|
||||||
|
a[i].value = l[i]
|
||||||
return a
|
return a
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
from SoSe24.algodat.foundation import AlgoDatArray
|
||||||
|
|
||||||
|
|
||||||
|
class AlgoDatHeap():
|
||||||
|
|
||||||
|
def __init__(self, values: AlgoDatArray):
|
||||||
|
self.heap = values
|
||||||
|
self.size = len(values)
|
||||||
|
|
||||||
|
def left_child(self, i):
|
||||||
|
return 2*i + 1
|
||||||
|
|
||||||
|
def right_child(self, i):
|
||||||
|
return 2*i + 2
|
||||||
|
|
||||||
|
def parent(self, i):
|
||||||
|
return (i-1)//2
|
||||||
|
|
||||||
|
def max_heapify(self, i):
|
||||||
|
l = self.left_child(i)
|
||||||
|
r = self.right_child(i)
|
||||||
|
largest = i
|
||||||
|
if l <= self.size and self.heap[l-1] > self.heap[i-1]:
|
||||||
|
largest = l
|
||||||
|
if r <= self.size and self.heap[r-1] > self.heap[largest-1]:
|
||||||
|
largest = r
|
||||||
|
if largest != i:
|
||||||
|
self.heap[i-1], self.heap[largest-1] = self.heap[largest-1], self.heap[i-1]
|
||||||
|
self.max_heapify(largest)
|
||||||
|
|
||||||
|
def min_heapify(self, i):
|
||||||
|
l = self.left_child(i)
|
||||||
|
r = self.right_child(i)
|
||||||
|
smallest = i
|
||||||
|
if l <= self.size and self.heap[l-1] < self.heap[i-1]:
|
||||||
|
smallest = l
|
||||||
|
if r <= self.size and self.heap[r-1] < self.heap[smallest-1]:
|
||||||
|
smallest = r
|
||||||
|
if smallest != i:
|
||||||
|
self.heap[i-1], self.heap[smallest-1] = self.heap[smallest-1], self.heap[i-1]
|
||||||
|
self.min_heapify(smallest)
|
||||||
|
|
||||||
|
def build_max_heap(self):
|
||||||
|
for i in range(self.size//2, 0, -1):
|
||||||
|
self.max_heapify(i)
|
||||||
|
|
||||||
|
def build_min_heap(self):
|
||||||
|
for i in range(self.size//2, 0, -1):
|
||||||
|
self.min_heapify(i)
|
||||||
|
|
||||||
|
def as_list(self):
|
||||||
|
return [self.heap[i].value for i in range(self.size)]
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
from SoSe24.algodat.foundation import AlgoDatArray, AlgoDatValue, read_int_sequence, read_int_sequence_limited
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import merge_sort as ms
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
filename = "../../seq3.txt"
|
||||||
|
dummy = read_int_sequence("../../seq3.txt")
|
||||||
|
n = len(dummy)
|
||||||
|
step = n // 100
|
||||||
|
|
||||||
|
memory_values = []
|
||||||
|
compare_values = []
|
||||||
|
|
||||||
|
for right_end in range(1, n, step):
|
||||||
|
AlgoDatValue.reset()
|
||||||
|
z = read_int_sequence_limited(filename, right_end)
|
||||||
|
storage = AlgoDatArray(z.size)
|
||||||
|
ms.merge_sort(z, storage, 0, right_end-1)
|
||||||
|
memory_values.append(AlgoDatValue.memory)
|
||||||
|
compare_values.append(AlgoDatValue.compare)
|
||||||
|
|
||||||
|
plt.plot(range(1, n, step), memory_values, 'b', label='Memory')
|
||||||
|
plt.plot(range(1, n, step), compare_values, 'r', label='Compare')
|
||||||
|
plt.legend()
|
||||||
|
plt.show()
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
from SoSe24.algodat.foundation import AlgoDatArray, AlgoDatValue, read_int_sequence
|
||||||
|
from time import perf_counter as pfc
|
||||||
|
|
||||||
|
|
||||||
|
def merge_sort(z: AlgoDatArray, buffer: AlgoDatArray, left: int, right: int):
|
||||||
|
if left == right:
|
||||||
|
return
|
||||||
|
mid = (left + right) // 2
|
||||||
|
merge_sort(z, buffer, left, mid)
|
||||||
|
merge_sort(z, buffer, mid + 1, right)
|
||||||
|
left_ptr = left
|
||||||
|
right_ptr = mid+1
|
||||||
|
storage_ptr = left
|
||||||
|
while left_ptr <= mid or right_ptr <= right:
|
||||||
|
if left_ptr <= mid and right_ptr <= right:
|
||||||
|
if z[left_ptr] < z[right_ptr]:
|
||||||
|
buffer[storage_ptr].value = z[left_ptr].value
|
||||||
|
left_ptr += 1
|
||||||
|
storage_ptr += 1
|
||||||
|
else:
|
||||||
|
buffer[storage_ptr].value = z[right_ptr].value
|
||||||
|
right_ptr += 1
|
||||||
|
storage_ptr += 1
|
||||||
|
elif left_ptr <= mid:
|
||||||
|
buffer[storage_ptr].value = z[left_ptr].value
|
||||||
|
left_ptr += 1
|
||||||
|
storage_ptr += 1
|
||||||
|
else:
|
||||||
|
buffer[storage_ptr].value = z[right_ptr].value
|
||||||
|
right_ptr += 1
|
||||||
|
storage_ptr += 1
|
||||||
|
for i in range(left, right):
|
||||||
|
z[i].value = buffer[i].value
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
z = read_int_sequence("../../seq0.txt")
|
||||||
|
print(z, len(z))
|
||||||
|
start = pfc()
|
||||||
|
storage = AlgoDatArray(z.size) # buffer
|
||||||
|
AlgoDatValue.summary()
|
||||||
|
merge_sort(z, storage, 0, z.size-1)
|
||||||
|
print(z)
|
||||||
|
print(f"Dauer: {pfc() - start:.4f}s")
|
||||||
|
AlgoDatValue.summary()
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
from SoSe24.algodat.foundation import AlgoDatArray, AlgoDatValue, read_int_sequence, read_int_sequence_limited
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import quick_sort as qs
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
filename = "../../seq3.txt"
|
||||||
|
#filename = "../../seq3_sorted.txt"
|
||||||
|
dummy = read_int_sequence(filename)
|
||||||
|
n = len(dummy)
|
||||||
|
step = n // 100
|
||||||
|
|
||||||
|
memory_values = []
|
||||||
|
compare_values = []
|
||||||
|
|
||||||
|
for right_end in range(1, n, step):
|
||||||
|
AlgoDatValue.reset()
|
||||||
|
z = read_int_sequence_limited(filename, right_end)
|
||||||
|
qs.quick_sort(z, 0, right_end-1)
|
||||||
|
memory_values.append(AlgoDatValue.memory)
|
||||||
|
compare_values.append(AlgoDatValue.compare)
|
||||||
|
|
||||||
|
plt.plot(range(1, n, step), memory_values, 'b', label='Memory')
|
||||||
|
plt.plot(range(1, n, step), compare_values, 'r', label='Compare')
|
||||||
|
plt.legend()
|
||||||
|
plt.show()
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
from SoSe24.algodat.foundation import AlgoDatArray, AlgoDatValue, read_int_sequence
|
||||||
|
from time import perf_counter as pfc
|
||||||
|
|
||||||
|
|
||||||
|
def partition(z: AlgoDatArray, left: int, right: int) -> int:
|
||||||
|
global pivot
|
||||||
|
pivot.value = z[right]
|
||||||
|
i = left
|
||||||
|
j = right - 1
|
||||||
|
while i < j:
|
||||||
|
while z[i] < pivot:
|
||||||
|
i += 1
|
||||||
|
while z[j] > pivot:
|
||||||
|
j -= 1
|
||||||
|
if i < j:
|
||||||
|
z[i], z[j] = z[j], z[i]
|
||||||
|
i += 1
|
||||||
|
j -= 1
|
||||||
|
if i==j and z[i] < pivot:
|
||||||
|
i += 1
|
||||||
|
if z[i] != pivot:
|
||||||
|
z[i], z[right] = z[right], z[i]
|
||||||
|
return i
|
||||||
|
|
||||||
|
|
||||||
|
def median_pivot(z: AlgoDatArray, left: int, right: int) -> int:
|
||||||
|
mid = left + (right - left) // 2
|
||||||
|
if z[left] < z[right]:
|
||||||
|
if z[mid] < z[right]:
|
||||||
|
# right is the largest
|
||||||
|
if z[mid] < z[left]:
|
||||||
|
# left is in the middle
|
||||||
|
z[left], z[right] = z[right], z[left]
|
||||||
|
else:
|
||||||
|
# mid is in the middle
|
||||||
|
z[mid], z[right] = z[right], z[mid]
|
||||||
|
else:
|
||||||
|
# right is in the middle
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
if z[mid] < z[right]:
|
||||||
|
# right is in the middle
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
#right is the smallest
|
||||||
|
if z[mid] < z[left]:
|
||||||
|
# mid is in the middle
|
||||||
|
z[mid], z[right] = z[right], z[mid]
|
||||||
|
else:
|
||||||
|
# mid is in the middle
|
||||||
|
z[left], z[right] = z[right], z[left]
|
||||||
|
return partition(z, left, right)
|
||||||
|
|
||||||
|
|
||||||
|
def quick_sort(z: AlgoDatArray, left: int, right: int):
|
||||||
|
if left < right:
|
||||||
|
#q = partition(z, left, right)
|
||||||
|
q = median_pivot(z, left, right)
|
||||||
|
quick_sort(z, left, q-1)
|
||||||
|
quick_sort(z, q+1, right)
|
||||||
|
|
||||||
|
|
||||||
|
pivot = AlgoDatValue(0)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
z = read_int_sequence("../../seq3.txt")
|
||||||
|
print(z, len(z))
|
||||||
|
start = pfc()
|
||||||
|
quick_sort(z, 0, z.size-1)
|
||||||
|
print(z)
|
||||||
|
print(f"Dauer: {pfc() - start:.4f}s")
|
||||||
|
AlgoDatValue.summary()
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
from SoSe24.algodat.foundation import AlgoDatArray, AlgoDatValue, read_int_sequence
|
||||||
|
from SoSe24.lec04_trees.bin_tree import BinTree, BinTreeNode
|
||||||
|
from time import perf_counter as pfc
|
||||||
|
|
||||||
|
class AVLTreeNode(BinTreeNode):
|
||||||
|
def __init__(self, value: AlgoDatValue):
|
||||||
|
super().__init__(value)
|
||||||
|
self.parent = None
|
||||||
|
self.balance = 0
|
||||||
|
|
||||||
|
def update_balance(self):
|
||||||
|
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
|
||||||
|
|
||||||
|
def right_rotate(self) -> BinTreeNode:
|
||||||
|
new_root = self.left
|
||||||
|
new_root.parent = self.parent
|
||||||
|
self.left = new_root.right
|
||||||
|
if self.left:
|
||||||
|
self.left.parent = self
|
||||||
|
new_root.right = self
|
||||||
|
self.parent = new_root
|
||||||
|
if new_root.parent:
|
||||||
|
if new_root.parent.left == self:
|
||||||
|
new_root.parent.left = new_root
|
||||||
|
else:
|
||||||
|
new_root.parent.right = new_root
|
||||||
|
self.update_balance()
|
||||||
|
new_root.update_balance()
|
||||||
|
return new_root
|
||||||
|
|
||||||
|
def left_rotate(self) -> BinTreeNode:
|
||||||
|
new_root = self.right
|
||||||
|
new_root.parent = self.parent
|
||||||
|
self.right = new_root.left
|
||||||
|
if self.right:
|
||||||
|
self.right.parent = self
|
||||||
|
new_root.left = self
|
||||||
|
self.parent = new_root
|
||||||
|
if new_root.parent:
|
||||||
|
if new_root.parent.left == self:
|
||||||
|
new_root.parent.left = new_root
|
||||||
|
else:
|
||||||
|
new_root.parent.right = new_root
|
||||||
|
self.update_balance()
|
||||||
|
new_root.update_balance()
|
||||||
|
return new_root
|
||||||
|
|
||||||
|
def right_left_rotate(self) -> BinTreeNode:
|
||||||
|
self.right = self.right.right_rotate()
|
||||||
|
return self.left_rotate()
|
||||||
|
|
||||||
|
def left_right_rotate(self) -> BinTreeNode:
|
||||||
|
self.left = self.left.left_rotate()
|
||||||
|
return self.right_rotate()
|
||||||
|
|
||||||
|
class AVLTree(BinTree):
|
||||||
|
|
||||||
|
def new_node(self, value: AlgoDatValue):
|
||||||
|
return AVLTreeNode(value)
|
||||||
|
|
||||||
|
def balance(self, node: AVLTreeNode):
|
||||||
|
node.update_balance()
|
||||||
|
if node.balance == -2:
|
||||||
|
if node.left.balance <= 0:
|
||||||
|
node = node.right_rotate()
|
||||||
|
else:
|
||||||
|
node = node.left_right_rotate()
|
||||||
|
elif node.balance == 2:
|
||||||
|
if node.right.balance >= 0:
|
||||||
|
node = node.left_rotate()
|
||||||
|
else:
|
||||||
|
node = node.right_left_rotate()
|
||||||
|
if node.parent:
|
||||||
|
self.balance(node.parent)
|
||||||
|
else:
|
||||||
|
self.root = node
|
||||||
|
|
||||||
|
def insert(self, value: AlgoDatValue):
|
||||||
|
node, parent = super().insert(value)
|
||||||
|
node.parent = parent
|
||||||
|
if parent:
|
||||||
|
self.balance(parent)
|
||||||
|
return node, parent
|
||||||
|
|
||||||
|
def delete(self, value: AlgoDatValue):
|
||||||
|
node, parent = super().delete(value)
|
||||||
|
if node:
|
||||||
|
node.parent = parent
|
||||||
|
if parent:
|
||||||
|
self.balance(parent)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
z = read_int_sequence("../../seq0.txt")
|
||||||
|
print(z, len(z))
|
||||||
|
start = pfc()
|
||||||
|
tree = AVLTree()
|
||||||
|
for i in z:
|
||||||
|
tree.insert(i)
|
||||||
|
tree.walk()
|
||||||
|
tree.tree_walk()
|
||||||
|
tree.levelwalk()
|
||||||
|
tree.graph_walk()
|
||||||
|
tree.delete(AlgoDatValue(46))
|
||||||
|
tree.delete(AlgoDatValue(48))
|
||||||
|
#tree.graph_walk()
|
||||||
|
print(f"Dauer: {pfc() - start:.4f}s")
|
||||||
|
AlgoDatValue.summary()
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
from SoSe24.algodat.foundation import AlgoDatArray, AlgoDatValue, read_int_sequence, read_int_sequence_limited
|
||||||
|
import matplotlib
|
||||||
|
matplotlib.use('TkAgg')
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import avl_tree as avl
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
filename = "../../seq3_sorted.txt"
|
||||||
|
#filename = "../../seq3.txt"
|
||||||
|
dummy = read_int_sequence(filename)
|
||||||
|
n = len(dummy)
|
||||||
|
step = n // 100
|
||||||
|
|
||||||
|
memory_values = []
|
||||||
|
compare_values = []
|
||||||
|
|
||||||
|
for right_end in range(1, n, step):
|
||||||
|
AlgoDatValue.reset()
|
||||||
|
z = read_int_sequence_limited(filename, right_end)
|
||||||
|
tree = avl.AVLTree()
|
||||||
|
for i in z:
|
||||||
|
tree.insert(i)
|
||||||
|
memory_values.append(AlgoDatValue.memory)
|
||||||
|
compare_values.append(AlgoDatValue.compare)
|
||||||
|
|
||||||
|
plt.plot(range(1, n, step), memory_values, 'b', label='Memory')
|
||||||
|
plt.plot(range(1, n, step), compare_values, 'r', label='Compare')
|
||||||
|
plt.legend()
|
||||||
|
plt.show()
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
from SoSe24.algodat.foundation import AlgoDatArray, AlgoDatValue, read_int_sequence
|
||||||
|
from time import perf_counter as pfc
|
||||||
|
|
||||||
|
|
||||||
|
class BTreeNode:
|
||||||
|
def __init__(self, m: int):
|
||||||
|
self.n = 0
|
||||||
|
self.leaf = True
|
||||||
|
self.keys = AlgoDatArray(2 * m - 1)
|
||||||
|
self.children = [None] * (2 * m)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "(" + " ".join([str(self.keys[i]) for i in range(self.n)]) + ")"
|
||||||
|
|
||||||
|
class BTree:
|
||||||
|
def __init__(self, m: int):
|
||||||
|
self.m = m
|
||||||
|
self.root = BTreeNode(m)
|
||||||
|
|
||||||
|
def search(self, key: AlgoDatValue, start: BTreeNode = None) -> BTreeNode:
|
||||||
|
if not start:
|
||||||
|
start = self.root
|
||||||
|
i = 0
|
||||||
|
while i < start.n and key > start.keys[i]:
|
||||||
|
i += 1
|
||||||
|
if i < start.n and key == start.keys[i]:
|
||||||
|
return start
|
||||||
|
if start.leaf:
|
||||||
|
return None
|
||||||
|
return self.search(key, start.children[i])
|
||||||
|
|
||||||
|
def split_child(self, parent: BTreeNode, i: int):
|
||||||
|
child = parent.children[i]
|
||||||
|
h = BTreeNode(self.m)
|
||||||
|
h.leaf = child.leaf
|
||||||
|
h.n = self.m - 1
|
||||||
|
for j in range(self.m - 1):
|
||||||
|
h.keys[j] = child.keys[j + self.m]
|
||||||
|
if not h.leaf:
|
||||||
|
for j in range(self.m):
|
||||||
|
h.children[j] = child.children[j + self.m]
|
||||||
|
for j in range(self.m, child.n + 1):
|
||||||
|
child.children[j] = None
|
||||||
|
child.n = self.m - 1
|
||||||
|
for j in range(parent.n, i, -1):
|
||||||
|
parent.children[j + 1] = parent.children[j]
|
||||||
|
parent.keys[j] = parent.keys[j - 1]
|
||||||
|
parent.children[i + 1] = h
|
||||||
|
parent.keys[i] = child.keys[self.m - 1]
|
||||||
|
parent.n += 1
|
||||||
|
|
||||||
|
def insert(self, k: AlgoDatValue):
|
||||||
|
r = self.root
|
||||||
|
if r.n == 2 * self.m - 1:
|
||||||
|
h = BTreeNode(self.m)
|
||||||
|
self.root = h
|
||||||
|
h.leaf = False
|
||||||
|
h.n = 0
|
||||||
|
h.children[0] = r
|
||||||
|
self.split_child(h, 0)
|
||||||
|
self.insert_in_node(h, k)
|
||||||
|
else:
|
||||||
|
self.insert_in_node(r, k)
|
||||||
|
|
||||||
|
def insert_in_node(self, start: BTreeNode, k: AlgoDatValue):
|
||||||
|
i = start.n
|
||||||
|
if start.leaf:
|
||||||
|
while i >= 1 and k < start.keys[i-1]:
|
||||||
|
start.keys[i] = start.keys[i-1]
|
||||||
|
i -= 1
|
||||||
|
start.keys[i] = k
|
||||||
|
start.n += 1
|
||||||
|
else:
|
||||||
|
j = 0
|
||||||
|
while j < start.n and k > start.keys[j]:
|
||||||
|
j += 1
|
||||||
|
if start.children[j].n == 2 * self.m - 1:
|
||||||
|
self.split_child(start, j)
|
||||||
|
if k > start.keys[j]:
|
||||||
|
j += 1
|
||||||
|
self.insert_in_node(start.children[j], k)
|
||||||
|
|
||||||
|
def walk(self, start: BTreeNode = None):
|
||||||
|
if not start:
|
||||||
|
start = self.root
|
||||||
|
i = 0
|
||||||
|
while i < start.n:
|
||||||
|
if not start.leaf:
|
||||||
|
self.walk(start.children[i])
|
||||||
|
print(start.keys[i], end=" ")
|
||||||
|
i += 1
|
||||||
|
if not start.leaf:
|
||||||
|
self.walk(start.children[i])
|
||||||
|
|
||||||
|
def height(self, start: BTreeNode = None):
|
||||||
|
if not start:
|
||||||
|
start = self.root
|
||||||
|
if start.leaf:
|
||||||
|
return 0
|
||||||
|
return 1 + self.height(start.children[0])
|
||||||
|
|
||||||
|
def graph_walk(self):
|
||||||
|
queue = [ self.root ]
|
||||||
|
with open("../../btree.gv", "w") as file:
|
||||||
|
file.write("digraph BTree {\n")
|
||||||
|
file.write(" node [fontname=\"Arial\"];\n")
|
||||||
|
while queue:
|
||||||
|
current = queue.pop(0)
|
||||||
|
p = str(current)
|
||||||
|
file.write(f'"{p}"; \n')
|
||||||
|
i = 0
|
||||||
|
while i <= current.n:
|
||||||
|
if not current.leaf:
|
||||||
|
queue.append(current.children[i])
|
||||||
|
c = str(current.children[i])
|
||||||
|
file.write(f'"{p}" -> "{c}";\n')
|
||||||
|
i += 1
|
||||||
|
file.write("}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
z = read_int_sequence("../../seq2.txt")
|
||||||
|
start = pfc()
|
||||||
|
tree = BTree(3)
|
||||||
|
for i in z:
|
||||||
|
tree.insert(i)
|
||||||
|
print(f"Height: {tree.height()}")
|
||||||
|
tree.walk()
|
||||||
|
tree.graph_walk()
|
||||||
|
s = tree.search(AlgoDatValue(0))
|
||||||
|
print(f"\nKnoten mit 0: {str(s)}")
|
||||||
|
print(f"Dauer: {pfc() - start:.4f}s")
|
||||||
|
AlgoDatValue.summary()
|
||||||
@@ -0,0 +1,171 @@
|
|||||||
|
from SoSe24.algodat.foundation import AlgoDatArray, AlgoDatValue, read_int_sequence
|
||||||
|
from time import perf_counter as pfc
|
||||||
|
|
||||||
|
class BinTreeNode:
|
||||||
|
def __init__(self, value: AlgoDatValue):
|
||||||
|
self.value = value
|
||||||
|
self.left = None
|
||||||
|
self.right = None
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.value}"
|
||||||
|
|
||||||
|
def height(self) -> int:
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class BinTree:
|
||||||
|
def __init__(self):
|
||||||
|
self.root = None
|
||||||
|
|
||||||
|
def new_node(self, value: AlgoDatValue) -> BinTreeNode:
|
||||||
|
return BinTreeNode(value)
|
||||||
|
|
||||||
|
def insert(self, value: AlgoDatValue) -> (BinTreeNode, BinTreeNode):
|
||||||
|
if not self.root:
|
||||||
|
self.root = self.new_node(value)
|
||||||
|
return self.root, None
|
||||||
|
|
||||||
|
current = self.root
|
||||||
|
while True:
|
||||||
|
if value < current.value:
|
||||||
|
if current.left:
|
||||||
|
current = current.left
|
||||||
|
else:
|
||||||
|
current.left = self.new_node(value)
|
||||||
|
return current.left, current
|
||||||
|
elif value >= current.value:
|
||||||
|
if current.right:
|
||||||
|
current = current.right
|
||||||
|
else:
|
||||||
|
current.right = self.new_node(value)
|
||||||
|
return current.right, current
|
||||||
|
else:
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
def search(self, value: AlgoDatValue) -> BinTreeNode:
|
||||||
|
current = self.root
|
||||||
|
while current:
|
||||||
|
if value < current.value:
|
||||||
|
current = current.left
|
||||||
|
elif value > current.value:
|
||||||
|
current = current.right
|
||||||
|
else:
|
||||||
|
return current
|
||||||
|
return None
|
||||||
|
|
||||||
|
def delete(self, value: AlgoDatValue):
|
||||||
|
parent = None
|
||||||
|
current = self.root
|
||||||
|
while current:
|
||||||
|
if value < current.value:
|
||||||
|
parent = current
|
||||||
|
current = current.left
|
||||||
|
elif value >= current.value:
|
||||||
|
parent = current
|
||||||
|
current = current.right
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
if current.left and current.right:
|
||||||
|
parent = current
|
||||||
|
successor = current.right
|
||||||
|
while successor.left:
|
||||||
|
parent = successor
|
||||||
|
successor = successor.left
|
||||||
|
current.value.value = successor.value.value
|
||||||
|
current = successor
|
||||||
|
|
||||||
|
if current.left:
|
||||||
|
child = current.left
|
||||||
|
else:
|
||||||
|
child = current.right
|
||||||
|
|
||||||
|
if not parent:
|
||||||
|
self.root = child
|
||||||
|
return child, None
|
||||||
|
elif parent.left == current:
|
||||||
|
parent.left = child
|
||||||
|
return child, parent
|
||||||
|
else:
|
||||||
|
parent.right = child
|
||||||
|
return child, parent
|
||||||
|
|
||||||
|
def walk(self): # in-order
|
||||||
|
print("[ ", end="")
|
||||||
|
self.walk_recursive(self.root, 0, 0)
|
||||||
|
print(" ]")
|
||||||
|
|
||||||
|
|
||||||
|
def graph_walk(self):
|
||||||
|
self.leaf_counter = 0
|
||||||
|
with open("../../graph.gv", "w") as file:
|
||||||
|
file.write("digraph BST {\n")
|
||||||
|
file.write(" node [fontname=\"Arial\"];\n")
|
||||||
|
self.graph_walk_recursive(self.root, file)
|
||||||
|
file.write("}")
|
||||||
|
|
||||||
|
def graph_walk_recursive(self, current, file):
|
||||||
|
if current is not None:
|
||||||
|
if current.left:
|
||||||
|
file.write(f"{current.value} -> {current.left.value}; \n")
|
||||||
|
self.graph_walk_recursive(current.left, file)
|
||||||
|
else:
|
||||||
|
file.write(f"left{self.leaf_counter} [shape=point]; \n")
|
||||||
|
file.write(f"{current.value} -> left{self.leaf_counter}; \n")
|
||||||
|
self.leaf_counter += 1
|
||||||
|
if current.right:
|
||||||
|
file.write(f"{current.value} -> {current.right.value}; \n")
|
||||||
|
self.graph_walk_recursive(current.right, file)
|
||||||
|
else:
|
||||||
|
file.write(f"right{self.leaf_counter} [shape=point]; \n")
|
||||||
|
file.write(f"{current.value} -> right{self.leaf_counter}; \n")
|
||||||
|
self.leaf_counter += 1
|
||||||
|
|
||||||
|
|
||||||
|
def tree_walk(self):
|
||||||
|
self.walk_recursive(self.root, 0, 1)
|
||||||
|
|
||||||
|
def walk_recursive(self, node: BinTreeNode, level = 0, increase = 1):
|
||||||
|
if node:
|
||||||
|
if increase >= 1:
|
||||||
|
end = "\n"
|
||||||
|
else:
|
||||||
|
end = " "
|
||||||
|
self.walk_recursive(node.left, level+increase, increase)
|
||||||
|
print(" "*level*3 + str(node.value), end=end)
|
||||||
|
self.walk_recursive(node.right, level+increase, increase)
|
||||||
|
|
||||||
|
def levelwalk(self):
|
||||||
|
if self.root is None:
|
||||||
|
return
|
||||||
|
queue = [self.root]
|
||||||
|
while queue:
|
||||||
|
current = queue.pop(0)
|
||||||
|
print(current.value, end=" ")
|
||||||
|
if current.left:
|
||||||
|
queue.append(current.left)
|
||||||
|
if current.right:
|
||||||
|
queue.append(current.right)
|
||||||
|
print()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
z = read_int_sequence("../../seq0.txt")
|
||||||
|
print(z, len(z))
|
||||||
|
start = pfc()
|
||||||
|
tree = BinTree()
|
||||||
|
for i in z:
|
||||||
|
tree.insert(i)
|
||||||
|
tree.walk()
|
||||||
|
tree.tree_walk()
|
||||||
|
tree.levelwalk()
|
||||||
|
tree.delete(AlgoDatValue(46))
|
||||||
|
tree.graph_walk()
|
||||||
|
print(f"Dauer: {pfc() - start:.4f}s")
|
||||||
|
AlgoDatValue.summary()
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
from SoSe24.algodat.foundation import AlgoDatArray, AlgoDatValue, read_int_sequence, read_int_sequence_limited
|
||||||
|
import matplotlib
|
||||||
|
matplotlib.use('TkAgg')
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import bin_tree as bt
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
filename = "../../seq3_sorted.txt"
|
||||||
|
#filename = "../../seq3.txt"
|
||||||
|
dummy = read_int_sequence(filename)
|
||||||
|
n = len(dummy)
|
||||||
|
step = n // 100
|
||||||
|
|
||||||
|
memory_values = []
|
||||||
|
compare_values = []
|
||||||
|
|
||||||
|
for right_end in range(1, n, step):
|
||||||
|
AlgoDatValue.reset()
|
||||||
|
z = read_int_sequence_limited(filename, right_end)
|
||||||
|
tree = bt.BinTree()
|
||||||
|
for i in z:
|
||||||
|
tree.insert(i)
|
||||||
|
memory_values.append(AlgoDatValue.memory)
|
||||||
|
compare_values.append(AlgoDatValue.compare)
|
||||||
|
print(right_end, AlgoDatValue.compare)
|
||||||
|
|
||||||
|
plt.plot(range(1, n, step), memory_values, 'b', label='Memory')
|
||||||
|
plt.plot(range(1, n, step), compare_values, 'r', label='Compare')
|
||||||
|
plt.legend()
|
||||||
|
plt.show()
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
from SoSe24.algodat.foundation import AlgoDatArray, AlgoDatValue, read_int_sequence, read_int_sequence_limited
|
||||||
|
import matplotlib
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import hash_table as ht
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
filename = "../../seq3.txt"
|
||||||
|
dummy = read_int_sequence(filename)
|
||||||
|
n = len(dummy)
|
||||||
|
step = n // 100
|
||||||
|
|
||||||
|
memory_values = []
|
||||||
|
compare_values = []
|
||||||
|
alpha_values = []
|
||||||
|
|
||||||
|
for right_end in range(1, n, step):
|
||||||
|
AlgoDatValue.reset()
|
||||||
|
z = read_int_sequence_limited(filename, right_end)
|
||||||
|
hash = ht.HashTable(int(right_end*2), hash_function=ht.h, exploratory_function=ht.f1)
|
||||||
|
for i in z:
|
||||||
|
hash.insert(i)
|
||||||
|
memory_values.append(AlgoDatValue.memory)
|
||||||
|
compare_values.append(AlgoDatValue.compare)
|
||||||
|
alpha_values.append(hash.alpha())
|
||||||
|
|
||||||
|
plt.plot(range(1, n, step), memory_values, 'b', label='Memory')
|
||||||
|
plt.plot(range(1, n, step), compare_values, 'r', label='Compare')
|
||||||
|
plt.plot(range(1, n, step), alpha_values, 'g', label='Alpha')
|
||||||
|
|
||||||
|
plt.legend()
|
||||||
|
plt.show()
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
from SoSe24.algodat.foundation import AlgoDatArray, AlgoDatValue, read_int_sequence, MinusInf
|
||||||
|
from time import perf_counter as pfc
|
||||||
|
|
||||||
|
#Goldener Schnitt
|
||||||
|
import math
|
||||||
|
a = (math.sqrt(5) - 1) / 2
|
||||||
|
|
||||||
|
def h(x, m):
|
||||||
|
return int(x*a - int(x*a) * m)
|
||||||
|
|
||||||
|
def f(x, i, m):
|
||||||
|
return (h(x, m) + i + 14*i*i) % m
|
||||||
|
|
||||||
|
def f1(x, i, m):
|
||||||
|
if i % 2 == 0:
|
||||||
|
return (h(x, m) + i*i) % m
|
||||||
|
return ((h(x, m) - i*i) % m + m) % m
|
||||||
|
|
||||||
|
class HashTable:
|
||||||
|
def __init__(self, m, h, f=None):
|
||||||
|
self.m = m
|
||||||
|
self.h = h
|
||||||
|
self.f = f
|
||||||
|
self.table = AlgoDatArray(m)
|
||||||
|
|
||||||
|
def insert(self, x):
|
||||||
|
i = 0
|
||||||
|
while i < self.m:
|
||||||
|
j = self.f(x.value, i, self.m)
|
||||||
|
if self.is_free(j):
|
||||||
|
self.table[j].value = x.value
|
||||||
|
return True
|
||||||
|
i += 1
|
||||||
|
return False
|
||||||
|
|
||||||
|
def search(self, x):
|
||||||
|
i = 0
|
||||||
|
while i < self.m:
|
||||||
|
j = f(x, i, self.m)
|
||||||
|
if self.table[j] == x:
|
||||||
|
return True
|
||||||
|
if self.table[j] == None:
|
||||||
|
return False
|
||||||
|
i += 1
|
||||||
|
return False
|
||||||
|
|
||||||
|
def delete(self, x):
|
||||||
|
i = 0
|
||||||
|
while i < self.m:
|
||||||
|
j = f(x, i, self.m)
|
||||||
|
if self.table[j].value == x:
|
||||||
|
self.table[j].value = "DELETED"
|
||||||
|
return True
|
||||||
|
if self.table[j].value is None:
|
||||||
|
return False
|
||||||
|
i += 1
|
||||||
|
return False
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return str(self.table)
|
||||||
|
|
||||||
|
def alpha(self):
|
||||||
|
i=0
|
||||||
|
used = 0
|
||||||
|
while i < self.m:
|
||||||
|
used += 0 if self.is_free(i) else 1
|
||||||
|
i += 1
|
||||||
|
return used / self.m
|
||||||
|
|
||||||
|
def is_free(self, i):
|
||||||
|
if self.table[i] == None:
|
||||||
|
return True
|
||||||
|
if self.table[i] == "DELETED":
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
z = read_int_sequence("../../seq1.txt")
|
||||||
|
start = pfc()
|
||||||
|
hash = HashTable(31, h, f)
|
||||||
|
for i in z:
|
||||||
|
hash.insert(i)
|
||||||
|
print(hash)
|
||||||
|
print(f"Alpha: {hash.alpha()}")
|
||||||
|
hash.delete(34)
|
||||||
|
hash.search(47)
|
||||||
|
hash.search(243)
|
||||||
|
print(hash)
|
||||||
|
print(f"Alpha: {hash.alpha()}")
|
||||||
|
print(f"Dauer: {pfc() - start:.4f}s")
|
||||||
|
AlgoDatValue.summary()
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
import math
|
||||||
|
from typing import Callable
|
||||||
|
import re
|
||||||
|
from graph import Graph, AdjacencyListGraph, AdjacencyMatrixGraph, NodeColor, Vertex
|
||||||
|
import heapq
|
||||||
|
|
||||||
|
def a_star(self, start_name: str, end_name: str, heuristic: Callable[[Vertex], float]):
|
||||||
|
color_map = {} # maps vertices to their color
|
||||||
|
distance_map = {} # maps vertices to their distance from the start vertex
|
||||||
|
predecessor_map = {} # maps vertices to their predecessor in the traversal tree
|
||||||
|
|
||||||
|
def cost(vertex: Vertex) -> float:
|
||||||
|
"""Compute the cost of the path to the given vertex."""
|
||||||
|
if distance_map[vertex] is None:
|
||||||
|
return math.inf
|
||||||
|
return distance_map[vertex] + heuristic(vertex)
|
||||||
|
|
||||||
|
Vertex.__lt__ = lambda self, other: cost(self) < cost(other)
|
||||||
|
|
||||||
|
# Initialize the maps
|
||||||
|
for vertex in self.all_vertices():
|
||||||
|
color_map[vertex] = NodeColor.WHITE
|
||||||
|
distance_map[vertex] = None
|
||||||
|
predecessor_map[vertex] = None
|
||||||
|
|
||||||
|
# Start at the given vertex
|
||||||
|
start_node = self.get_vertex(start_name)
|
||||||
|
color_map[start_node] = NodeColor.GRAY
|
||||||
|
distance_map[start_node] = 0
|
||||||
|
|
||||||
|
# Initialize the queue with the start vertex
|
||||||
|
queue = [] # Use a list instead of a deque, because we need to sort it with
|
||||||
|
heapq.heappush(queue, start_node)
|
||||||
|
|
||||||
|
# Process the queue
|
||||||
|
while len(queue) > 0:
|
||||||
|
vertex = heapq.heappop(queue)
|
||||||
|
if color_map[vertex] == NodeColor.BLACK:
|
||||||
|
# Skip already processed vertices (possibly with a higher cost)
|
||||||
|
continue
|
||||||
|
if vertex.value == end_name:
|
||||||
|
# Return the distance and predecessor maps
|
||||||
|
return distance_map, predecessor_map
|
||||||
|
for dest, weight in self.get_adjacent_vertices_with_weight(vertex.value):
|
||||||
|
if color_map[dest] == NodeColor.BLACK:
|
||||||
|
continue
|
||||||
|
f = distance_map[vertex] + weight + heuristic(dest)
|
||||||
|
if color_map[dest] == NodeColor.GRAY and f > cost(dest):
|
||||||
|
continue
|
||||||
|
predecessor_map[dest] = vertex
|
||||||
|
distance_map[dest] = distance_map[vertex] + weight
|
||||||
|
heapq.heappush(queue, dest)
|
||||||
|
if color_map[dest] == NodeColor.WHITE:
|
||||||
|
color_map[dest] = NodeColor.GRAY
|
||||||
|
color_map[vertex] = NodeColor.BLACK
|
||||||
|
|
||||||
|
# Return the distance and predecessor maps if no path was found
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
|
||||||
|
# Add the a_star method to the Graph classes
|
||||||
|
AdjacencyListGraph.a_star = a_star
|
||||||
|
AdjacencyMatrixGraph.a_star = a_star
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
def read_labyrinth_into_graph(graph: Graph, filename: str):
|
||||||
|
"""Read a labyrinth from a file into a graph. The file format is a grid of characters:"""
|
||||||
|
start = None
|
||||||
|
end = None
|
||||||
|
with open(filename, "r") as file:
|
||||||
|
nodes = []
|
||||||
|
lines = file.readlines()
|
||||||
|
for y, line in enumerate(lines):
|
||||||
|
for x, char in enumerate(line):
|
||||||
|
if char in ' AS':
|
||||||
|
name = pos_to_nodename((x, y))
|
||||||
|
graph.insert_vertex(name)
|
||||||
|
nodes.append((x, y))
|
||||||
|
if char == 'A':
|
||||||
|
end = (x, y)
|
||||||
|
if char == 'S':
|
||||||
|
start = (x, y)
|
||||||
|
for x, y in nodes:
|
||||||
|
name1 = f"x{x}y{y}"
|
||||||
|
for neighbor in [(x - 1, y), (x, y - 1), (x, y + 1), (x + 1, y)]:
|
||||||
|
if neighbor in nodes:
|
||||||
|
name_neighbor = pos_to_nodename(neighbor)
|
||||||
|
graph.connect(name1, name_neighbor, 1)
|
||||||
|
return start, end, lines
|
||||||
|
|
||||||
|
|
||||||
|
def nodename_to_pos(nodename):
|
||||||
|
"""Convert a node name to a position (x, y)."""
|
||||||
|
m = re.match(r"(^x(\d*)y(\d*))", nodename)
|
||||||
|
if m:
|
||||||
|
return (int(m.group(2)), int(m.group(3)))
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def pos_to_nodename(pos):
|
||||||
|
"""Convert a position (x, y) to a node name."""
|
||||||
|
x, y = pos
|
||||||
|
return f"x{x}y{y}"
|
||||||
|
|
||||||
|
|
||||||
|
def get_heuristic(end) -> Callable[[Vertex], float]:
|
||||||
|
"""Return a heuristic function for the given end position."""
|
||||||
|
def heuristic(v: Vertex) -> float:
|
||||||
|
x, y = nodename_to_pos(v.value)
|
||||||
|
x1, y1 = end
|
||||||
|
# Euclidean distance
|
||||||
|
return math.sqrt(abs(x - x1)**2 + abs(y - y1)**2)
|
||||||
|
|
||||||
|
return heuristic
|
||||||
|
|
||||||
|
def update_lines(lines, distance_map, path):
|
||||||
|
"""Update the lines with the path found by the A* algorithm."""
|
||||||
|
def replace_at(x, y, replacement):
|
||||||
|
if lines[y][x] not in " .":
|
||||||
|
return
|
||||||
|
lines[y] = lines[y][:x] + replacement + lines[y][x+1:]
|
||||||
|
|
||||||
|
for node in distance_map.keys():
|
||||||
|
if distance_map[node] is not None:
|
||||||
|
x, y = nodename_to_pos(node.value)
|
||||||
|
replace_at(x, y, ".")
|
||||||
|
for node in path:
|
||||||
|
x, y = nodename_to_pos(node)
|
||||||
|
replace_at(x, y, "*")
|
||||||
|
return lines
|
||||||
|
|
||||||
|
graph = AdjacencyListGraph()
|
||||||
|
#graph = AdjacencyMatrixGraph()
|
||||||
|
start, end, lines = read_labyrinth_into_graph(graph, "../../labyrinth.txt")
|
||||||
|
distance_map, predecessor_map = graph.a_star(pos_to_nodename(start), pos_to_nodename(end), get_heuristic(end))
|
||||||
|
endname = pos_to_nodename(end)
|
||||||
|
lines = update_lines(lines, distance_map, graph.path(endname, predecessor_map))
|
||||||
|
for line in lines:
|
||||||
|
print(line, end="")
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
|
||||||
|
|
||||||
|
class DisjointValue():
|
||||||
|
|
||||||
|
def __init__(self, value):
|
||||||
|
self.value = value
|
||||||
|
self.parent = None
|
||||||
|
|
||||||
|
def canonical(self):
|
||||||
|
if self.parent:
|
||||||
|
return self.parent.canonical()
|
||||||
|
return self
|
||||||
|
|
||||||
|
def same_set(self, other):
|
||||||
|
return self.canonical() == other.canonical()
|
||||||
|
|
||||||
|
def union(self, other):
|
||||||
|
self.canonical().parent = other.canonical()
|
||||||
@@ -0,0 +1,225 @@
|
|||||||
|
from collections import deque
|
||||||
|
from typing import List
|
||||||
|
import re
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
class NodeColor(Enum):
|
||||||
|
"""Enumeration for node colors in a graph traversal."""
|
||||||
|
WHITE = 1 # WHITE: not visited
|
||||||
|
GRAY = 2 # GRAY: visited but not all neighbors visited
|
||||||
|
BLACK = 3 # BLACK: visited and all neighbors visited
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Vertex:
|
||||||
|
"""A vertex in a graph."""
|
||||||
|
def __init__(self, value):
|
||||||
|
self.value = value
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return str(self.value)
|
||||||
|
|
||||||
|
class Graph:
|
||||||
|
"""A graph."""
|
||||||
|
def insert_vertex(self, name: str):
|
||||||
|
raise NotImplementedError("Please implement this method in subclass")
|
||||||
|
|
||||||
|
def connect(self, name1: str, name2: str, weight: float = 1):
|
||||||
|
raise NotImplementedError("Please implement this method in subclass")
|
||||||
|
|
||||||
|
def all_vertices(self) -> List[Vertex]:
|
||||||
|
raise NotImplementedError("Please implement this method in subclass")
|
||||||
|
|
||||||
|
def get_vertex(self, name: str) -> Vertex:
|
||||||
|
raise NotImplementedError("Please implement this method in subclass")
|
||||||
|
|
||||||
|
def get_adjacent_vertices(self, name: str) -> List[Vertex]:
|
||||||
|
raise NotImplementedError("Please implement this method in subclass")
|
||||||
|
|
||||||
|
def get_adjacent_vertices_with_weight(self, name: str) -> List[tuple[Vertex, float]]:
|
||||||
|
raise NotImplementedError("Please implement this method in subclass")
|
||||||
|
|
||||||
|
def all_edges(self) -> List[tuple[str, str, float]]:
|
||||||
|
raise NotImplementedError("Please implement this method in subclass")
|
||||||
|
|
||||||
|
def bfs(self, start_name: str):
|
||||||
|
"""
|
||||||
|
Perform a breadth-first search starting at the given vertex.
|
||||||
|
:param start_name: the name of the vertex to start at
|
||||||
|
:return: a tuple of two dictionaries, the first mapping vertices to distances from the start vertex,
|
||||||
|
the second mapping vertices to their predecessors in the traversal tree
|
||||||
|
"""
|
||||||
|
|
||||||
|
color_map = {} # maps vertices to their color
|
||||||
|
distance_map = {} # maps vertices to their distance from the start vertex
|
||||||
|
predecessor_map = {} # maps vertices to their predecessor in the traversal tree
|
||||||
|
|
||||||
|
# Initialize the maps
|
||||||
|
for vertex in self.all_vertices():
|
||||||
|
color_map[vertex] = NodeColor.WHITE
|
||||||
|
distance_map[vertex] = None
|
||||||
|
predecessor_map[vertex] = None
|
||||||
|
|
||||||
|
# Start at the given vertex
|
||||||
|
start_node = self.get_vertex(start_name)
|
||||||
|
color_map[start_node] = NodeColor.GRAY
|
||||||
|
distance_map[start_node] = 0
|
||||||
|
|
||||||
|
# Initialize the queue with the start vertex
|
||||||
|
queue = deque()
|
||||||
|
queue.append(start_node)
|
||||||
|
|
||||||
|
# Process the queue
|
||||||
|
while len(queue) > 0:
|
||||||
|
vertex = queue.popleft()
|
||||||
|
for dest in self.get_adjacent_vertices(vertex.value):
|
||||||
|
if color_map[dest] == NodeColor.WHITE:
|
||||||
|
color_map[dest] = NodeColor.GRAY
|
||||||
|
distance_map[dest] = distance_map[vertex] + 1
|
||||||
|
predecessor_map[dest] = vertex
|
||||||
|
queue.append(dest)
|
||||||
|
color_map[vertex] = NodeColor.BLACK
|
||||||
|
|
||||||
|
# Return the distance and predecessor maps
|
||||||
|
return distance_map, predecessor_map
|
||||||
|
|
||||||
|
|
||||||
|
def path(self, destination, map):
|
||||||
|
"""
|
||||||
|
Compute the path from the start vertex to the given destination vertex.
|
||||||
|
The map parameter is the predecessor map
|
||||||
|
"""
|
||||||
|
path = []
|
||||||
|
destination_node = self.get_vertex(destination)
|
||||||
|
while destination_node is not None:
|
||||||
|
path.insert(0, destination_node.value)
|
||||||
|
destination_node = map[destination_node]
|
||||||
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class AdjacencyListGraph(Graph):
|
||||||
|
"""A graph implemented as an adjacency list."""
|
||||||
|
def __init__(self):
|
||||||
|
self.adjacency_map = {} # maps vertex names to lists of adjacent vertices
|
||||||
|
self.vertex_map = {} # maps vertex names to vertices
|
||||||
|
|
||||||
|
def insert_vertex(self, name: str):
|
||||||
|
if name not in self.vertex_map:
|
||||||
|
self.vertex_map[name] = Vertex(name)
|
||||||
|
if name not in self.adjacency_map:
|
||||||
|
self.adjacency_map[name] = []
|
||||||
|
|
||||||
|
def connect(self, name1: str, name2: str, weight: float = 1):
|
||||||
|
adjacency_list = self.adjacency_map[name1]
|
||||||
|
dest = self.vertex_map[name2]
|
||||||
|
adjacency_list.append((dest, weight))
|
||||||
|
|
||||||
|
def all_vertices(self) -> List[Vertex]:
|
||||||
|
return list(self.vertex_map.values())
|
||||||
|
|
||||||
|
def get_vertex(self, name: str) -> Vertex:
|
||||||
|
return self.vertex_map[name]
|
||||||
|
|
||||||
|
def get_adjacent_vertices(self, name: str) -> List[Vertex]:
|
||||||
|
return list(map(lambda x: x[0], self.adjacency_map[name]))
|
||||||
|
|
||||||
|
def get_adjacent_vertices_with_weight(self, name: str) -> List[tuple[Vertex, float]]:
|
||||||
|
return self.adjacency_map[name]
|
||||||
|
def all_edges(self) -> List[tuple[str, str, float]]:
|
||||||
|
result = []
|
||||||
|
for name in self.adjacency_map:
|
||||||
|
for (dest, weight) in self.adjacency_map[name]:
|
||||||
|
result.append((name, dest.value, weight))
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class AdjacencyMatrixGraph(Graph):
|
||||||
|
"""A graph implemented as an adjacency matrix."""
|
||||||
|
def __init__(self):
|
||||||
|
self.index_map = {} # maps vertex names to indices
|
||||||
|
self.vertex_list = [] # list of vertices
|
||||||
|
self.adjacency_matrix = [] # adjacency matrix
|
||||||
|
|
||||||
|
def insert_vertex(self, name: str):
|
||||||
|
if name not in self.index_map:
|
||||||
|
self.index_map[name] = len(self.vertex_list)
|
||||||
|
self.vertex_list.append(Vertex(name))
|
||||||
|
for row in self.adjacency_matrix: # add a new column to each row
|
||||||
|
row.append(None)
|
||||||
|
self.adjacency_matrix.append([None] * len(self.vertex_list)) # add a new row
|
||||||
|
|
||||||
|
def connect(self, name1: str, name2: str, weight: float = 1):
|
||||||
|
index1 = self.index_map[name1]
|
||||||
|
index2 = self.index_map[name2]
|
||||||
|
self.adjacency_matrix[index1][index2] = weight
|
||||||
|
|
||||||
|
def all_vertices(self) -> List[Vertex]:
|
||||||
|
return self.vertex_list
|
||||||
|
|
||||||
|
def get_vertex(self, name: str) -> Vertex:
|
||||||
|
index = self.index_map[name]
|
||||||
|
return self.vertex_list[index]
|
||||||
|
|
||||||
|
def get_adjacent_vertices(self, name: str) -> List[Vertex]:
|
||||||
|
index = self.index_map[name]
|
||||||
|
result = []
|
||||||
|
for i in range(len(self.vertex_list)):
|
||||||
|
if self.adjacency_matrix[index][i] is not None:
|
||||||
|
name = self.vertex_list[i].value
|
||||||
|
result.append(self.get_vertex(name))
|
||||||
|
return result
|
||||||
|
|
||||||
|
def get_adjacent_vertices_with_weight(self, name: str) -> List[tuple[Vertex, float]]:
|
||||||
|
index = self.index_map[name]
|
||||||
|
result = []
|
||||||
|
for i in range(len(self.vertex_list)):
|
||||||
|
if self.adjacency_matrix[index][i] is not None:
|
||||||
|
name = self.vertex_list[i].value
|
||||||
|
result.append((self.get_vertex(name), self.adjacency_matrix[index][i]))
|
||||||
|
return result
|
||||||
|
|
||||||
|
def all_edges(self) -> List[tuple[str, str, float]]:
|
||||||
|
result = []
|
||||||
|
for i in range(len(self.vertex_list)):
|
||||||
|
for j in range(len(self.vertex_list)):
|
||||||
|
if self.adjacency_matrix[i][j] is not None:
|
||||||
|
result.append((self.vertex_list[i].value, self.vertex_list[j].value, self.adjacency_matrix[i][j]))
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
def read_cave_into_graph(graph: Graph, filename: str):
|
||||||
|
"""Read a cave description from a file and insert it into the given graph."""
|
||||||
|
with open(filename, "r") as file:
|
||||||
|
lines = file.readlines()
|
||||||
|
for line in lines:
|
||||||
|
# match a line with two node names and an optional direction
|
||||||
|
m = re.match(r"(^\s*\"(.*)\"\s*([<>]*)\s*\"(.*)\"\s*)", line)
|
||||||
|
if m:
|
||||||
|
startnode = m.group(2)
|
||||||
|
endnode = m.group(4)
|
||||||
|
opcode = m.group(3)
|
||||||
|
graph.insert_vertex(startnode)
|
||||||
|
graph.insert_vertex(endnode)
|
||||||
|
if '>' in opcode:
|
||||||
|
graph.connect(startnode, endnode)
|
||||||
|
if '<' in opcode:
|
||||||
|
graph.connect(endnode, startnode)
|
||||||
|
|
||||||
|
|
||||||
|
graph = AdjacencyListGraph()
|
||||||
|
#graph = AdjacencyMatrixGraph()
|
||||||
|
read_cave_into_graph(graph, "../../hoehle.txt")
|
||||||
|
_, predecessor_map = graph.bfs('Höhleneingang')
|
||||||
|
path = graph.path('Schatzkammer', predecessor_map)
|
||||||
|
print(path)
|
||||||
|
_, predecessor_map = graph.bfs('Schatzkammer')
|
||||||
|
path = graph.path('Höhleneingang', predecessor_map)
|
||||||
|
print(path)
|
||||||
|
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
import math
|
||||||
|
import re
|
||||||
|
|
||||||
|
from SoSe24.lec06_graph.disjoint import DisjointValue
|
||||||
|
from graph import Graph, AdjacencyListGraph, AdjacencyMatrixGraph, Vertex
|
||||||
|
import heapq
|
||||||
|
|
||||||
|
|
||||||
|
def mst_prim(self, start_name: str = None):
|
||||||
|
""" Compute the minimum spanning tree of the graph using Prim's algorithm. """
|
||||||
|
|
||||||
|
distance_map = {} # maps vertices to their current distance from the spanning tree
|
||||||
|
parent_map = {} # maps vertices to their predecessor in the spanning tree
|
||||||
|
|
||||||
|
Vertex.__lt__ = lambda self, other: distance_map[self] < distance_map[other]
|
||||||
|
|
||||||
|
queue = []
|
||||||
|
|
||||||
|
if start_name is None:
|
||||||
|
start_name = self.all_vertices()[0].value;
|
||||||
|
|
||||||
|
# Initialize the maps
|
||||||
|
for vertex in self.all_vertices():
|
||||||
|
distance_map[vertex] = 0 if vertex.value == start_name else math.inf
|
||||||
|
parent_map[vertex] = None
|
||||||
|
queue.append(vertex)
|
||||||
|
|
||||||
|
heapq.heapify(queue) # Convert the list into a heap
|
||||||
|
|
||||||
|
# Process the queue
|
||||||
|
cost = 0 # The cost of the minimum spanning tree
|
||||||
|
while len(queue) > 0:
|
||||||
|
vertex = heapq.heappop(queue)
|
||||||
|
cost += distance_map[vertex] # Add the cost of the edge to the minimum spanning tree
|
||||||
|
for (dest, w) in self.get_adjacent_vertices_with_weight(vertex.value):
|
||||||
|
if dest in queue and distance_map[dest] > w:
|
||||||
|
# Update the distance and parent maps
|
||||||
|
queue.remove(dest)
|
||||||
|
distance_map[dest] = w
|
||||||
|
parent_map[dest] = vertex
|
||||||
|
queue.append(dest) # Add the vertex back to the queue
|
||||||
|
heapq.heapify(queue) # Re-heapify the queue
|
||||||
|
|
||||||
|
# Return the distance and predecessor maps
|
||||||
|
return parent_map, cost
|
||||||
|
|
||||||
|
|
||||||
|
def mst_kruskal(self, start_name: str = None):
|
||||||
|
""" Compute the minimum spanning tree of the graph using Kruskal's algorithm. """
|
||||||
|
|
||||||
|
cost = 0
|
||||||
|
result = []
|
||||||
|
edges = self.all_edges()
|
||||||
|
|
||||||
|
# Create a disjoint set for each vertex
|
||||||
|
vertex_map = {v.value: DisjointValue(v) for v in self.all_vertices()}
|
||||||
|
|
||||||
|
# Sort the edges by weight
|
||||||
|
edges.sort(key=lambda edge: edge[2])
|
||||||
|
|
||||||
|
# Process the edges
|
||||||
|
for edge in edges:
|
||||||
|
start_name, end_name, weight = edge
|
||||||
|
# Check if the edge creates a cycle
|
||||||
|
if not vertex_map[start_name].same_set(vertex_map[end_name]):
|
||||||
|
result.append(edge)
|
||||||
|
vertex_map[start_name].union(vertex_map[end_name])
|
||||||
|
cost += weight
|
||||||
|
|
||||||
|
return result, cost
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
AdjacencyListGraph.mst_prim = mst_prim
|
||||||
|
AdjacencyMatrixGraph.mst_prim = mst_prim
|
||||||
|
|
||||||
|
AdjacencyListGraph.mst_kruskal = mst_kruskal
|
||||||
|
AdjacencyMatrixGraph.mst_kruskal = mst_kruskal
|
||||||
|
|
||||||
|
|
||||||
|
def read_elektro_into_graph(graph: Graph, filename: str):
|
||||||
|
pattern = re.compile(r'"([^"]+)";"([^"]+)";(\d+)')
|
||||||
|
with (open(filename, "r") as file):
|
||||||
|
for line in file:
|
||||||
|
m = pattern.match(line)
|
||||||
|
if m:
|
||||||
|
start_name = m.group(1)
|
||||||
|
end_name = m.group(2)
|
||||||
|
cost = int(m.group(3))
|
||||||
|
graph.insert_vertex(start_name)
|
||||||
|
graph.insert_vertex(end_name)
|
||||||
|
graph.connect(start_name, end_name, cost)
|
||||||
|
graph.connect(end_name, start_name, cost)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
graph = AdjacencyMatrixGraph()
|
||||||
|
read_elektro_into_graph(graph, "../../elektro.txt")
|
||||||
|
|
||||||
|
parents, cost = graph.mst_prim()
|
||||||
|
print(f"Kosten nach Prim: {cost}")
|
||||||
|
for node, parent in parents.items():
|
||||||
|
if parent is not None:
|
||||||
|
print(f"{node} - {parent}")
|
||||||
|
|
||||||
|
edges, cost = graph.mst_kruskal()
|
||||||
|
print(f"Kosten nach Kruskal: {cost}")
|
||||||
|
for start_name, end_name, _ in edges:
|
||||||
|
print(f"{start_name} - {end_name}")
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
"Höhleneingang";"Ost/West-Passage";5
|
||||||
|
"Höhleneingang";"Nord/Süd-Passage";3
|
||||||
|
"Nord/Süd-Passage";"Nebelraum";7
|
||||||
|
"Steiniger Pfad";"Ost/West-Passage";2
|
||||||
|
"Ost/West-Passage";"Schwefelgewölbe";4
|
||||||
|
"Schwefelgewölbe";"Steiniger Pfad";1
|
||||||
|
"Schatzkammer";"Nebelraum";2
|
||||||
|
"Steiniger Pfad";"Schatzkammer";6
|
||||||
+1000
@@ -0,0 +1,1000 @@
|
|||||||
|
-99
|
||||||
|
-99
|
||||||
|
-99
|
||||||
|
-99
|
||||||
|
-99
|
||||||
|
-99
|
||||||
|
-99
|
||||||
|
-99
|
||||||
|
-99
|
||||||
|
-99
|
||||||
|
-99
|
||||||
|
-98
|
||||||
|
-98
|
||||||
|
-98
|
||||||
|
-98
|
||||||
|
-98
|
||||||
|
-98
|
||||||
|
-98
|
||||||
|
-98
|
||||||
|
-98
|
||||||
|
-98
|
||||||
|
-98
|
||||||
|
-98
|
||||||
|
-98
|
||||||
|
-97
|
||||||
|
-97
|
||||||
|
-97
|
||||||
|
-97
|
||||||
|
-97
|
||||||
|
-96
|
||||||
|
-96
|
||||||
|
-96
|
||||||
|
-96
|
||||||
|
-96
|
||||||
|
-96
|
||||||
|
-96
|
||||||
|
-96
|
||||||
|
-96
|
||||||
|
-96
|
||||||
|
-96
|
||||||
|
-96
|
||||||
|
-95
|
||||||
|
-95
|
||||||
|
-95
|
||||||
|
-95
|
||||||
|
-95
|
||||||
|
-95
|
||||||
|
-95
|
||||||
|
-95
|
||||||
|
-95
|
||||||
|
-95
|
||||||
|
-95
|
||||||
|
-94
|
||||||
|
-94
|
||||||
|
-94
|
||||||
|
-94
|
||||||
|
-94
|
||||||
|
-94
|
||||||
|
-94
|
||||||
|
-94
|
||||||
|
-94
|
||||||
|
-94
|
||||||
|
-94
|
||||||
|
-93
|
||||||
|
-93
|
||||||
|
-93
|
||||||
|
-93
|
||||||
|
-93
|
||||||
|
-93
|
||||||
|
-93
|
||||||
|
-93
|
||||||
|
-92
|
||||||
|
-92
|
||||||
|
-92
|
||||||
|
-92
|
||||||
|
-92
|
||||||
|
-92
|
||||||
|
-92
|
||||||
|
-92
|
||||||
|
-92
|
||||||
|
-92
|
||||||
|
-92
|
||||||
|
-92
|
||||||
|
-92
|
||||||
|
-92
|
||||||
|
-92
|
||||||
|
-91
|
||||||
|
-91
|
||||||
|
-91
|
||||||
|
-91
|
||||||
|
-91
|
||||||
|
-91
|
||||||
|
-91
|
||||||
|
-91
|
||||||
|
-91
|
||||||
|
-90
|
||||||
|
-90
|
||||||
|
-90
|
||||||
|
-90
|
||||||
|
-90
|
||||||
|
-90
|
||||||
|
-90
|
||||||
|
-90
|
||||||
|
-90
|
||||||
|
-90
|
||||||
|
-90
|
||||||
|
-89
|
||||||
|
-89
|
||||||
|
-89
|
||||||
|
-89
|
||||||
|
-89
|
||||||
|
-89
|
||||||
|
-89
|
||||||
|
-89
|
||||||
|
-89
|
||||||
|
-89
|
||||||
|
-89
|
||||||
|
-89
|
||||||
|
-89
|
||||||
|
-89
|
||||||
|
-89
|
||||||
|
-89
|
||||||
|
-88
|
||||||
|
-88
|
||||||
|
-88
|
||||||
|
-88
|
||||||
|
-88
|
||||||
|
-88
|
||||||
|
-87
|
||||||
|
-87
|
||||||
|
-87
|
||||||
|
-87
|
||||||
|
-87
|
||||||
|
-87
|
||||||
|
-87
|
||||||
|
-87
|
||||||
|
-87
|
||||||
|
-86
|
||||||
|
-86
|
||||||
|
-86
|
||||||
|
-86
|
||||||
|
-86
|
||||||
|
-86
|
||||||
|
-86
|
||||||
|
-86
|
||||||
|
-86
|
||||||
|
-86
|
||||||
|
-86
|
||||||
|
-85
|
||||||
|
-85
|
||||||
|
-85
|
||||||
|
-85
|
||||||
|
-84
|
||||||
|
-84
|
||||||
|
-84
|
||||||
|
-84
|
||||||
|
-84
|
||||||
|
-84
|
||||||
|
-84
|
||||||
|
-84
|
||||||
|
-84
|
||||||
|
-84
|
||||||
|
-83
|
||||||
|
-83
|
||||||
|
-83
|
||||||
|
-83
|
||||||
|
-83
|
||||||
|
-83
|
||||||
|
-83
|
||||||
|
-83
|
||||||
|
-83
|
||||||
|
-82
|
||||||
|
-82
|
||||||
|
-82
|
||||||
|
-82
|
||||||
|
-82
|
||||||
|
-82
|
||||||
|
-82
|
||||||
|
-82
|
||||||
|
-82
|
||||||
|
-82
|
||||||
|
-82
|
||||||
|
-82
|
||||||
|
-81
|
||||||
|
-81
|
||||||
|
-81
|
||||||
|
-81
|
||||||
|
-81
|
||||||
|
-81
|
||||||
|
-81
|
||||||
|
-81
|
||||||
|
-81
|
||||||
|
-81
|
||||||
|
-81
|
||||||
|
-81
|
||||||
|
-80
|
||||||
|
-80
|
||||||
|
-80
|
||||||
|
-80
|
||||||
|
-80
|
||||||
|
-79
|
||||||
|
-79
|
||||||
|
-79
|
||||||
|
-79
|
||||||
|
-79
|
||||||
|
-79
|
||||||
|
-79
|
||||||
|
-79
|
||||||
|
-79
|
||||||
|
-79
|
||||||
|
-79
|
||||||
|
-78
|
||||||
|
-78
|
||||||
|
-78
|
||||||
|
-78
|
||||||
|
-78
|
||||||
|
-78
|
||||||
|
-78
|
||||||
|
-78
|
||||||
|
-78
|
||||||
|
-78
|
||||||
|
-77
|
||||||
|
-77
|
||||||
|
-77
|
||||||
|
-77
|
||||||
|
-77
|
||||||
|
-77
|
||||||
|
-77
|
||||||
|
-77
|
||||||
|
-77
|
||||||
|
-77
|
||||||
|
-77
|
||||||
|
-77
|
||||||
|
-77
|
||||||
|
-77
|
||||||
|
-77
|
||||||
|
-76
|
||||||
|
-76
|
||||||
|
-76
|
||||||
|
-76
|
||||||
|
-76
|
||||||
|
-76
|
||||||
|
-76
|
||||||
|
-76
|
||||||
|
-76
|
||||||
|
-76
|
||||||
|
-75
|
||||||
|
-75
|
||||||
|
-75
|
||||||
|
-75
|
||||||
|
-75
|
||||||
|
-75
|
||||||
|
-75
|
||||||
|
-75
|
||||||
|
-75
|
||||||
|
-75
|
||||||
|
-75
|
||||||
|
-75
|
||||||
|
-75
|
||||||
|
-75
|
||||||
|
-74
|
||||||
|
-74
|
||||||
|
-74
|
||||||
|
-74
|
||||||
|
-74
|
||||||
|
-74
|
||||||
|
-74
|
||||||
|
-74
|
||||||
|
-74
|
||||||
|
-74
|
||||||
|
-74
|
||||||
|
-74
|
||||||
|
-74
|
||||||
|
-74
|
||||||
|
-74
|
||||||
|
-74
|
||||||
|
-74
|
||||||
|
-73
|
||||||
|
-73
|
||||||
|
-73
|
||||||
|
-73
|
||||||
|
-73
|
||||||
|
-73
|
||||||
|
-73
|
||||||
|
-73
|
||||||
|
-73
|
||||||
|
-72
|
||||||
|
-72
|
||||||
|
-72
|
||||||
|
-72
|
||||||
|
-72
|
||||||
|
-72
|
||||||
|
-71
|
||||||
|
-71
|
||||||
|
-71
|
||||||
|
-71
|
||||||
|
-71
|
||||||
|
-71
|
||||||
|
-71
|
||||||
|
-71
|
||||||
|
-71
|
||||||
|
-71
|
||||||
|
-71
|
||||||
|
-70
|
||||||
|
-70
|
||||||
|
-70
|
||||||
|
-70
|
||||||
|
-70
|
||||||
|
-70
|
||||||
|
-70
|
||||||
|
-70
|
||||||
|
-70
|
||||||
|
-69
|
||||||
|
-69
|
||||||
|
-69
|
||||||
|
-69
|
||||||
|
-69
|
||||||
|
-69
|
||||||
|
-69
|
||||||
|
-69
|
||||||
|
-69
|
||||||
|
-69
|
||||||
|
-69
|
||||||
|
-69
|
||||||
|
-68
|
||||||
|
-68
|
||||||
|
-68
|
||||||
|
-68
|
||||||
|
-68
|
||||||
|
-68
|
||||||
|
-68
|
||||||
|
-67
|
||||||
|
-67
|
||||||
|
-67
|
||||||
|
-67
|
||||||
|
-67
|
||||||
|
-67
|
||||||
|
-67
|
||||||
|
-66
|
||||||
|
-66
|
||||||
|
-66
|
||||||
|
-66
|
||||||
|
-66
|
||||||
|
-66
|
||||||
|
-66
|
||||||
|
-66
|
||||||
|
-66
|
||||||
|
-65
|
||||||
|
-65
|
||||||
|
-65
|
||||||
|
-65
|
||||||
|
-65
|
||||||
|
-64
|
||||||
|
-64
|
||||||
|
-64
|
||||||
|
-64
|
||||||
|
-64
|
||||||
|
-64
|
||||||
|
-64
|
||||||
|
-64
|
||||||
|
-64
|
||||||
|
-64
|
||||||
|
-63
|
||||||
|
-63
|
||||||
|
-63
|
||||||
|
-63
|
||||||
|
-63
|
||||||
|
-63
|
||||||
|
-63
|
||||||
|
-63
|
||||||
|
-63
|
||||||
|
-63
|
||||||
|
-63
|
||||||
|
-62
|
||||||
|
-62
|
||||||
|
-62
|
||||||
|
-62
|
||||||
|
-62
|
||||||
|
-62
|
||||||
|
-62
|
||||||
|
-61
|
||||||
|
-61
|
||||||
|
-61
|
||||||
|
-61
|
||||||
|
-61
|
||||||
|
-61
|
||||||
|
-61
|
||||||
|
-61
|
||||||
|
-60
|
||||||
|
-60
|
||||||
|
-60
|
||||||
|
-60
|
||||||
|
-60
|
||||||
|
-60
|
||||||
|
-60
|
||||||
|
-60
|
||||||
|
-60
|
||||||
|
-60
|
||||||
|
-60
|
||||||
|
-60
|
||||||
|
-60
|
||||||
|
-60
|
||||||
|
-60
|
||||||
|
-60
|
||||||
|
-60
|
||||||
|
-60
|
||||||
|
-59
|
||||||
|
-59
|
||||||
|
-59
|
||||||
|
-59
|
||||||
|
-59
|
||||||
|
-59
|
||||||
|
-59
|
||||||
|
-59
|
||||||
|
-59
|
||||||
|
-58
|
||||||
|
-58
|
||||||
|
-58
|
||||||
|
-58
|
||||||
|
-58
|
||||||
|
-58
|
||||||
|
-58
|
||||||
|
-58
|
||||||
|
-58
|
||||||
|
-57
|
||||||
|
-57
|
||||||
|
-57
|
||||||
|
-57
|
||||||
|
-57
|
||||||
|
-57
|
||||||
|
-57
|
||||||
|
-57
|
||||||
|
-57
|
||||||
|
-57
|
||||||
|
-57
|
||||||
|
-56
|
||||||
|
-56
|
||||||
|
-56
|
||||||
|
-56
|
||||||
|
-56
|
||||||
|
-56
|
||||||
|
-56
|
||||||
|
-56
|
||||||
|
-56
|
||||||
|
-56
|
||||||
|
-55
|
||||||
|
-55
|
||||||
|
-55
|
||||||
|
-55
|
||||||
|
-55
|
||||||
|
-55
|
||||||
|
-55
|
||||||
|
-55
|
||||||
|
-55
|
||||||
|
-55
|
||||||
|
-54
|
||||||
|
-54
|
||||||
|
-54
|
||||||
|
-54
|
||||||
|
-54
|
||||||
|
-54
|
||||||
|
-54
|
||||||
|
-54
|
||||||
|
-54
|
||||||
|
-54
|
||||||
|
-54
|
||||||
|
-54
|
||||||
|
-53
|
||||||
|
-53
|
||||||
|
-53
|
||||||
|
-53
|
||||||
|
-53
|
||||||
|
-53
|
||||||
|
-53
|
||||||
|
-53
|
||||||
|
-52
|
||||||
|
-52
|
||||||
|
-52
|
||||||
|
-52
|
||||||
|
-52
|
||||||
|
-51
|
||||||
|
-51
|
||||||
|
-51
|
||||||
|
-50
|
||||||
|
-50
|
||||||
|
-50
|
||||||
|
-50
|
||||||
|
-50
|
||||||
|
-50
|
||||||
|
-50
|
||||||
|
-50
|
||||||
|
-50
|
||||||
|
-50
|
||||||
|
-50
|
||||||
|
-50
|
||||||
|
-50
|
||||||
|
-50
|
||||||
|
-50
|
||||||
|
-50
|
||||||
|
-49
|
||||||
|
-49
|
||||||
|
-49
|
||||||
|
-49
|
||||||
|
-49
|
||||||
|
-49
|
||||||
|
-49
|
||||||
|
-49
|
||||||
|
-49
|
||||||
|
-49
|
||||||
|
-49
|
||||||
|
-48
|
||||||
|
-48
|
||||||
|
-48
|
||||||
|
-48
|
||||||
|
-48
|
||||||
|
-48
|
||||||
|
-48
|
||||||
|
-48
|
||||||
|
-48
|
||||||
|
-48
|
||||||
|
-48
|
||||||
|
-48
|
||||||
|
-48
|
||||||
|
-48
|
||||||
|
-48
|
||||||
|
-48
|
||||||
|
-48
|
||||||
|
-48
|
||||||
|
-48
|
||||||
|
-48
|
||||||
|
-47
|
||||||
|
-47
|
||||||
|
-47
|
||||||
|
-47
|
||||||
|
-47
|
||||||
|
-47
|
||||||
|
-47
|
||||||
|
-47
|
||||||
|
-47
|
||||||
|
-47
|
||||||
|
-47
|
||||||
|
-47
|
||||||
|
-47
|
||||||
|
-47
|
||||||
|
-47
|
||||||
|
-46
|
||||||
|
-46
|
||||||
|
-46
|
||||||
|
-46
|
||||||
|
-46
|
||||||
|
-46
|
||||||
|
-46
|
||||||
|
-46
|
||||||
|
-46
|
||||||
|
-46
|
||||||
|
-46
|
||||||
|
-46
|
||||||
|
-45
|
||||||
|
-45
|
||||||
|
-45
|
||||||
|
-45
|
||||||
|
-45
|
||||||
|
-45
|
||||||
|
-45
|
||||||
|
-45
|
||||||
|
-45
|
||||||
|
-45
|
||||||
|
-45
|
||||||
|
-45
|
||||||
|
-44
|
||||||
|
-44
|
||||||
|
-44
|
||||||
|
-44
|
||||||
|
-44
|
||||||
|
-44
|
||||||
|
-44
|
||||||
|
-44
|
||||||
|
-44
|
||||||
|
-44
|
||||||
|
-44
|
||||||
|
-43
|
||||||
|
-43
|
||||||
|
-43
|
||||||
|
-43
|
||||||
|
-43
|
||||||
|
-43
|
||||||
|
-43
|
||||||
|
-43
|
||||||
|
-43
|
||||||
|
-43
|
||||||
|
-43
|
||||||
|
-43
|
||||||
|
-42
|
||||||
|
-42
|
||||||
|
-42
|
||||||
|
-42
|
||||||
|
-42
|
||||||
|
-42
|
||||||
|
-42
|
||||||
|
-42
|
||||||
|
-42
|
||||||
|
-42
|
||||||
|
-41
|
||||||
|
-41
|
||||||
|
-41
|
||||||
|
-41
|
||||||
|
-40
|
||||||
|
-40
|
||||||
|
-40
|
||||||
|
-40
|
||||||
|
-40
|
||||||
|
-40
|
||||||
|
-40
|
||||||
|
-40
|
||||||
|
-40
|
||||||
|
-39
|
||||||
|
-39
|
||||||
|
-39
|
||||||
|
-39
|
||||||
|
-39
|
||||||
|
-39
|
||||||
|
-39
|
||||||
|
-39
|
||||||
|
-39
|
||||||
|
-39
|
||||||
|
-39
|
||||||
|
-39
|
||||||
|
-39
|
||||||
|
-39
|
||||||
|
-39
|
||||||
|
-38
|
||||||
|
-38
|
||||||
|
-38
|
||||||
|
-38
|
||||||
|
-38
|
||||||
|
-38
|
||||||
|
-38
|
||||||
|
-38
|
||||||
|
-38
|
||||||
|
-38
|
||||||
|
-37
|
||||||
|
-37
|
||||||
|
-37
|
||||||
|
-37
|
||||||
|
-37
|
||||||
|
-37
|
||||||
|
-37
|
||||||
|
-36
|
||||||
|
-36
|
||||||
|
-36
|
||||||
|
-36
|
||||||
|
-36
|
||||||
|
-36
|
||||||
|
-36
|
||||||
|
-36
|
||||||
|
-35
|
||||||
|
-35
|
||||||
|
-35
|
||||||
|
-35
|
||||||
|
-35
|
||||||
|
-35
|
||||||
|
-34
|
||||||
|
-34
|
||||||
|
-34
|
||||||
|
-34
|
||||||
|
-34
|
||||||
|
-33
|
||||||
|
-33
|
||||||
|
-33
|
||||||
|
-33
|
||||||
|
-33
|
||||||
|
-33
|
||||||
|
-33
|
||||||
|
-32
|
||||||
|
-32
|
||||||
|
-32
|
||||||
|
-32
|
||||||
|
-32
|
||||||
|
-32
|
||||||
|
-32
|
||||||
|
-32
|
||||||
|
-32
|
||||||
|
-32
|
||||||
|
-32
|
||||||
|
-32
|
||||||
|
-31
|
||||||
|
-31
|
||||||
|
-31
|
||||||
|
-31
|
||||||
|
-31
|
||||||
|
-31
|
||||||
|
-31
|
||||||
|
-31
|
||||||
|
-31
|
||||||
|
-31
|
||||||
|
-31
|
||||||
|
-30
|
||||||
|
-30
|
||||||
|
-30
|
||||||
|
-30
|
||||||
|
-30
|
||||||
|
-30
|
||||||
|
-30
|
||||||
|
-30
|
||||||
|
-30
|
||||||
|
-30
|
||||||
|
-30
|
||||||
|
-30
|
||||||
|
-29
|
||||||
|
-29
|
||||||
|
-29
|
||||||
|
-29
|
||||||
|
-29
|
||||||
|
-29
|
||||||
|
-29
|
||||||
|
-29
|
||||||
|
-29
|
||||||
|
-29
|
||||||
|
-28
|
||||||
|
-28
|
||||||
|
-28
|
||||||
|
-28
|
||||||
|
-28
|
||||||
|
-28
|
||||||
|
-28
|
||||||
|
-28
|
||||||
|
-28
|
||||||
|
-28
|
||||||
|
-27
|
||||||
|
-27
|
||||||
|
-27
|
||||||
|
-27
|
||||||
|
-27
|
||||||
|
-27
|
||||||
|
-27
|
||||||
|
-27
|
||||||
|
-27
|
||||||
|
-27
|
||||||
|
-27
|
||||||
|
-27
|
||||||
|
-26
|
||||||
|
-26
|
||||||
|
-26
|
||||||
|
-26
|
||||||
|
-26
|
||||||
|
-26
|
||||||
|
-26
|
||||||
|
-26
|
||||||
|
-26
|
||||||
|
-25
|
||||||
|
-25
|
||||||
|
-25
|
||||||
|
-25
|
||||||
|
-25
|
||||||
|
-25
|
||||||
|
-25
|
||||||
|
-25
|
||||||
|
-24
|
||||||
|
-24
|
||||||
|
-24
|
||||||
|
-24
|
||||||
|
-24
|
||||||
|
-24
|
||||||
|
-24
|
||||||
|
-24
|
||||||
|
-24
|
||||||
|
-24
|
||||||
|
-23
|
||||||
|
-23
|
||||||
|
-23
|
||||||
|
-23
|
||||||
|
-23
|
||||||
|
-23
|
||||||
|
-23
|
||||||
|
-23
|
||||||
|
-23
|
||||||
|
-22
|
||||||
|
-22
|
||||||
|
-22
|
||||||
|
-22
|
||||||
|
-22
|
||||||
|
-22
|
||||||
|
-22
|
||||||
|
-22
|
||||||
|
-22
|
||||||
|
-21
|
||||||
|
-21
|
||||||
|
-21
|
||||||
|
-21
|
||||||
|
-21
|
||||||
|
-21
|
||||||
|
-21
|
||||||
|
-21
|
||||||
|
-20
|
||||||
|
-20
|
||||||
|
-20
|
||||||
|
-20
|
||||||
|
-20
|
||||||
|
-20
|
||||||
|
-20
|
||||||
|
-20
|
||||||
|
-20
|
||||||
|
-20
|
||||||
|
-20
|
||||||
|
-19
|
||||||
|
-19
|
||||||
|
-19
|
||||||
|
-19
|
||||||
|
-19
|
||||||
|
-19
|
||||||
|
-19
|
||||||
|
-19
|
||||||
|
-19
|
||||||
|
-19
|
||||||
|
-19
|
||||||
|
-18
|
||||||
|
-18
|
||||||
|
-18
|
||||||
|
-18
|
||||||
|
-18
|
||||||
|
-18
|
||||||
|
-18
|
||||||
|
-18
|
||||||
|
-18
|
||||||
|
-18
|
||||||
|
-18
|
||||||
|
-17
|
||||||
|
-17
|
||||||
|
-17
|
||||||
|
-17
|
||||||
|
-17
|
||||||
|
-17
|
||||||
|
-17
|
||||||
|
-17
|
||||||
|
-17
|
||||||
|
-17
|
||||||
|
-17
|
||||||
|
-17
|
||||||
|
-17
|
||||||
|
-17
|
||||||
|
-17
|
||||||
|
-16
|
||||||
|
-16
|
||||||
|
-16
|
||||||
|
-16
|
||||||
|
-16
|
||||||
|
-16
|
||||||
|
-16
|
||||||
|
-15
|
||||||
|
-15
|
||||||
|
-15
|
||||||
|
-15
|
||||||
|
-15
|
||||||
|
-15
|
||||||
|
-15
|
||||||
|
-15
|
||||||
|
-15
|
||||||
|
-15
|
||||||
|
-15
|
||||||
|
-14
|
||||||
|
-14
|
||||||
|
-14
|
||||||
|
-14
|
||||||
|
-14
|
||||||
|
-14
|
||||||
|
-14
|
||||||
|
-14
|
||||||
|
-14
|
||||||
|
-14
|
||||||
|
-14
|
||||||
|
-14
|
||||||
|
-14
|
||||||
|
-13
|
||||||
|
-13
|
||||||
|
-13
|
||||||
|
-13
|
||||||
|
-13
|
||||||
|
-13
|
||||||
|
-13
|
||||||
|
-12
|
||||||
|
-12
|
||||||
|
-12
|
||||||
|
-12
|
||||||
|
-12
|
||||||
|
-11
|
||||||
|
-11
|
||||||
|
-11
|
||||||
|
-11
|
||||||
|
-11
|
||||||
|
-11
|
||||||
|
-11
|
||||||
|
-11
|
||||||
|
-11
|
||||||
|
-10
|
||||||
|
-10
|
||||||
|
-10
|
||||||
|
-10
|
||||||
|
-10
|
||||||
|
-10
|
||||||
|
-10
|
||||||
|
-10
|
||||||
|
-10
|
||||||
|
-10
|
||||||
|
-10
|
||||||
|
-10
|
||||||
|
-10
|
||||||
|
-10
|
||||||
|
-10
|
||||||
|
-10
|
||||||
|
-9
|
||||||
|
-9
|
||||||
|
-9
|
||||||
|
-9
|
||||||
|
-9
|
||||||
|
-9
|
||||||
|
-9
|
||||||
|
-9
|
||||||
|
-9
|
||||||
|
-8
|
||||||
|
-8
|
||||||
|
-8
|
||||||
|
-8
|
||||||
|
-8
|
||||||
|
-8
|
||||||
|
-8
|
||||||
|
-8
|
||||||
|
-8
|
||||||
|
-8
|
||||||
|
-8
|
||||||
|
-8
|
||||||
|
-8
|
||||||
|
-8
|
||||||
|
-7
|
||||||
|
-7
|
||||||
|
-7
|
||||||
|
-7
|
||||||
|
-7
|
||||||
|
-7
|
||||||
|
-7
|
||||||
|
-7
|
||||||
|
-7
|
||||||
|
-7
|
||||||
|
-7
|
||||||
|
-7
|
||||||
|
-7
|
||||||
|
-7
|
||||||
|
-7
|
||||||
|
-6
|
||||||
|
-6
|
||||||
|
-6
|
||||||
|
-6
|
||||||
|
-6
|
||||||
|
-6
|
||||||
|
-6
|
||||||
|
-6
|
||||||
|
-6
|
||||||
|
-6
|
||||||
|
-6
|
||||||
|
-6
|
||||||
|
-6
|
||||||
|
-6
|
||||||
|
-5
|
||||||
|
-5
|
||||||
|
-5
|
||||||
|
-5
|
||||||
|
-5
|
||||||
|
-5
|
||||||
|
-5
|
||||||
|
-5
|
||||||
|
-4
|
||||||
|
-4
|
||||||
|
-4
|
||||||
|
-4
|
||||||
|
-4
|
||||||
|
-4
|
||||||
|
-4
|
||||||
|
-4
|
||||||
|
-3
|
||||||
|
-3
|
||||||
|
-3
|
||||||
|
-2
|
||||||
|
-2
|
||||||
|
-2
|
||||||
|
-2
|
||||||
|
-2
|
||||||
|
-2
|
||||||
|
-2
|
||||||
|
-2
|
||||||
|
-2
|
||||||
|
-2
|
||||||
|
-2
|
||||||
|
-2
|
||||||
|
-1
|
||||||
|
-1
|
||||||
|
-1
|
||||||
|
-1
|
||||||
|
-1
|
||||||
|
-1
|
||||||
|
-1
|
||||||
|
-1
|
||||||
Reference in new issue
Block a user