Compare commits

..

7 Commits

66 changed files with 23 additions and 3636 deletions

View File

@ -5,7 +5,7 @@
<excludeFolder url="file://$MODULE_DIR$/.venv" /> <excludeFolder url="file://$MODULE_DIR$/.venv" />
<excludeFolder url="file://$MODULE_DIR$/venv" /> <excludeFolder url="file://$MODULE_DIR$/venv" />
</content> </content>
<orderEntry type="jdk" jdkName="Python 3.12 (AlgoDatSoSe25)" jdkType="Python SDK" /> <orderEntry type="jdk" jdkName="Python 3.12 (SoSe25)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
</component> </component>
</module> </module>

View File

@ -1,141 +0,0 @@
#############################################################################################################################################
#...#.#.........#...#.....#.........#.....#.......#.................#...#.....#.......#.....#.......#.......#...............#.....#........E#
#.#.#.#.#.#.#####.#.#.###.#.#.#####.#.#.#.#.#####.###.#.###########.#.#.#.###.#.#.#####.#.###.###.#.#.#######.#######.#####.#.###.#.#######.#
#.#...#...#.#.....#.#.#.........................#...#.#.....#...#.....#.#...#.#.#.......#.....#...#...#.....#.#...#...#.#...#...#.#.....#.#.#
#.#####.#.#.#.#####.#.#.#.#.###.###.#.#.#.###.#.###.#######.#.#.#########.###.#.###############.#######.###.#.#.#.#.###.#.#####.#.#####.#.#.#
#.......#.#...#...#.#.#.#...#.................#.#.........................#...#.#...#.........#...#.....#...#.#.#.#.....#.#.....#...#.....#.#
#.#.#####.#####.###.#.#.#####.###.#.###.###.###.#########.#.#.###.#.#######.###.#.###.#######.#.#.#.#####.###.###.###.#.#.#.#.#####.###.###.#
#.#.......#.......#.#...#.......#.#...#.....#...#...#.........#.#...#.....#.#...#...#...#.....#.#...#.....#...#.....#.#...#.#.#...#...#.#...#
#.###.###.#######.#.###.#.#####.#.###.###.###.###.#.#######.#.#.#######.#.#.#.#####.###.###.#########.#####.###.#####.#.#####.#.#####.###.#.#
#...#.#.#...#.....#...#.#.#...#.#.............#...#.....#...#...#.......#...#.....#...#...#...........#.....#.#...#...#.#...#.#.....#.....#.#
###.#.#.#.#.#.###.###.#.#.#.#.#.###.###.#.#.#.#.#.#####.#######.#.###.###.#.#####.#.#.###.###########.#.#####.#.#.#.###.#.#.#.#.###.#######.#
#.....#.................#...#.#...#.#.#...#.#.#.#.....#.......#.#...#.#.#.#.......#.#...#.....#.......#.#.#.....#.#.#.....#...#.#.......#.#.#
#######.#.#.#.#.#.#.#########.#####.#.###.#.#.#.#.#.#########.#.###.#.#.#.#########.#######.#.###.#####.#.#.#####.#.###########.#.#####.#.#.#
#.....#.#.#.#.#.#...#.........#...#.#.#...#.#...#.#.......#...#.....#.#.............#.....#.#...#.#.....#.....#...#.......#...#.#...#.....#.#
#.###.#.###.###.###.#.#.#######.#.#.#.#.#.#.#####.#####.#.#.#######.#.#########.#####.###.#.###.###.###########.#.#######.#.#.#####.#######.#
#.#...#...#.....#.#.#.#.#.....#.#...#.#...#...#.......#.#.#.......#.#...#.....#.#.........#...#...#...#.........#...#.#...#.#.....#.........#
#.###.###.#######.#.###.#.###.#.#####.###.###.#.#######.#.#####.###.#.#.#####.#.#.#######.#######.###.###.#########.#.#.###.#####.#.#########
#...#...#.............#.#.#.#...#.#.......#.#.#.#.......#.#...#.....#.#.#.....#.........#...#.....#.#.....#.#.....#.#.#...........#.#.......#
#.#.###.#############.#.#.#.#####.#.#######.#.#.#.#######.###.#####.###.#.###.#.#######.###.#.###.#.#######.#.###.#.#.#######.#.#.#.#####.#.#
#.#.#...............#...#.#.....#...#...#.....#.#.#.....#.....#...#.....#...#.#...#.....#.#.#...#...#.......#.#.#...#.......#.#.#.#.....#.#.#
#.#.###############.###.#.#.###.#.###.#.#.#####.#.###.#######.#.#.#.#.#####.#.###.#.#####.#.###.#####.#.#####.#.#########.###.#.#.#####.#.#.#
#.#.......#.......#.#...#.#...#.#.#...#...#.#...#.#...#.....#.#.#...#.....#.#.#...#.#.....#...#.....#.#.#...#.....#.......#...#...#...#...#.#
#########.#####.#.#.#.###.###.#.#.#.#######.#.###.#.###.###.#.#######.###.#.#.#.###.#.#######.###.#.#.#.#.#.#####.#.#######.#########.#####.#
#...#...#...#...#.#.#.#...#...#.#.#.#.............#.....#...#...#...#...#.#.....#...#...#...#.#.......#.#.#.....#.#.......#.#...#.....#.#...#
#.#.#.#.###.#.###.#.###.###.#.###.#.#############.#.#####.###.#.#.#.#####.#.#####.#####.#.#.#.#.#.###.#.#.###.#.#.#######.#.###.#.#.#.#.#.###
#.#...............#.....#...#.#...#.....#.....#...#.#.#...#.....#.#.......#.....#.....#...#.#...#...#.#...................#...#...#.#...#...#
#.#######.#.#.###########.#.###.#######.#.###.#.###.#.#.###.#####.###########.#####.#.###.#.#######.#################.#####.#.#####.###.###.#
#.....#.....#.#...............#...#...#.#.#.#...#.....#...#...#.......#...#...#...#.#...#.#.......#...#.........#...#.#...#.#.....#.#...#...#
#####.#######.#.#############.###.#.###.#.#.#############.###.###.###.#.###.###.#.###.#.###.#########.#.#######.#.#.#.#.#.#######.#.#####.###
#.....#.....#.#.............#.....#...#...#.........#.......#...#...#.#.......#.#...#...............#.#...#.#...#.#...#.#.....#...#.#...#...#
#.#####.###.#.#############.#########.#####.###.###.#.#####.###.#####.#.#.#####.###.#.###.#########.#.###.#.#.###.#####.#####.#.###.#.#.###.#
#.#.......#.#...#.......#...#.......#.....#...#.#.....#.#.....#.......#...#...#.#...#...#.#.......#.#.#...#...#.......#.#.#...#.#...#.#.....#
#.#.#######.#.#.#.###.###.###.#####.###.#####.#.#######.#.###########.#####.#.#.#.#####.#.#.#####.#.#.#.###.###########.#.#.###.#.#.#.#######
#.#.#.....#.#.#.#...#.....#.#.....#...#.......#...#.......#.....#...#.#.....#...#.....#.#.#...#.....#.#.#.#...........#.#.#.#...#.#.........#
#.#.#.###.#.#.#####.#######.#.#.#.###.###########.#######.#.###.#.#.#.#.#############.###.#.#.#####.#.#.#.#########.#.#.#.#.#.#####.###.###.#
#...#...#.#.#.....#.......#...#...#.#...#.......#...#...#.#...#...#...#.#.....#.....#...#.#.#.....#.#...#.........#.#.#.#...#.....#.....#...#
#.#####.#.#.#.#.#########.#####.###.###.#.#####.#.#.#.#.#####.#########.###.#.#####.###.#.###.###.#.#.#.#.#.#####.#.#.#.#.###.###.#.###.#####
#...#.#.#...#.#.........#.....#.#...#...#.....#...#...#.........#...#.#...#.#.....#...#.#...#.#...#.#.#.#.#.#...#.#.#...#.#.#.#...#...#.....#
###.#.#.#####.#######.#.#####.#.#.#.#.#######.###.#.###########.#.#.#.###.#.#####.#.###.###.#.#.###.#.#.#.###.#.#.#.#####.#.#.#.#.###.#.###.#
#.#.#.#...#.#.#...#...#.....#.#.#.#...#...#.....#.#.#.........#...#...#...#...#.....#...#...#.#.#...#...#.....#.#.#.#.....#.................#
#.#.#.###.#.#.###.#.#########.#.#.#####.#.#.###.#.#.#.#######.#######.#.#####.#.#####.###.#####.###############.#.#.#.#####.#.#.###.###.#.#.#
#.#.#...#.#.....#.#.............#...#...#.#...#.#.#.#.#.....#.......#.#...#...#.#...#.....#...#.......#.....#...#...#.#.......#...#.#...#.#.#
#.#.#.###.#####.#.#########.###.###.#.###.###.#.#.#.#.#.#.###.#.#.###.###.#.#####.#.###.#.#.#.#####.#.#.###.#.#######.#.#########.###.#.#.#.#
#...#.........#...#...#...#.....#.#.#.#.#.....#...#.#.#.#.....#.#.....#...#.#...#.#...#.#.#.#...#...#.#.#.....#.....#.#.............#.#.#.#.#
#.#####.#####.###.###.#.#.#.###.#.#.#.#.#######.#####.#.###.#.#.#######.###.#.#.#.###.#.#.#.###.#.###.#.#######.###.#.###.#.#######.#.#.#.###
#.#.........#...#.#...#.....#.........#.......#.......#...#...#.........#...#.#...#...#...#...#...#.....#.........#.#.....#.#.......#...#...#
#.###.#####.###.#.#.#.#.#######.#.#########.#############.###.###########.#.#.#####.#######.#.#########.#.#########.#####.###.###.#####.###.#
#...#...#...#.#.#...#.#.#.....#...#.......#...#.......#...#...#.......#...#...#...#.....#...#...#.....#.#.#...#.........#...#...#.#.........#
###.#.#.#.###.#.#####.#.#.###.#####.###.#.###.#.###.#.#####.#.###.###.#.#.###.#.#.#####.#.#####.#.#.#.###.###.#.#.#######.#.###.###.#.#.#.#.#
#...#.#.#.#...#.......#...#...#.....#.#.#...#...#.........#.#.....#.#.#.#.#...#.#.....#.#...#...#.#.#.....#...#.#.#.......#...#...#.#.....#.#
#.#####.#.#.#.#######.#####.###.#####.#.###.#####.#######.#.#######.#.#.###.###.#.###.#.###.#.#.#.#.#######.###.#.#.###.#####.#.#.#.###.###.#
#.#.....#...........#.....#.....#...#.#...#.....#.#...#...#...#.#...#...#...#.......#.#...#.#.#.#.#.......#.#...#.#...#...#...#.#...#.....#.#
#.#.#.#####.#.#####.#####.#.#####.#.#.###.#####.#.#.#.#.#####.#.#.#.###.#.###.###.#.#.###.#.#.#.#.###.###.#.#.#######.#.#.#.#.#.#.###.###.#.#
#.............#...#.....#.#.......#...#.#.....#...#.....#.....#.#.#.....#.#...#.....#...#...#.#.#.....#.#.#.#.......#.#...#.#.......#...#...#
#.#.###.###.###.#.#####.#.###########.#.#.###.#####.#######.###.#.#######.#####.#######.#####.#.#######.#.#.#####.###.#.#.#.#####.###.#.###.#
#.#.#...#.......#.#...#.#.#.....#...#.#.....#...#...#.......#.#.........#.......#.....#...#...#.......#...#.....#.....#.#.#.........#.#...#.#
#.#.#.#.#.#.#####.#.#.###.#.###.#.#.#.#.#.#####.#.#####.#.###.#.#####################.###.#.#########.#.###.###.#######.#.#######.#.###.#.###
#.....#.........#.#.#.#.....#.#.#.#.#.#.#.#.....#.#...#.#.....#...#...#.........#...#.#...#.....#.....#.....#.#.#.......#...#.....#...#.#...#
#.###.#.###.###.#.#.#.#.#.#.#.#.#.###.#.###.#####.#.#.#.#.#.###.###.#.#.#.#.###.#.#.#.#.#######.#####.#######.#.#.###.###.#.#.#.#.###.###.#.#
#.....#.#.....#.#...#...#.#...#.#...#.#...#...#.....#...#.#.#...#...#...#.........#...#.#.#.....#...#.#.......#...#.......#...#.#...#...#.#.#
#######.###.###.#########.###.#.#.#.#.###.###.#########.#.#.#.###.#.###.###.#########.#.#.#.###.#.#.#.###.#.#####.#.#####.#######.#.###.###.#
#.#...#...#.#...#.....#...#...#.#.#.........#.#.........#.#.#...#.#...#...#.#.......#.#.#.#.#...#.#.#.....#.#.....#.#.....#...#...#...#.....#
#.#.#.###.#.#.#######.#.###.###.#.#########.#.#.###.###.#.###.#.#.#.#.#.###.#.#####.###.#.#.#.###.#.###.#####.#####.#######.#.#.#####.#####.#
#...#...#...#.......#.#...#.#...#.#.......#.#...#.......#.......#.#.#.#.#...#...#.#...#.#.#.#.#...#.......#...#...#.....#...#...............#
#.###.#.###.#######.#.###.#.#.#####.#####.#######.#.#############.#.#.#.#.#.###.#.###.#.#.#.###.#########.#.###.#.#####.#.#######.#.###.#.###
#.#.#...#...#.....#.#.....#.#.#.....#...#.........#.........#.....#.#.#.#.#.#...#...#...#.#.....#.#.........#...#...#.......................#
#.#.###.#.#######.#.#######.#.#.#####.#.#.#.#######.###.###.#.#####.#.###.#.#.###.#.#####.###.###.#.#.#########.#####.#.###.#.#.###.###.#.#.#
#.....#.#.........#...#.....#.#.....#.#.#.#.....#...#.....#...#.....#...#.#.#...#.#.............#...#...........#.......#...#.#.#...........#
#####.#.#######.#####.#.#####.#####.#.#.#.#####.#.###.###.#####.#######.#.#####.#####.#####.###.#.###########.###.#####.#.#####.#####.#.#.#.#
#.#...#.....#...#.....#.#...#.#.....#...#.....#.....#.#...#.#...#...#...#.#...#.....#.........#.#...#.......#...#.#.....#...#...#.....#.#.#.#
#.#.#######.#.###.#.###.#.###.#.#####.#.#####.#####.###.#.#.#.#####.#.###.#.#.#####.#######.#.#####.#.#####.###.#.###.#####.#.#.#.#######.#.#
#.#.#.....#...#...#.#...#...#.#...#...#...#.#.....#.....#...#.......#...#...#.....#.#.....#.#.....#.#.#.....#.#.#...#.#...#...#.#.......#.#.#
#.#.#.###.#####.###.#.#####.#.#.#.#.#.###.#.#.###.#########.#.#####.#####.#####.#.#.#.###.#####.###.#.#.#####.#.###.#.###.#####.#######.#.#.#
#.#.#.#.......#...#.#.#.....#.....#.#...#.#.....#.........#.#.......#...#...#...#.#.......#...#.#.....#.......#.#...#.....#...#.......#.#.#.#
#.#.#.#######.#.#.#.#.#.###.#.#####.###.#.#.###.#########.###.#######.#.#.###.#########.###.#.#.#.#.#######.###.#.#####.###.#.#.#######.#.#.#
#...#.....#.#.#.#.#.#.#.#...#.........#.#...#...#.....#.#...#.....#...#...#...#.....#.......#.#.#.#...........#.#.#...#...#.#...#.......#.#.#
#.#######.#.#.###.#.#.#.#.###.#########.#####.#####.#.#.###.#####.#.#######.#.#.#.#.#.###.###.#.#.#.#########.#.#.#.#.#.#.#.#####.#######.#.#
#.#.......#.......#.#...#.#.#.#.........#...#.......#.#...#.#.....#...#.#...#.#.#.#.#.....#.#.#...#.......#.#.#.#...#...#.#.#...#.....#...#.#
#.#.#######.#####.#######.#.#.#.#########.###########.#.###.#.#######.#.#.###.#.#.#########.#.#######.#.#.#.#.#.#########.#.#.#.#####.#.#.#.#
#.#.#.......#...#.#.......#...#.....................#...#...#.#...#...#.#...#.#.#.#...#...#...#...#...#.#.....#...#...#...#...#.....#...#...#
#.#.#########.#.###.###########.#.###.#########.#.#####.#.###.#.###.###.###.###.#.#.#.#.#.#.###.#.#.#.#.#.#.###.#.#.#.#.###########.#####.#.#
#.#.#.............#...#.#.......#...#.....#...#.#.....#.#.....#.......#...#.....#...#...#.#.#...#.#.#.#.#.#.....#.#.#.#.#.........#.....#.#.#
#.#.#.#####.#.###.###.#.#.#.#########.###.#.#.#######.#.#############.###.###############.#.#.###.#.#.#.#.#####.#.#.#.#.#.#####.###.###.#.#.#
#.#.#.#.#...#.#.#.....#...#.............#...#.......#.#.#...........#.....#.......#.....#.#.#.#.#...#.#.#.#.......#.#.#.#...#.#.........#.#.#
#.#.#.#.#.#.#.#.#########.#.#####.###.#########.###.#.#.#.#########.#####.#.#####.###.#.#.#.#.#.#.###.###.#########.#.#.###.#.#####.#######.#
#.#.....#.#.#.#.........#...#.......#.#.......#.#...#...#...#...#...#...#...#.........#.#.....#.......#...#.......#.#...#...#.....#.........#
#.###.###.#.#.#.#######.#####.###.#.###.###.#.#.#.#####.#.#.#.#.###.#.#.###############.#######.###.###.#.#.#####.#.###.#.#####.#.#.#######.#
#...#.....#.#.#.#.#...........#...#.....#.....#.#.....#...#.......#...#.............#.....#...#.#.......#.#.#.......#...#.....#.............#
###.#.#####.#.#.#.#.#.###########.#######.###########.#.#.#######.#.#.#############.#######.#.#.#.#.#####.#.###.###.#.#######.#.###.#.###.#.#
#...#.#...#.#...#.#.#...#.......#.......#...#.....#...#.#...#.....#...#...........#.........#.....#.#...#.#...#...#.#.......#...#.....#...#.#
#####.#.#.#.#####.#.#.#.###.###.#####.#.###.#.###.#.###.#####.#########.#####.###############.#.#####.#.###.#.###.#.#.###.#########.#######.#
#...#...#.#.#...#...#.#...#...#.....#.#.#.#...#.#.#.#.#.....#.#...#...#.....#.#.............#.#.#.....#...#.#...#...#...#.........#...#.....#
#.#.#.###.#.#.###.###.###.###.#####.#.#.#.#####.#.#.#.#####.#.#.#.#.#.#.###.###.###.#.#######.###.#######.#.###.#####.#####.#.###.#.#.#.#####
#.#.......#...#...#...#.#.....#...#...#.....#...#...#.#.....#...#.#.#.............#.#.#.......#...#.....#.#...#.......#...#.#.#...#...#.....#
#.###########.#.###.###.#########.#########.#.#.###.#.#.#########.#.###########.###.#.#.#######.#######.#.#############.#.###.#.###.#######.#
#...#...#...#.#.#...#.#...........#.......#.#.#...#...#.#.........#...#.......#.#...#.#.#...........#...#...#...........#...#.#...#.......#.#
###.#.#.#.#.###.#.###.#.###.###.###.###.###.###.###.#.#.#.#########.###.#####.###.#.###.#.#########.#.###.#.#.#############.#.###.#.#.###.#.#
#.#...#...#...#.#.#.......#.#...#.....#.......#...#...#.#...#.....#.#...#...#.#.........#.#...#.....#.....#.#...#.............#.#.#.#...#.#.#
#.###########.#.#.#########.#.###.#######.###.#.#.#.#.#.#.#.###.###.#.###.#.#.#.#.#.#####.###.#.#.#####.#.#.#.#.#.#############.#.#.###.#.#.#
#.#.........#...#.#.......#.#.#...#.......#.#...#.#.#.....#...#.#...#.....#.#...#.#...#.#...#.#.......#.#...#.#.#.....#.......#.#.........#.#
#.#.#######.#####.#.#####.#.###.###.#####.#.#.###.#.#####.###.#.#.#########.#####.###.#.#.#.#.#######.#.#######.#####.#.#####.#.###.###.###.#
#...#.#...........#.#.....#.....#...#...#.#.#.#...#.#...#...#.#...#.......#.....#.#.#.#.....#.#.......#...#...#.#.....#.....#.#...#.#.....#.#
#.###.#.#######.#.#.#.###.#.#####.###.#.#.#.#.#.###.#.#.#####.#.#####.#.#######.#.#.#.#.#####.#.#########.#.#.#.#########.###.#.###.#.#.#.#.#
#.....#.......#.#...#.#...#...#.#.#...#.#.#...#...#.#.#.......#.#...#.#.....#...#...#.#.#.....#.#.......#.#.#...#.........#...#...#.#...#.#.#
###.#########.#.#####.#.#####.#.#.#.###.#.#.###.#.#.#.#########.#.#.###.#.#.#.#.#####.#.#.#####.#.#.#####.#.#####.#########.#####.#.###.#.#.#
#...#.........#...#...#.#.......#.#.....#.#.....#.#.#.#...#.......#...#...#.#.#.....#.#.#.....#.#.#.#...........#.......#.#.#...#...#...#...#
#.###.###########.###.#.#########.###.###.#####.#.#.#.#.#.#.#########.#.###.#.#####.#.#.#####.#.#.###.#######.#.#######.#.#.#.#.#.#.#.#.#####
#...#.#.......#.#.....#...#.....#...#...#.....#...#...#.#...#.........#.#...#.....#...#.......#.#.#...#.....#.#.#.....#.#.#...#.#...#...#...#
###.#.#.###.#.#.#####.###.#.###.###.###.#####.#########.###.#.#########.#.#####.#.#####.#######.#.#.###.###.#.#.#.#.###.#.#####.###.###.###.#
#.#.#.#...#.#...#...#.#...#.#.#.....#.......#.............#.#.....#...#.#.#...................#.#.#.#...#...#.#.....#...#.......#.....#...#.#
#.#.#.#####.#####.#.#.#.###.#.#######.###################.#######.#.#.#.#.#.#.###.###########.#.#.#.#.#####.###.#.#.#.###.###.#.#.#.#.#.#.#.#
#...#.....#.......#.........#...#.....#.........#.......#.........#.#...#.#.#...#.#...#.#.....#.#...#.....#.....#.#.#.#.....#.#...#...#...#.#
#.#######.#####.#############.#.#.###.#.#####.#.#.###.#############.#####.#####.#.#.#.#.#.#####.#.###.###.#####.###.#.#.###.#.#####.###.###.#
#...#...#.....#.....#...#.....#...#...#...#...#.......#...........#...#.#.#...#.#.#.#.#.........#...#...#.....#.#...#.#...#.#.....#...#.#...#
###.#.#.#####.#####.#.#.#.#.#######.#####.#.###########.#####.###.#.#.#.#.#.#.#.#.#.#.#######.#####.#########.#.#.###.#.#.#.#########.#.#.#.#
#.#.#.#...#...#.....#.#...#...#...#.......#...#...#.......#...#.#.#.#.#.....#.#.#.#.#.....................#...#...#...#.#.#.....#...#...#.#.#
#.#.#.###.#.#########.#######.#.#.###########.#.#.#.#######.###.#.###.#.#####.#.#.#.#.#.#.###.###.#.#####.#.#####.#.###.#.#####.#.#.###.#.###
#.#.#...#.#...#.......#.......#.#...........#...#.#.#.#.....#...#...#.#.#.....#.#.#.#.....#...#.....#...#...#.#...#.#.#.......#...#...#.....#
#.#.###.#####.#.#.#########.###.###.#######.#####.#.#.#.#####.#.###.#.#.#.#####.#.#.###.###.###.###.#.#.#####.#.###.#.#.#####.#####.#.#.###.#
#...#...#.....#.#.........#.......#.....#.#.#...#.....#...#.#.#...#...#...#.....#.#...#.....#...#...#.#.......#...#.#...#...#.#.....#.#.#...#
#.###.#.#.#.###.###.###.#.#######.#####.#.#.#.#.#.###.###.#.#.###.#####.###.#.#######.#.#####.###.###.#######.###.#.#.###.#.#.#.#####.#.#.#.#
#.#...#.#...#...#...#...#.......#.....#.#...#.#.#...#.#...#.#...#...........#.#...#...#.....#...#...#.#...#...#...#.#...#.#.#.#...#.#.#...#.#
#.#.###.###.#.###.#.#.#########.###.###.#.#####.#.#.#.#.###.###.#########.#####.#.#.#######.#.#####.#.#.#.#.#.#.###.#####.#.#####.#.#.#.###.#
#...#.#.#...#.#...#...#.......#.#...#...#.#.....#.#.#.#...#.#.............#.....#.#.#...#.#...#.....#.#.#.#.#.#.#.#.....#.#.......#.....#...#
#####.#.#.#.#.#.#######.#####.#.#.###.###.###.#.#.#.#.###.#.#.###.#.###.#.#.#####.#.#.#.#.#####.#####.#.###.#.#.#.#####.#.###############.###
#.....#.#.#.#.#.#.....#.....#...#...#.#.#.....#.#.#.#...#.#...#...#...#.#.#.#.#...#.#.#.#.....#.#.#...#...#.#.#...#...#.#.............#...#.#
#.#.#.#.#.#.#.#.#.###.#######.###.###.#.#.#####.#.#.#.###.#####.###.#.#.#.#.#.#.###.#.#.#####.#.#.#.#####.#.#.###.#.#.#.#.#.#########.#.###.#
#.#.#.#.#...#.#.....#...#...#.........#...#.....#.#.......#...#...#.#.#...#.#.#.......#...................#.#.#...#.#.#.#...#.......#.#...#.#
#.#.###.###.#.#####.###.#.#.###########.###.#####.#.#.#####.#.###.#.#.###.#.#.###########.###.#.#.#####.#.#.#.#.#####.#.###.#####.#.#.###.#.#
#.#.....#...#.#...#...#.#.#.......................................#.........#.....#.......#...#...#.....#.#.#.#.#...#.#.#.........#.#...#...#
#.#######.#.#.#.#.#####.#.#########.#.#.#####.#.#.#.#.#############.#.###.#.#.#####.#####.###.#.#.#.#####.#.###.#.#.#.#.###.#######.###.###.#
#.#...............................#.#...#.....#.#.#.#...#...............#.....#.....#.........#.#.......#.#.......#...#.#...#...#...#.#...#.#
#.#.#######.###.#####.#.###.#.#.###.#.#####.###.#.###.#.###########.#.#.#.#####.###.#####.###.#.#######.#.#####.#####.#.#.#.#.###.###.###.###
#...#.....#.#...#.....#...#.#.#.#...#.#...#.........................................#...#...#...#.....#.......#...#...#...#.#...#.......#...#
#.#.#.###.#.#.###.#######.#.#.#.#.###.#.#.###.#.#####.#######.#.#.#.#.#####.#.#######.#.#.#####.#.###.#####.###.#.#########.###.#.#########.#
#.#.#.#.#...#.#.....#.......#...#...#.#.#.....#.....#.....#...#.#.#.#.#.....#.........#.#.....#.#.#.#...#...#.....................#.....#...#
#.###.#.#####.#####.#.#####.#######.#.#.###########.#.###.#.###.#.###.###.#.###########.#.###.#.#.#.###.#####.###.#.###.#####.#.###.###.#.#.#
#...#.#.....#...#...#.#...#.#.......#.#.#.......#...#...#...............#.#.........#.#.#.............#.....#.#.#...#.#.....................#
#.#.#.###.#.###.#.#####.#.###.#######.#.#.#.#####.###.#.#.#########.###.#.#.#.#.###.#.#.#.#######.#.#.#####.#.#.#####.#####.#.###.###.###.#.#
#S#.......#.....#.......#.....#.........#...............#...............#.....#.......#.............#.....#...............#.................#
#############################################################################################################################################

View File

@ -1,15 +0,0 @@
###############
#.......#....E#
#.#.###.#.###.#
#.....#.#...#.#
#.###.#####.#.#
#.#.#.......#.#
#.#.#####.###.#
#...........#.#
###.#.#####.#.#
#...#.....#.#.#
#.#.#.###.#.#.#
#.....#...#.#.#
#.###.#.#.#.#.#
#S..#.....#...#
###############

View File

@ -1,5 +0,0 @@
Sabqponm
abcryxxl
accszExk
acctuvwj
abdefghi

View File

@ -1,15 +0,0 @@
###############
#.......#....E#
#.#.###.#.###.#
#.....#.#...#.#
#.###.#####.#.#
#.#.#.......#.#
#.#.#####.###.#
#...........#.#
###.#.#####.#.#
#...#.....#.#.#
#.#.#.###.#.#.#
#.....#...#.#.#
#.###.#.#.#.#.#
#S..#.....#...#
###############

View File

@ -1,8 +0,0 @@
"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

View File

@ -1,8 +0,0 @@
"Höhleneingang" <> "Ost/West-Passage"
"Höhleneingang" <> "Nord/Süd-Passage"
"Nord/Süd-Passage" <> "Nebelraum"
"Steiniger Pfad" > "Ost/West-Passage"
"Ost/West-Passage" <> "Schwefelgewölbe"
"Schwefelgewölbe" > "Steiniger Pfad"
"Schatzkammer" > "Nebelraum"
"Steiniger Pfad" > "Schatzkammer"

View File

@ -1,25 +0,0 @@
"Höhleneingang" <> "Ost/West-Passage"
"Höhleneingang" <> "Nord/Süd-Passage"
"Nord/Süd-Passage" <> "Nebelraum"
"Steiniger Pfad" > "Ost/West-Passage"
"Ost/West-Passage" <> "Schwefelgewölbe"
"Schwefelgewölbe" > "Steiniger Pfad"
"Schatzkammer" > "Nebelraum"
"Steiniger Pfad" <> "Schatzkammer"
"Steiniger Pfad" > "Höhleneingang"
"Kristallhöhle" <> "Schwefelgewölbe"
"Kristallhöhle" <> "Nebelraum"
"Versteckter Gang" > "Kristallhöhle"
"Versteckter Gang" > "Schatzkammer"
"Unterirdischer See" <> "Kristallhöhle"
"Unterirdischer See" <> "Ost/West-Passage"
"Geheimgang" > "Unterirdischer See"
"Geheimgang" > "Versteckter Gang"
"Abgrund" > "Geheimgang"
"Abgrund" > "Nebelraum"
"Verlorene Kammer" > "Abgrund"
"Verlorene Kammer" > "Schwefelgewölbe"
"Geheimgang" > "Schatzkammer"
"Kristallhöhle" > "Höhleneingang"
"Schwefelgewölbe" > "Höhleneingang"
"Abgrund" > "Schatzkammer"

View File

@ -1,9 +0,0 @@
xxxxxxxxxxxxxxxxxxxxx
x x
x S x
x x
x xxxxxxxx x
x x
x x
x A x
xxxxxxxxxxxxxxxxxxxxx

View File

@ -1,5 +0,0 @@
xxxxxAxxxxxxxxx
x xSx
xxxxxxxxxx xx x
x x
xxxxxxxxxxxxxxx

View File

@ -1,150 +0,0 @@
from utils.memory_array import MemoryArray
from utils.memory_cell import MemoryCell
from utils.memory_manager import MemoryManager
from utils.literal import Literal
import random
class PriorityQueue:
def __init__(self, size: Literal):
self.items = MemoryArray(size)
self.heapsize = MemoryCell(0)
def __len__(self):
return self.heapsize.value
def insert(self, item: MemoryCell):
if self.heapsize == self.items.length():
raise Exception("Queue is full")
self.heapsize.set(self.heapsize.succ())
self.items[adjust_index(self.heapsize)].set(item.value)
heapyfy_up(self.items, self.heapsize)
def pop(self) -> MemoryCell | None:
if self.is_empty():
return None
result = MemoryCell(self.items[Literal(0)])
self.heapsize.set(self.heapsize.pred())
if self.heapsize > Literal(1):
swap(self.items, 0, int(self.heapsize))
max_heapyfy(self.items, Literal(1), self.heapsize)
return result
def peek(self) -> MemoryCell | None:
if self.is_empty():
return None
return MemoryCell(self.items[Literal(0)])
def is_empty(self) -> bool:
return self.heapsize == Literal(0)
def __str__(self):
result = "[ "
for i, cell in enumerate(self.items.cells):
if i == int(self.heapsize):
result += "| "
result += str(cell) + " "
result += "]"
return result
def left_child(i: Literal):
return Literal(2 * int(i))
def right_child(i: Literal):
return Literal(2 * int(i) + 1)
def adjust_index(i: Literal):
return i.pred()
def heapyfy_up(z: MemoryArray, i: Literal):
if i == Literal(1):
return
parent = Literal(int(i) // 2)
if z[adjust_index(parent)] >= z[adjust_index(i)]:
return
swap(z, int(i)-1, int(parent)-1)
heapyfy_up(z, parent)
def max_heapyfy(z: MemoryArray, i: Literal, heapsize: Literal):
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)
max_heapyfy(z, max_value, heapsize)
def swap(z: MemoryArray, i: int, j: int):
tmp = z[Literal(i)].value
z[Literal(i)] = z[Literal(j)]
z[Literal(j)].set(tmp)
def analyze_complexity_insert(sizes, presorted=False):
"""
Analysiert die Komplexität der Insert-Funktion.
"""
for size in sizes:
MemoryManager().purge() # Speicher zurücksetzen
pq = PriorityQueue(Literal(size))
insert_list = [random.randint(-100, 100) for _ in range(size)]
if presorted:
insert_list.sort()
for insert_value in insert_list[:-1]:
pq.insert(MemoryCell(insert_value))
MemoryManager().reset()
pq.insert(MemoryCell(insert_list[-1]))
MemoryManager.save_stats(size)
MemoryManager.plot_stats(["cells", "compares", "writes"])
def analyze_complexity_pop(sizes, presorted=False):
"""
Analysiert die Komplexität der Pop-Funktion.
"""
for size in sizes:
MemoryManager().purge() # Speicher zurücksetzen
pq = PriorityQueue(Literal(size))
insert_list = [random.randint(-100, 100) for _ in range(size)]
if presorted:
insert_list.sort()
for insert_value in insert_list:
pq.insert(MemoryCell(insert_value))
MemoryManager().reset()
pq.pop()
MemoryManager.save_stats(size)
MemoryManager.plot_stats(["cells", "compares", "writes"])
if __name__ == '__main__':
length = Literal(10)
pq = PriorityQueue(length)
for i in range(10):
space_left = int(length) - len(pq)
if space_left > 0:
insert_count = random.randint(1, space_left)
insert_sequence = [random.randint(1, int(length)) for _ in range(insert_count)]
print(f"-> {insert_sequence}")
for j in insert_sequence:
pq.insert(MemoryCell(j))
print(f"{pq}")
if not pq.is_empty():
pop_count = random.randint(1, len(pq))
output_sequence = [int(pq.pop()) for _ in range(pop_count)]
print(f"<- {output_sequence}")
print(f"{pq}")
sizes = range(10, 501, 5)
analyze_complexity_insert(sizes, presorted=True)
analyze_complexity_pop(sizes, presorted=True)

View File

@ -1,93 +0,0 @@
from utils.memory_array import MemoryArray
from utils.memory_cell import MemoryCell
from utils.memory_manager import MemoryManager
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
def partition(z: MemoryArray, l: Literal, r: Literal):
p = mid_index(z, l, r, Literal((int(l)+int(r))//2))
swap(z, p, r)
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 mid_index(z: MemoryArray, i1, i2, i3):
if z[i1] < z[i2] < z[i3]:
return i2
elif z[i3] < z[i2] < z[i1]:
return i2
elif z[i2] < z[i1] < z[i3]:
return i1
elif z[i3] < z[i1] < z[i2]:
return i1
else:
return i3
def quick_sort(z: MemoryArray, l: Literal = None, r: Literal = None):
if l is None:
l = Literal(0)
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
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.
:param sort_func: Die Funktion, die analysiert wird.
:param sizes: Eine Liste von Eingabegrößen für die Analyse.
"""
for size in sizes:
MemoryManager.purge() # Speicher zurücksetzen
if presorted:
random_array = MemoryArray.create_sorted_array(size)
else:
random_array = MemoryArray.create_random_array(size, -100, 100)
sort_func(random_array)
MemoryManager.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)
if __name__ == '__main__':
sizes = range(10, 101, 5)
analyze_complexity(quick_sort, sizes)
analyze_complexity(quick_sort, sizes, True)

View File

@ -1,15 +0,0 @@
from utils.memory_cell import MemoryCell
class BinaryTreeNode(MemoryCell):
def __init__(self, value):
super().__init__(value)
self.left = None
self.right = None
def __repr__(self):
return f"BinaryTreeNode(value={self.value}, left={self.left}, right={self.right})"
def __str__(self):
return str(self.value)

View File

@ -1,12 +0,0 @@
from utils.memory_array import MemoryArray
from vorlesung.L05_binaere_baeume.avl_tree import AVLTree
if __name__ == "__main__":
a = MemoryArray.create_array_from_file("data/seq0.txt")
tree = AVLTree()
for cell in a:
tree.insert(int(cell))
tree.graph_traversal()

View File

@ -1,12 +0,0 @@
from utils.memory_array import MemoryArray
from vorlesung.L05_binaere_baeume.bin_tree import BinaryTree
if __name__ == "__main__":
a = MemoryArray.create_array_from_file("data/seq0.txt")
tree = BinaryTree()
for cell in a:
tree.insert(int(cell))
tree.graph_traversal()

View File

@ -1,106 +0,0 @@
import math
import unittest
from utils.literal import Literal
from utils.memory_cell import MemoryCell
from utils.memory_array import MemoryArray
from vorlesung.L07_hashtable.hashtable import HashTableOpenAddressing
#Goldener Schnitt
a = Literal((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
# 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
if __name__ == "__main__":
print("*** Aufgabe 3 ***")
size = Literal(20)
print(f"Anlage einer Hash-Tabelle mit offener Adressierung mit Größe {size}")
ht = HashTableOpenAddressing(size, f)
print("Einfügen der Werte aus seq0.txt")
for cell in MemoryArray.create_array_from_file("data/seq0.txt"):
if not ht.insert(cell):
print(f"Einfügen von {cell} nicht möglich")
print(ht)
print(f"Belegungsfaktor: {ht.alpha()}")
with MemoryCell(52) as cell:
print(f"Suche nach {cell}")
if ht.search(cell):
print(f"{cell} gefunden, wird gelöscht.")
ht.delete(cell)
else:
print(f"{cell} nicht gefunden")
print(ht)
print(f"Belegungsfaktor: {ht.alpha()}")
print("Einfügen von 24")
with MemoryCell(24) as cell:
if not ht.insert(cell):
print(f"Einfügen von {cell} nicht möglich")
print(ht)
print(f"Belegungsfaktor: {ht.alpha()}")
print()
print("*** Aufgabe 4 ***")
size = Literal(90)
print(f"Anlage einer Hash-Tabelle mit offener Adressierung mit Größe {size}")
ht = HashTableOpenAddressing(size, f)
print("Einfügen der Werte aus seq1.txt")
for cell in MemoryArray.create_array_from_file("data/seq1.txt"):
if not ht.insert(cell):
print(f"Einfügen von {cell} nicht möglich")
print(ht)
print(f"Belegungsfaktor: {ht.alpha()}")
print()
print("*** Aufgabe 5 ***")
size = Literal(89)
print(f"Anlage einer Hash-Tabelle mit offener Adressierung mit Größe {size}")
ht = HashTableOpenAddressing(size, f)
print("Einfügen der Werte aus seq1.txt")
for cell in MemoryArray.create_array_from_file("data/seq1.txt"):
if not ht.insert(cell):
print(f"Einfügen von {cell} nicht möglich")
print(ht)
print(f"Belegungsfaktor: {ht.alpha()}")
print()
print("*** Aufgabe 6 ***")
size = Literal(90)
print(f"Anlage einer Hash-Tabelle mit offener Adressierung mit Größe {size}")
print("Verwendung der symmetrischen quadratischen Sondierung")
ht = HashTableOpenAddressing(size, fs)
print("Einfügen der Werte aus seq1.txt")
for cell in MemoryArray.create_array_from_file("data/seq1.txt"):
if not ht.insert(cell):
print(f"Einfügen von {cell} nicht möglich")
print(ht)
print(f"Belegungsfaktor: {ht.alpha()}")
print()

View File

@ -1,37 +0,0 @@
import re
from utils.project_dir import get_path
from vorlesung.L08_graphen.graph import Graph, AdjacencyListGraph, AdjacencyMatrixGraph
def read_cave_into_graph(graph: Graph, filename: str):
"""Read a cave description from a file and insert it into the given graph."""
filename = get_path(filename)
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, "data/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)
graph.graph("Höhle")

View File

@ -1,59 +0,0 @@
from utils.project_dir import get_path
from vorlesung.L08_graphen.graph import AdjacencyListGraph
DIRECTIONS = [(1,0), (0, 1), (-1, 0), (0, -1)]
class D16Solution:
def __init__(self):
with open(get_path("data/aoc2416.txt"), "r") as file:
self.puzzle = [line.strip() for line in file]
self.cells = set()
self.start = None
self.end = None
def get_cells_start_end(self):
for row in range(len(self.puzzle)):
for col in range(len(self.puzzle[row])):
if self.puzzle[row][col] != '#':
self.cells.add((col, row))
if self.puzzle[row][col] == 'S':
self.start = (col, row)
if self.puzzle[row][col] == 'E':
self.end = (col, row)
def get_label(self, cell, direction):
"""Generate a label for a cell based on its coordinates and direction."""
return f"{cell[0]},{cell[1]}_{direction}"
def create_graph(self):
graph = AdjacencyListGraph()
for cell in self.cells:
for direction in DIRECTIONS:
label = self.get_label(cell, direction)
graph.insert_vertex(label)
for cell in self.cells:
for d, direction in enumerate(DIRECTIONS):
label = self.get_label(cell, direction)
dx, dy = direction
neighbor = (cell[0] + dx, cell[1] + dy)
if neighbor in self.cells:
graph.connect(label, self.get_label(neighbor, direction))
direction_left = DIRECTIONS[(d - 1) % 4]
direction_right = DIRECTIONS[(d + 1) % 4]
graph.connect(label, self.get_label(cell, direction_left), 1000)
graph.connect(label, self.get_label(cell, direction_right), 1000)
return graph
if __name__ == "__main__":
solution = D16Solution()
solution.get_cells_start_end()
graph = solution.create_graph()
start = solution.get_label(solution.start, DIRECTIONS[0])
print(f"Start: {start}")
distance_map, predecessor_map = graph.dijkstra(start)
end_labels = [solution.get_label(solution.end, direction) for direction in DIRECTIONS]
end_vertices = [graph.get_vertex(label) for label in end_labels]
min_weight = min([distance_map[vertex] for vertex in end_vertices])
print(f"Minimum distance to End {solution.end}: {min_weight}")

View File

@ -1,32 +0,0 @@
from vorlesung.L08_graphen.graph import Graph, AdjacencyMatrixGraph
from utils.project_dir import get_path
import re
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, get_path("data/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}")

View File

@ -1,4 +1,3 @@
matplotlib matplotlib
numpy numpy
pygame pygame
graphviz

View File

@ -107,7 +107,7 @@ def partitionIterative(arr : MemoryArray, l : Literal, h : Literal, mode=0):
# Carefull that we do not use a reference. I suppose python would return one here if we just assign without value>Literal cast. # Carefull that we do not use a reference. I suppose python would return one here if we just assign without value>Literal cast.
# At least this helped fix weird issue # At least this helped fix weird issue
pivotValue : Literal = arr[h] pivotValue : Literal = Literal(arr[h].value)
i : MemoryCell = MemoryCell(l.pred()) i : MemoryCell = MemoryCell(l.pred())
for j in mrange(l, h): for j in mrange(l, h):
@ -115,7 +115,7 @@ def partitionIterative(arr : MemoryArray, l : Literal, h : Literal, mode=0):
i += Literal(1) # increment index of smaller element i += Literal(1) # increment index of smaller element
swap(arr, int(i), int(j)) swap(arr, int(i), int(j))
swap(arr, i.succ().value, h.value) swap(arr, int(i.succ()), int(h))
return i.succ() return i.succ()
def LEGACY_quickSort(z: MemoryArray, l: Literal = Literal(0), r: Literal = Literal(-1), mode=0): def LEGACY_quickSort(z: MemoryArray, l: Literal = Literal(0), r: Literal = Literal(-1), mode=0):
@ -185,6 +185,6 @@ if __name__ == '__main__':
print(filename) print(filename)
toSort = MemoryArray.create_array_from_file(filename) toSort = MemoryArray.create_array_from_file(filename)
timeMS(quickSortIterative, toSort, Literal(0), toSort.length().pred(), mode=1) timeMS(quickSortIterative, toSort, Literal(0), toSort.length().pred(), mode=1)
print(toSort) # print(toSort)
print("Kann durch die Modifikation eine besser Laufzeit als nlog(n) erreicht werden? Nein! nlog(n) ist das Minimum. Durch die Änderung kann aber der Worst-Case fall von n^2 für z.B. bereits vorsortierte Arrays oder Arrays mit vielen Duplikaten vermieden werden.") print("Kann durch die Modifikation eine besser Laufzeit als nlog(n) erreicht werden? Nein! nlog(n) ist das Minimum. Durch die Änderung kann aber der Worst-Case fall von n^2 für z.B. bereits vorsortierte Arrays oder Arrays mit vielen Duplikaten vermieden werden.")

View File

@ -1,293 +0,0 @@
import logging
logger = logging.getLogger(__name__)
# logging.basicConfig(level=logging.DEBUG)
import time
def timeMS(func, *args, **kwargs):
startTime = time.perf_counter()
result = func(*args, **kwargs)
endTime = time.perf_counter()
elapsedMS = (endTime - startTime) * 1000 # Convert to milliseconds
print(f"{func.__name__} took {elapsedMS:.2f} ms")
return result
from utils.memory_array import MemoryArray
from utils.memory_cell import MemoryCell
from utils.literal import Literal
from utils.constants import MAX_VALUE
from utils.memory_manager import MemoryManager
from utils.memory_range import mrange
# Impl of MemoryArray says we cant add our own Datatypes beside Literal and List
# BUUUUT we can just wrap our Datatype in a List :-)
# We store them in a MemoryArray internaly tho anyhow so we increment our Counters for the RAM
class HeapEntry:
def __init__(self, item, priority=1):
self.data = MemoryArray(Literal(2))
# 0: Content, 1: Prio
self.data[Literal(0)] = Literal(item)
self.data[Literal(1)] = Literal(priority)
def getItem(self):
return self.data[Literal(0)]
def getPriority(self):
return self.data[Literal(1)]
def setPriority(self, priority):
self.data[Literal(1)] = Literal(priority)
def __lt__(self, other):
if other is None:
return True
if isinstance(other, (int, float)):
return self.getPriority().value > other
return self.getPriority() > other.getPriority()
def __gt__(self, other):
if other is None:
return False
if isinstance(other, (int, float)):
return self.getPriority().value < other
return self.getPriority() < other.getPriority()
def __eq__(self, other):
return self.getPriority() == other.getPriority()
def __str__(self):
return f"({self.getItem()}, prio={self.getPriority()})"
class PriorityQueue:
def __init__(self, max_size : Literal = Literal(100)):
self.heap = MemoryArray(max_size)
# Add uninitialized HeapEntry Values so the Adds/Compares do not fail on emtpy stack.
# Would have to switch to MIN_VALUE if we switch what is a "Higher" Prio
for i in mrange(max_size.value):
self.heap[i].set([HeapEntry(MAX_VALUE, MAX_VALUE)])
self.size = MemoryCell(0)
def parent(self, i: Literal) -> Literal:
return MemoryCell(i.pred()) // Literal(2)
def leftChild(self, i: Literal) -> Literal:
return MemoryCell(MemoryCell(2) * i) + Literal(1)
def rightChild(self, i: Literal) -> Literal:
return MemoryCell(MemoryCell(2) * i) + Literal(2)
# Swap the Lists -> Therefore get the value which is the List and then Set it again
def swap(self, i: Literal, j: Literal):
tmp_i = self.heap[i].value
tmp_j = self.heap[j].value
self.heap[i].set(tmp_j)
self.heap[j].set(tmp_i)
def maxHeapify(self, i: Literal):
left = self.leftChild(i)
right = self.rightChild(i)
largest = i
if left < Literal(self.size.value) and self.heap[left].value[0] > self.heap[largest].value[0]:
largest = left
if right < Literal(self.size.value) and self.heap[right].value[0] > self.heap[largest].value[0]:
largest = right
if largest != i:
self.swap(i, largest)
self.maxHeapify(largest)
def insert(self, entry : HeapEntry):
if self.size >= self.heap.length():
raise IndexError("Heap full")
i = self.size
self.heap[i].set([entry])
while i > Literal(0) and self.heap[self.parent(i)].value[0] < self.heap[i].value[0]:
self.swap(i, self.parent(i))
i = self.parent(i)
self.size += Literal(1)
def pop(self):
if self.isEmpty():
raise IndexError("Queue is empty!")
max_item = self.heap[Literal(0)].value[0]
self.heap[Literal(0)] = self.heap[self.size - Literal(1)]
self.size -= Literal(1)
self.maxHeapify(Literal(0))
return max_item
def peek(self):
if self.isEmpty():
raise IndexError("Queue is empty")
return self.heap[Literal(0)].value[0]
def isEmpty(self):
return self.size == Literal(0)
def __len__(self):
return self.size
def __str__(self):
entries = []
for i in mrange(self.size.value):
entry = self.heap[i].value[0]
if entry.getItem() != MAX_VALUE:
entries.append(str(entry))
return "[" + ", ".join(entries) + "]"
# Insert here so we dont run into import problems, but can deliver this file Standalone
class BinaryTreeNode(MemoryCell):
def __init__(self, value):
super().__init__(value)
self.left = None
self.right = None
def __repr__(self):
return f"BinaryTreeNode(value={self.value}, left={self.left}, right={self.right})"
def __str__(self):
return str(self.value)
class BinaryTree:
def __init__(self):
self.root: BinaryTreeNode | None = None
def insert(self, value: BinaryTreeNode):
# Insert at Leaf, if smaller then left one, otherwise right one
def _insert(node: BinaryTreeNode | None, value) -> BinaryTreeNode:
if node is None:
return BinaryTreeNode(value)
if value < node:
node.left = _insert(node.left, value) # type: ignore -> Ignoring pywright errors
else:
node.right = _insert(node.right, value) # type: ignore -> Ignoring pywright errors
return node
self.root = _insert(self.root, value)
def traverse(self, mode="in", visual=False):
mode = mode.lower()
# Have internal depth counting
def InternalTraverse(node, prefix="", is_left=True, depth=0):
if node is None:
return [] if not visual else []
result = []
node_str = str(node)
prefixAcc = prefix + ("| " if is_left and depth > 0 else " ")
if visual:
connector = "+-- " if is_left else "L-- "
line = prefix + connector + node_str if depth > 0 else node_str
result.append(line)
else:
result.append(node_str)
if mode == "pre":
result += InternalTraverse(node.left, prefixAcc, True, depth + 1)
result += InternalTraverse(node.right, prefixAcc, False, depth + 1)
elif mode == "in":
result += InternalTraverse(node.left, prefixAcc, True, depth + 1)
result += InternalTraverse(node.right, prefixAcc, False, depth + 1)
elif mode == "post":
result += InternalTraverse(node.left, prefixAcc, True, depth + 1)
result += InternalTraverse(node.right, prefixAcc, False, depth + 1)
return result
if self.root is None:
return "(empty tree)" if visual else []
result = InternalTraverse(self.root)
return "\n".join(result) if visual else result
def levelOrderWithPriorityQueue(self):
if not self.root:
return []
# Create a priority queue, using a reduced prio for every new entry -> behaviour as regular queue FIFO
pq = PriorityQueue(Literal(1000))
# Again we cannot create a MemoryArray of dynamic sizes and also cannot create a string as MemoryCell does not like it
# Again we just create a list holding a single dummy Entry (to set its size to 1) and then just use this "list" as our string
# Appending to it is easy as it is just a regular list and in the end we return it
# Like MemoryCell("").value.append("STRING") will fail. But list-wrap works.
#
# Sorry for Syntax, dont know any better way to have everything as RAM-Managed memory:-(
result = MemoryArray(["MYSTRING"])
result[Literal(0)].set([]);
counter = MemoryCell(0)
def nextPriority():
val = counter.value
counter.set(Literal(val + 1))
return val
pq.insert(HeapEntry([self.root], nextPriority()))
while not pq.isEmpty():
entry = pq.pop()
node = entry.getItem().value
result[Literal(0)].value.append(str(node[0]))
if node[0].left:
pq.insert(HeapEntry([node[0].left], nextPriority()))
if node[0].right:
pq.insert(HeapEntry([node[0].right], nextPriority()))
return result[Literal(0)]
def __str__(self):
return str(self.traverse(mode="PrE", visual=True))
def analyze_complexity(fn, sizes):
"""
Analysiert die Komplexität einer maximalen Teilfolgenfunktion.
:param max_sequence_func: Die Funktion, die analysiert wird.
:param sizes: Eine Liste von Eingabegrößen für die Analyse.
"""
for size in sizes:
MemoryManager.purge() # Speicher zurücksetzen
random_array = MemoryArray.create_random_array(size, -100, 100)
fn(random_array, Literal(0), random_array.length().pred())
MemoryManager.save_stats(size)
MemoryManager.plot_stats(["cells", "adds", "compares", "reads", "writes"])
if __name__ == '__main__':
# For debug, assert if working and complexity-analysis
# example()
print("Sorry for the Syntax and the large file, tried to keep everything as a standalone file to help make it \" download and run \".\n \
Also did - once again - not find a better way to have a queue managed by the RAM contain the values of non-integer-attributes I \n\
needed it to. Therefore i reused my Priorityqueue and its accesses via the unspecified wrapped list.");
# for filename in ["data/seq0.txt", "data/seq1.txt", "data/seq2.txt" ,"data/seq3.txt"]:
for filename in [ "data/seq0.txt"]:
print(filename)
binTreeData = MemoryArray.create_array_from_file(filename)
binTree = BinaryTree()
for value in binTreeData:
binTree.insert(BinaryTreeNode(value))
# Print overlaoded InOrder traversal
print(binTree)
# print(binTree.traverse(mode="pre", visual=False))
# print(binTree.traverse(mode="in", visual=False))
# print(binTree.traverse(mode="post", visual=False))
# Print Levelorder traversal:
print(binTree.levelOrderWithPriorityQueue())

View File

@ -1,246 +0,0 @@
import logging
logger = logging.getLogger(__name__)
# logging.basicConfig(level=logging.DEBUG)
import time
def timeMS(func, *args, **kwargs):
startTime = time.perf_counter()
result = func(*args, **kwargs)
endTime = time.perf_counter()
elapsedMS = (endTime - startTime) * 1000 # Convert to milliseconds
print(f"{func.__name__} took {elapsedMS:.2f} ms")
return result
from utils.memory_array import MemoryArray
from utils.literal import Literal
from utils.memory_manager import MemoryManager
from vorlesung.L05_binaere_baeume.bin_tree import BinaryTree
from vorlesung.L05_binaere_baeume.bin_tree_node import BinaryTreeNode
def analyze_complexity(fn, sizes):
"""
Analysiert die Komplexität einer maximalen Teilfolgenfunktion.
:param max_sequence_func: Die Funktion, die analysiert wird.
:param sizes: Eine Liste von Eingabegrößen für die Analyse.
"""
for size in sizes:
MemoryManager.purge() # Speicher zurücksetzen
random_array = MemoryArray.create_random_array(size, -100, 100)
fn(random_array, Literal(0), random_array.length().pred())
MemoryManager.save_stats(size)
MemoryManager.plot_stats(["cells", "adds", "compares", "reads", "writes"])
lineAccumulator = []
# Returnvalue does not get forwarded so we can not work with return.
# Will try glob vars to append the string
# Signature: def print_node(node, indent=0, line=None):
def clbk_graphvizify(toDecorate : BinaryTreeNode, indent=0, line=None):
global lineAccumulator
if isinstance(toDecorate, AVLTreeNode):
lineAccumulator.append(f'n_{id(toDecorate)} [label=<{toDecorate.value}<BR/><FONT COLOR="RED" POINT-SIZE="10.0" FACE="ambrosia">B: {toDecorate.balanceFactor}</FONT>>]')
else:
lineAccumulator.append(f"n_{id(toDecorate)} [label={toDecorate.value}]")
# Create edges for nodes with Child (use l - r)
if toDecorate.left is not None:
lineAccumulator.append(f"n_{id(toDecorate)} -> n_{id(toDecorate.left)}")
if toDecorate.right is not None:
lineAccumulator.append(f"n_{id(toDecorate)} -> n_{id(toDecorate.right)}")
def graphvizify() -> str:
# Header
result = "digraph {\n\t"
# Body
result += ('\n\t'.join(str(item) for item in lineAccumulator))
# Footer
result += "\n}"
return result
class AVLTreeNode(BinaryTreeNode):
def __init__(self, value):
super().__init__(value)
self.parentRef = None
# Start balanced as we probably have no children right after insert
self.balanceFactor = Literal(0)
def rightRotate(self, node) -> 'AVLTreeNode|None':
if node is None:
return None
oLeft = node.left;
oLeft.parentRef = node.parentRef;
node.left = oLeft.right;
if node.left is not None:
node.left.parentRef = node;
oLeft.right = node;
node.parentRef = oLeft;
if oLeft.parentRef is not None:
if oLeft.parentRef.right is node:
oLeft.parentRef.right = oLeft;
elif oLeft.parentRef.left is node:
oLeft.parentRef.left = oLeft;
node.getSetBalanceFactor()
oLeft.getSetBalanceFactor()
return oLeft
def leftRotate(self, node) -> 'AVLTreeNode|None':
if node is None:
return None
oRight = node.right
oRight.parentRef = node.parentRef
node.right = oRight.left
if node.right is not None:
node.right.parentRef = node
oRight.left = node
node.parentRef = oRight
if oRight.parentRef is not None:
if oRight.parentRef.right is node:
oRight.parentRef.right = oRight
elif oRight.parentRef.left is node:
oRight.parentRef.left = oRight
node.getSetBalanceFactor()
oRight.getSetBalanceFactor()
return oRight
def getSetBalanceFactor(self) -> Literal:
leftHeight = self.left.height() if self.left else 0
rightHeight = self.right.height() if self.right else 0
self.balanceFactor = Literal(rightHeight - leftHeight)
return self.balanceFactor
def rightLeftRotate(self, node) -> 'AVLTreeNode|None':
node.right = self.rightRotate(node.right)
return self.leftRotate(node)
def leftRightRotate(self, node) -> 'AVLTreeNode|None':
node.left = self.leftRotate(node.left)
return self.rightRotate(node)
def debugTraverse(node, source=1):
if node is None:
return None
logger.debug(f"{node.value} {node.getSetBalanceFactor()} {source}")
debugTraverse(node.left, 10);
debugTraverse(node.right, 20);
class AVLTree(BinaryTree):
# @override
def new_node(self, value) -> AVLTreeNode:
return AVLTreeNode(value)
def balanceAVLTree(self, node : AVLTreeNode):
# balance < -1 means imbalance to the left, > 1 means imbalance to the right
logger.debug("in")
if node is None:
return None
logger.debug("out")
node.getSetBalanceFactor()
logger.debug(f"Parent Balancing for {node.value} -> {node.balanceFactor} {node.left.height() if node.left else None} and {node.right.height() if node.right else None}")
# imbalance to left -> If we enter this we cannot LOGICALLY have a left=None node -> No need to chekc
if node.balanceFactor < Literal(-1):
# Left-Left
if node.left.balanceFactor <= Literal(0): # type: ignore -> Ignoring pywright error, see comment above
# Wow, this syntax is sketchy ^^
# TODO Maybe declare as static if python supports this? Or just leaf param be?
logger.debug("rr")
node = node.rightRotate(node)
# Left-Right
else:
# TODO Maybe declare as static if python supports this? Or just leaf param be?
logger.debug("lrr")
node = node.leftRightRotate(node)
# Right heavy
# imbalance to right -> If we enter this we cannot LOGICALLY have a right=None node -> No need to chekc
if node.balanceFactor > Literal(1):
# Right-Right case
if node.right.balanceFactor >= Literal(0): # type: ignore -> Ignoring pywright error, see comment above
# TODO Maybe declare as static if python supports this? Or just leaf param be?
logger.debug("lr")
node = node.leftRotate(node)
# Right-Left case
else:
# TODO Maybe declare as static if python supports this? Or just leaf param be?
logger.debug("rlr")
node = node.rightLeftRotate(node)
logger.debug(f"Reached {node.parentRef}")
if node.parentRef is not None:
logger.debug(f"Calling again for {node.parentRef.value}");
self.balanceAVLTree(node.parentRef);
else:
self.root = node;
# Node is balanced
return node
# @override
def insert(self, value):
node, parent = super().insert(value)
# NOTE Python does not have a Problem with NOT tellin us that we override something important
# or something that does not exist.... This Makes for AWESOME debugging .... ... ...
node.parentRef = parent
if parent:
node = self.balanceAVLTree(node.parentRef)
return node, parent
if __name__ == '__main__':
tree = AVLTree()
### Force RR
# testData = [30, 20, 10];
# for value in testData:
# tree.insert(MemoryCell(value));
### Force LR
# testData = [10, 20, 30];
# for value in testData:
# tree.insert(MemoryCell(value));
### Force LRR
# testData = [30, 10, 20]
# for value in testData:
# tree.insert(MemoryCell(value))
### Force RLR
# testData = [10, 30, 20]
# for value in testData:
# tree.insert(MemoryCell(value))
# Force rebuild of our balanceFactor indices...
# debugTraverse(tree.root)
binTreeData = MemoryArray.create_array_from_file("data/seq0.txt")
for value in binTreeData:
tree.insert(value)
lineAccumulator.clear();
tree.in_order_traversal(clbk_graphvizify)
# tree.tree_structure_traversal(clbk_graphvizify)
print(graphvizify())

View File

@ -1,334 +0,0 @@
import logging
from graphviz import Digraph
logger = logging.getLogger(__name__)
# logging.basicConfig(level=logging.DEBUG)
import time
def timeMS(func, *args, **kwargs):
startTime = time.perf_counter()
result = func(*args, **kwargs)
endTime = time.perf_counter()
elapsedMS = (endTime - startTime) * 1000 # Convert to milliseconds
print(f"{func.__name__} took {elapsedMS:.2f} ms")
return result
from utils.memory_array import MemoryArray
from utils.memory_cell import MemoryCell
from utils.literal import Literal
from utils.memory_range import mrange
from utils.memory_manager import MemoryManager
# We often want to "dynamically" extend our array, but thats not really what it was intended for
# Therefore we write a function to do so reusably
def memArrayInsert(array: MemoryArray, position: Literal, value) -> MemoryArray:
logger.debug(f"Pre-Insert: {array}")
newSize : Literal = array.length().succ()
newArray = MemoryArray(newSize)
# Copy elements til position
for i in mrange(position):
newArray[i] = array[i]
# Insert new value with check for MemCell Type
newArray[position] = value if isinstance(value, MemoryCell) else MemoryCell(value)
for i in mrange(position.succ(), newSize):
newArray[i] = array[i.pred()]
logger.debug(f"Post-Insert: {array}")
return newArray
# Likewise for the Split
# Here we split the array at the position EXCLUSIVELY for the first MemoryArray
def memArraySplit(array: MemoryArray, position: Literal) -> tuple[MemoryArray, MemoryArray]:
leftSize = position
rightSize = MemoryCell(array.length()) - position
left = MemoryArray(leftSize)
right = MemoryArray(rightSize)
for i in mrange(leftSize):
left[i] = array[i]
for i in mrange(rightSize):
right[i] = array[Literal(i.value + position.value)]
return left, right
class BTreeNode(MemoryCell):
def __init__(self):
super().__init__(value=None)
self.value = MemoryArray([])
self.children = MemoryArray([])
self.leaf = True
def __str__(self):
return "( " + " ".join(str(val) for val in self.value) + " )"
def isLeaf(self):
return self.children.length() == Literal(0)
class BTree:
def __init__(self, order):
self.order = Literal(order)
self.root = BTreeNode()
def _insertNonFull(self, node, key):
if node.leaf:
i = node.value.length()
# pos for new Key
while i > Literal(0) and key < node.value[i.pred()]:
i = i.pred()
# insert key at pos using helper fkt
node.value = memArrayInsert(node.value, i, key)
else:
j = Literal(0)
while j < node.value.length() and node.value[j] < key:
j = j.succ()
child = node.children[j].value[0]
# Splt if full
if child.value.length() == Literal(2 * self.order.value - 1):
self._splitChild(node, j)
if key > node.value[j]:
j = j.succ()
child = node.children[j].value[0]
# insert rec in selected Child
self._insertNonFull(child, key)
def insert(self, key):
if not isinstance(key, MemoryCell):
key = MemoryCell(key)
rootRef = self.root
if rootRef.value.length() == MemoryCell(2 * self.order.value - 1):
newRoot = BTreeNode()
newRoot.leaf = False
newRoot.children = MemoryArray(["DUMMY"])
newRoot.children[Literal(0)].set([rootRef])
self._splitChild(newRoot, Literal(0))
self.root = newRoot
self._insertNonFull(newRoot, key)
else:
logger.debug("Inserting in non-full");
self._insertNonFull(rootRef, key)
def _splitChild(self, parent: BTreeNode, index: Literal):
child = parent.children[index].value[0]
newRight = BTreeNode()
newRight.leaf = child.leaf
# median index
mid = Literal(self.order.value - 1)
medianKey = child.value[mid]
# Childs keys in l and r
leftKeys, rightKeys = memArraySplit(child.value, mid)
child.value = leftKeys
newRight.value = MemoryArray(rightKeys.length().pred())
for j in mrange(rightKeys.length().pred()):
newRight.value[j] = rightKeys[j.succ()]
# if child is not leaf distribute itschildren
if not child.leaf:
leftChildren, rightChildren = memArraySplit(child.children, mid.succ())
child.children = leftChildren
newRight.children = rightChildren
# Insert median key in parent and insert new right as i+1's chld
parent.value = memArrayInsert(parent.value, index, medianKey)
parent.children = memArrayInsert(parent.children, index.succ(), [newRight])
def search(self, key) -> BTreeNode:
return BTreeNode()
# Depth-Search
def _traversalInOrder(self, node : BTreeNode, result):
logger.debug(type(node.value))
logger.debug(node.value)
for i in mrange(node.value.length()):
# RecTerm
if not node.isLeaf():
self._traversalInOrder(node.children[i].value[0], result)
result.append(str(node.value[i]))
# RecTerm
if not node.isLeaf():
self._traversalInOrder(node.children[Literal(len(node.value))].value[0], result)
def structureTraversal(self, callback):
def traverse(node):
if node is None:
return
callback(node)
if not node.isLeaf():
for i in range(len(node.children)):
child = node.children[Literal(i)].value[0]
traverse(child)
traverse(self.root)
def traversal(self) -> list:
resultAcc = []
self._traversalInOrder(self.root, resultAcc)
return resultAcc
def search(self, searchVal : Literal) -> BTreeNode|None:
if self.root is None:
return None
current = self.root
while not current.isLeaf():
# Exit early if we are already way to large for our current Node. Should improve runtime
if searchVal > current.value[current.value.length().pred()]:
current = current.children[current.value.length()].value[0]
continue
# Go thru every value in the Node until we find anything
i = MemoryCell(0)
while i < current.value.length() and searchVal > current.value[i]:
i = i.succ()
# return exact match
if i < current.value.length() and searchVal == current.value[i]:
return current
# go to appropriate child
# If searchVal smaller than first value (i=0) -> leftmost child
# if searchVal larger than all values (i=len) -> rightmost child
# Otherwise -> child between values
current = current.children[Literal(i)].value[0]
# Final check in leaf node
for i in mrange(current.value.length()):
if searchVal == current.value[i]:
return current
return None
def height(self) -> Literal:
if self.root is None:
return Literal(0)
current = self.root
height = MemoryCell(1)
while not current.isLeaf():
height = height.succ()
current = current.children[Literal(0)].value[0]
return height
def clbk_graphvizify(toDecorate: BTreeNode, indent=0, line=None):
global dotAcc
values_str = "|".join(str(val) for val in toDecorate.value)
dotAcc.node(f'n_{id(toDecorate)}', values_str, shape='record')
# Create edges for all children
if not toDecorate.isLeaf():
for i in mrange(toDecorate.children.length()):
child = toDecorate.children[i].value[0]
dotAcc.edge(f'n_{id(toDecorate)}', f'n_{id(child)}')
def visualizeBTree(tree: BTree, filename='build/schoeffel_btree'):
try:
import graphviz
except ImportError:
raise AssertionError("Graphviz installed? Try commenting visualizeBTree or install 'pip install graphviz'")
global dotAcc
dotAcc = Digraph()
dotAcc.attr(rankdir='TB')
dotAcc.attr('node', shape='record', style='filled', fillcolor='lightgray')
dotAcc.attr('edge', arrowsize='0.5')
tree.structureTraversal(clbk_graphvizify)
try:
dotAcc.render(filename, view=True)
except Exception as e:
print(f"Could not display graph: {e}")
print("Saving graph file without viewing (Running WSL?)")
try:
dotAcc.render(filename, view=False)
except Exception as e:
print(f"Could not save graph file: {e}")
def graphvizify() -> str:
result = """digraph {
rankdir=TB;
node [shape=record, style=filled, fillcolor=lightgray];
edge [arrowsize=0.5];
"""
# Body
result += '\n\t'.join(str(item) for item in dotAcc)
# Footer
result += "\n}"
return result
def analyze_complexity(fn, sizes):
"""
Analysiert die Komplexität einer maximalen Teilfolgenfunktion.
:param max_sequence_func: Die Funktion, die analysiert wird.
:param sizes: Eine Liste von Eingabegrößen für die Analyse.
"""
for size in sizes:
MemoryManager.purge() # Speicher zurücksetzen
random_array = MemoryArray.create_random_array(size, -100, 100)
for value in random_array:
fn(value)
MemoryManager.save_stats(size)
MemoryManager.plot_stats(["cells", "compares", "reads", "writes"])
if __name__ == '__main__':
tree = BTree(order=3)
tree.insert(Literal(10));
tree.insert(Literal(11));
tree.insert(Literal(12));
print(tree.traversal());
binTreeData = MemoryArray.create_array_from_file("data/seq0.txt")
j = 0
for value in binTreeData:
tree.insert(value)
j = j +1
# Uncomment to view progress in insertion at every step saved as PDF
# visualizeBTree(tree, f"build/schoeffel_btree{j}")
logger.debug(tree.root.children)
# Graphvizify Wrapper
visualizeBTree(tree)
print(f"InOrder Traversal: {tree.traversal()}")
print(tree.search(Literal(50)))
print(tree.height())
tree3 = BTree(order=3)
tree5 = BTree(order=5)
binTreeData = MemoryArray.create_array_from_file("data/seq2.txt")
for value in binTreeData:
tree3.insert(value)
tree5.insert(value)
print(f"Order three for Seq2 produces height {tree3.height()}")
print(f"Order five for Seq2 produces height {tree5.height()}")
order3 = tree3.traversal()
order5 = tree5.traversal()
assert all(int(order3[i]) <= int(order3[i+1]) for i in range(len(order3)-1)), "Order3 not in ascending order"
assert all(int(order5[i]) <= int(order5[i+1]) for i in range(len(order5)-1)), "Order5 not in ascending order"
print(tree3.search(Literal(0)))
print(tree5.search(Literal(0)))
MemoryManager.purge()
analyze_complexity(tree3.insert, [10, 20, 30, 40, 50, 60, 70, 80, 90, 100])
# tree.tree_structure_traversal(clbk_graphvizify)
# print(graphvizify())

View File

@ -1,266 +0,0 @@
import logging
logger = logging.getLogger(__name__)
# logging.basicConfig(level=logging.DEBUG)
import time
def timeMS(func, *args, **kwargs):
startTime = time.perf_counter()
result = func(*args, **kwargs)
endTime = time.perf_counter()
elapsedMS = (endTime - startTime) * 1000 # Convert to milliseconds
print(f"{func.__name__} took {elapsedMS:.2f} ms")
return result
from utils.memory_array import MemoryArray
from utils.memory_cell import MemoryCell
from utils.literal import Literal
from utils.memory_range import mrange
from utils.memory_manager import MemoryManager
def getHashFunction(m : Literal):
# Hash function using golden ratio
# Golden ratio (sqrt(5) - 1) / 2; Allowed to to this way as it is a one-time calc ^^
A : Literal = Literal((5 ** 0.5 - 1) / 2)
def hashFunctionGoldenRatio(key : Literal|MemoryCell) -> Literal:
if not isinstance(key, MemoryCell):
key = MemoryCell(key)
assert(isinstance(key, MemoryCell))
product : Literal = key * A
# discard decimal part
intPart = Literal(int(product.value))
fracPart = MemoryCell(product) - intPart
# Scale by m
hashVal = Literal(int(fracPart.value * m.value))
return hashVal
return hashFunctionGoldenRatio
# Probing function h + i + 5i^2
def getProbingFunction(hashFunc, m: Literal, probingMethod="quadratic"):
def quadraticProbe(key, i: Literal|MemoryCell):
if not isinstance(i, MemoryCell):
i = MemoryCell(i)
# TODO Can save operation/allocation of MemCEll here, but for better flow leave this way
hPrime_x = MemoryCell(hashFunc(key))
iSquared = MemoryCell(i * i)
result = MemoryCell(MemoryCell(hPrime_x + i) + iSquared * Literal(5)) % m
return result
def symmetricProbe(key, i: Literal|MemoryCell):
if not isinstance(i, MemoryCell):
i = MemoryCell(i)
hPrime_x = MemoryCell(hashFunc(key))
iSquared = MemoryCell(i * i)
if i % Literal(2) == Literal(0):
result = MemoryCell(hPrime_x + iSquared) % m
else:
result = MemoryCell(hPrime_x - iSquared) % m
return result
if probingMethod == "symmetric":
return symmetricProbe
else:
return quadraticProbe
class HashTable:
# marker for deleted entries
DELETED = MemoryCell("DELETED")
def __init__(self, size, probingMethod="quadratic"):
if not isinstance (size, Literal|MemoryCell):
size = Literal(size)
self.size = size
self.table = MemoryArray(self.size)
for i in mrange(self.size):
self.table[i].set(None)
self.count = Literal(0) # Count of actual elements
self.deletedCount = Literal(0) # Count of deleted slots
# Initialize hash and probe function
self.hashFunc = getHashFunction(self.size)
self.probe = getProbingFunction(self.hashFunc, self.size, probingMethod)
def insert(self, value):
if not isinstance(value, MemoryCell):
value = MemoryCell(value)
# Check if table is full (considering deleted entries asd well as normal count)
if (MemoryCell(self.count) + self.deletedCount) == self.size:
logger.info(f"Failed to insert {value} - table is full")
return False
i = Literal(0)
while i < self.size:
# Get our Index via the ProbingFkt
index = self.probe(value, i)
# Empty slot or deleted marker found
# Need the get here as we want to compare the value (none) to None.
# For the rest normal should be fine
if self.table[index].get() is None or self.table[index] == self.DELETED:
if self.table[index] == self.DELETED:
# Were on a cell that was deleted. Now we write in it, but decrease delCount
self.deletedCount = self.deletedCount.pred()
self.table[index] = value
self.count = self.count.succ() # Inc fillcounter
logger.debug(f"value {value} was inserted successfully at {index}, count is now at \
{self.count}|{self.deletedCount}");
return True
# Value already exists
# TODO DISCUSSION Wouldnt it defcato be a successfull insert if we are already in our hashmap? Maybe then return true here?
if self.table[index] == value:
logger.info(f"value {value} already exits in the table");
return False
i = i.succ()
# Couldn't find a slot even though count < size
logger.info(f"Failed to insert {value} - table is full")
return False
def search(self, value):
if not isinstance(value, MemoryCell):
value = MemoryCell(value)
i = Literal(0)
while i < self.size:
index = self.probe(value, i)
# Empty slot found - value doesn't exist
if self.table[index].get() is None:
return False
# Value found
if self.table[index] == value:
return True
# If deleted marker or different value, continue
i = i.succ()
return False
def delete(self, value):
if not isinstance(value, MemoryCell):
value = MemoryCell(value)
i = Literal(0)
while i < self.size:
index = self.probe(value, i)
# Empty slot found - value doesn't exist
if self.table[index].get() is None:
return False
# Value found - mark as deleted
if self.table[index] == value:
# TODO Check wheter it is .set() or = here
self.table[index].set(self.DELETED)
self.count = self.count.pred()
self.deletedCount = self.deletedCount.succ()
return True
i = i.succ()
return False
def __str__(self):
result = "["
for i in mrange(self.size):
if self.table[i].get() is None:
result += "None"
else:
result += str(self.table[i])
if i < self.size.pred():
result += ", "
result += "]"
return result
def alpha(self):
# load factor (count / size)
return Literal(self.count.value / self.size.value)
if __name__ == "__main__":
table = HashTable(20)
seq0Data = MemoryArray.create_array_from_file("data/seq0.txt")
print(f"Hash table before inserting seq0.txt: {table}")
print("Inserting values from seq0.txt...")
for value in seq0Data:
table.insert(value)
strFirst = str(table.table[Literal(0)])
# delete first 5 entries and then try to reinsert them
for i in mrange(5):
table.delete(seq0Data[i])
assert(table.deletedCount == Literal(5)), "Deleted count should be 5"
for i in mrange(5):
table.insert(seq0Data[i])
assert(table.deletedCount == Literal(0)), "Deleted count should be 0 after reinserting"
assert(strFirst == str(table.table[Literal(0)])), "First entry should be the same as before"
print(f"Hashtable after inserting seq0.txt: {table}")
print(f"Load factor after inserting: {table.alpha()}")
print("Deleting 52...")
table.delete(Literal(52))
print(f"Hash table after deletion: {table}")
print(f"New load factr: {table.alpha()}")
print("\nInserting value 24...")
table.insert(Literal(24))
print(f"Hash table after inserting 24: {table}")
table90 = HashTable(90)
seq1Data = MemoryArray.create_array_from_file("data/seq1.txt")
print(f"Inserting values from seq1.txt into table with size 90...")
insertedCount = Literal(0)
for value in seq1Data:
if table90.insert(value):
insertedCount = insertedCount.succ()
print(f"Successfully inserted {insertedCount} out of {Literal(len(seq1Data))} values")
print(f"Load factor: {table90.alpha()}")
table89 = HashTable(89)
print(f"Inserting values from seq1.txt into table with 89...")
insertedCount = Literal(0)
for value in seq1Data:
if table89.insert(value):
insertedCount = insertedCount.succ()
print(f"Successfully inserted {insertedCount} out of {Literal(len(seq1Data))} values")
print(f"Load factor: {table89.alpha()}")
tableSymmetric = HashTable(90, probingMethod="symmetric")
print(f"Inserting values from seq1.txt into table with size 90 and symmetric probing...")
insertedCount = Literal(0)
for value in seq1Data:
if tableSymmetric.insert(value):
insertedCount = insertedCount.succ()
print(f"Successfully inserted {insertedCount} out of {Literal(len(seq1Data))} values")
print(f"Load factor: {tableSymmetric.alpha()}")

View File

@ -1,124 +0,0 @@
import logging
from graphviz import Digraph
from collections import deque
logger = logging.getLogger(__name__)
# logging.basicConfig(level=logging.DEBUG)
import time
def timeMS(func, *args, **kwargs):
startTime = time.perf_counter()
result = func(*args, **kwargs)
endTime = time.perf_counter()
elapsedMS = (endTime - startTime) * 1000 # Convert to milliseconds
print(f"{func.__name__} took {elapsedMS:.2f} ms")
return result
from utils.memory_array import MemoryArray
from utils.memory_cell import MemoryCell
from utils.literal import Literal
from utils.memory_range import mrange
from utils.memory_manager import MemoryManager
class Graph:
def __init__(self):
self.adjacencyList = {}
def addEdge(self, node1, node2, bidirectional=True):
if node1 not in self.adjacencyList:
self.adjacencyList[node1] = []
if node2 not in self.adjacencyList:
self.adjacencyList[node2] = []
self.adjacencyList[node1].append(node2)
if bidirectional:
self.adjacencyList[node2].append(node1)
def serialize(self):
return self.adjacencyList
def breadthFirstSearch(self, start, goal, edgesGonePassed = None):
if start not in self.adjacencyList or goal not in self.adjacencyList:
return None, None
# Dont want to have a class for this, set suffices
visited = set()
queue = deque([(start, [start], set())]) # (current_node, path, edgesGoneToGetHere)
while queue:
currentNode, path, edgesGone = queue.popleft()
if currentNode == goal:
return path, edgesGone
if currentNode not in visited:
logger.info(f"visiting {currentNode}")
visited.add(currentNode)
for neighbor in self.adjacencyList[currentNode]:
edge = (currentNode, neighbor)
# We already went this Edge. Read Part3 as not allowing this to happen
edgeReverse = (neighbor, currentNode)
if neighbor not in visited and (edgesGonePassed is None or edge not in edgesGonePassed) and (edgesGonePassed is None or edgeReverse not in edgesGonePassed):
# Pythonic way of saying neighbour, path no next clear neighbour
# and union of edgesGone with current edge
queue.append((neighbor, path + [neighbor], edgesGone | {edge}))
return None, None
def graphvizify(filePath: str, outputFile: str = 'build/hoehleGraph'):
graph = Digraph()
graph.attr(rankdir='TB')
graph.attr('node', shape='circle', style='filled', fillcolor='lightgray')
graph.attr('edge', arrowsize='0.7')
# Reuse the function to also create our Graph... Waste not want not^^
caveGraph = Graph();
with open(filePath, 'r') as f:
for line in f:
line = line.strip()
delimiter = '<>' if '<>' in line else '>' if '>' in line else None
if delimiter:
node1, node2 = map(str.strip, line.split(delimiter))
node1, node2 = map(lambda x: x.strip('"'), map(str.strip, line.split(delimiter)))
graph.edge(f"\"{node1}\"", f"\"{node2}\"", dir='none' if delimiter == '<>' else None)
caveGraph.addEdge(node1, node2, (delimiter == '<>'));
try:
graph.render(outputFile, view=True)
except Exception as e:
print(f"Could not display graph: {e}\n Trying to save without viewing!")
try:
graph.render(outputFile, view=False)
print(f"Your built map should be available here: {outputFile}.pdf")
except Exception as e:
print(f"Could not save graph file: {e}")
return caveGraph
if __name__ == "__main__":
for filename in [ "data/hoehle.txt", "data/hoehleLarger.txt"]:
caveGraph = graphvizify(filename, 'build/hoehleGraph')
start = "Höhleneingang"
goal = "Schatzkammer"
shortestPath, edgesGoneInitial = caveGraph.breadthFirstSearch(start, goal)
logger.debug(caveGraph.adjacencyList)
logger.debug(edgesGoneInitial)
if shortestPath:
print(f"Shortest path from {start} to {goal} is:")
print(" -> ".join(shortestPath))
else:
print(f"No path found from {start} to {goal}.")
returnpath, _ = caveGraph.breadthFirstSearch(goal, start, edgesGoneInitial)
if returnpath:
print(f"Shortest path from {goal} to {start} is:")
print(" -> ".join(returnpath))
else:
print("No path back Home found. Good Luck")
exit(0)

View File

@ -1,60 +0,0 @@
import logging
logger = logging.getLogger(__name__)
with open('2024/data/input16.data') as f:
# with open('2024/data/input16.example') as f:
# Dont need edges, these are irrelevant to us
content = [list(line.strip()[1:-1]) for line in f.readlines()[1:-1]]
directions = {'N': (-1, 0), 'E': (0, 1), 'S': (1, 0), 'W': (0, -1)}
start = next((row, col, 'E') for row in range(len(content)) for col in range(len(content[0])) if content[row][col] == 'S')
end = next((row, col) for row in range(len(content)) for col in range(len(content[0])) if content[row][col] == 'E')
# Python it up^^ (outer loop goes over each cell in grid and continues with non-walls,
# for each valid cell go over all directions and applies costs dependent on turning)
graph = { (row, col, dirName): {(row+nextdirRow, col+nextdirCol, nextDir): (0 if dirName == nextDir else 1000) + 1
for nextDir, (nextdirRow, nextdirCol) in directions.items()
if 0 <= row+nextdirRow < len(content) and 0 <= col+nextdirCol < len(content[0]) and content[row+nextdirRow][col+nextdirCol] != '#'}
for row in range(len(content)) for col in range(len(content[0])) if content[row][col] != '#' for dirName in directions }
# Algo
def dijsktra(graph, start, end):
import heapq # Priority Queue, Do not use own for shorter Code^^
pq, costs, predecessors = [(0, start)], {node: float('inf') for node in graph}, {node: None for node in graph}
costs[start] = 0
while pq:
cost, node = heapq.heappop(pq)
logger.debug(f"Procesing node {node} with current cost {cost}")
if node[:2] == end[:2]: # Stop when reaching the end position, dont care about direction
path = []
while node:
path.append(node)
node = predecessors[node]
logger.debug(f"Final cost: {cost}")
return cost, path[::-1] # Reverse path to get start-to-end order
if cost > costs[node]: continue
for neighbor, edge_cost in graph[node].items():
if (new_cost := cost + edge_cost) < costs[neighbor]:
costs[neighbor], predecessors[neighbor] = new_cost, node
heapq.heappush(pq, (new_cost, neighbor))
logger.debug(f"Relaxing edge {node} -> {neighbor}, edge cost: {edge_cost}, new cost: {new_cost}")
return float('inf'), [] # No path found
def drawGraph(content, path=None):
# Create copy of grid to overlay graph -> No inPlace Stuff
grid = [row[:] for row in content]
if path:
symbols = {'N': '^', 'E': '>', 'S': 'v', 'W': '<'}
for row, col, direction in path:
grid[row][col] = symbols[direction]
return '\n'.join(''.join(row) for row in grid)
costShortest, shortestPath = dijsktra(graph, start, end)
print("Shortest path cost:", costShortest)
print(drawGraph(content, shortestPath))

View File

@ -1,7 +1,7 @@
from utils.memory_array import MemoryArray from utils.memory_array import MemoryArray
from utils.memory_cell import MemoryCell from utils.memory_cell import MemoryCell
from utils.literal import Literal from utils.literal import Literal
from utils.constants import MIN_VALUE from utils.constants import MAX_VALUE
from utils.memory_range import mrange from utils.memory_range import mrange
# Impl of MemoryArray says we cant add our own Datatypes beside Literal and List # Impl of MemoryArray says we cant add our own Datatypes beside Literal and List
@ -28,14 +28,14 @@ class HeapEntry:
return True return True
if isinstance(other, (int, float)): if isinstance(other, (int, float)):
return self.getPriority().value > other return self.getPriority().value > other
return self.getPriority() < other.getPriority() return self.getPriority() > other.getPriority()
def __gt__(self, other): def __gt__(self, other):
if other is None: if other is None:
return False return False
if isinstance(other, (int, float)): if isinstance(other, (int, float)):
return self.getPriority().value < other return self.getPriority().value < other
return self.getPriority() > other.getPriority() return self.getPriority() < other.getPriority()
def __eq__(self, other): def __eq__(self, other):
return self.getPriority() == other.getPriority() return self.getPriority() == other.getPriority()
@ -49,7 +49,7 @@ class PriorityQueue:
# Add uninitialized HeapEntry Values so the Adds/Compares do not fail on emtpy stack. # Add uninitialized HeapEntry Values so the Adds/Compares do not fail on emtpy stack.
# Would have to switch to MIN_VALUE if we switch what is a "Higher" Prio # Would have to switch to MIN_VALUE if we switch what is a "Higher" Prio
for i in mrange(max_size.value): for i in mrange(max_size.value):
self.heap[i].set([HeapEntry(MIN_VALUE, MIN_VALUE)]) self.heap[i].set([HeapEntry(MAX_VALUE, MAX_VALUE)])
self.size = MemoryCell(0) self.size = MemoryCell(0)
def parent(self, i: Literal) -> Literal: def parent(self, i: Literal) -> Literal:
@ -89,13 +89,12 @@ class PriorityQueue:
i = self.size i = self.size
self.heap[i].set([entry]) self.heap[i].set([entry])
self.size += Literal(1)
while i > Literal(0) and self.heap[self.parent(i)].value[0] < self.heap[i].value[0]: while i > Literal(0) and self.heap[self.parent(i)].value[0] < self.heap[i].value[0]:
self.swap(i, self.parent(i)) self.swap(i, self.parent(i))
i = self.parent(i) i = self.parent(i)
self.size += Literal(1)
def pop(self): def pop(self):
if self.isEmpty(): if self.isEmpty():
raise IndexError("Queue is empty!") raise IndexError("Queue is empty!")
@ -120,35 +119,6 @@ class PriorityQueue:
def __len__(self): def __len__(self):
return self.size return self.size
def __str__(self):
entries = []
for i in mrange(self.size.value):
entry = self.heap[i].value[0]
if entry.getItem() != MIN_VALUE:
entries.append(str(entry))
return "[" + ", ".join(entries) + "]"
def testQueueRandom(number: int):
import random
import string
pq = PriorityQueue(Literal(number))
entries = []
for _ in range(number):
value = ''.join(random.choices(string.ascii_uppercase + string.digits, k=3))
priority = random.randint(1, 100)
entry = HeapEntry(value, priority)
entries.append(entry)
pq.insert(entry)
print(pq)
for entry in entries:
print(f"Unprioritized: {entry}")
while not pq.isEmpty():
print(pq.pop())
if __name__ == '__main__': if __name__ == '__main__':
# Proof of Concept # Proof of Concept
@ -173,21 +143,16 @@ if __name__ == '__main__':
assert(not pq.isEmpty()) assert(not pq.isEmpty())
assert(pq.pop() == HeapEntry("A", 1)) assert(pq.pop() == HeapEntry("A", 1))
assert(pq.isEmpty()) assert(pq.isEmpty())
pq.insert(HeapEntry("A", 1))
pq.insert(HeapEntry("C", 3)) pq.insert(HeapEntry("C", 3))
pq.insert(HeapEntry("B", 2)) pq.insert(HeapEntry("B", 2))
pq.insert(HeapEntry("A", 1))
assert(pq.size == Literal(3)) assert(pq.size == Literal(3))
assert(pq.pop() == HeapEntry("C", 3))
assert(pq.pop() == HeapEntry("B", 2))
assert(pq.pop() == HeapEntry("A", 1)) assert(pq.pop() == HeapEntry("A", 1))
assert(pq.pop() == HeapEntry("B", 2))
assert(pq.pop() == HeapEntry("C", 3))
pq.insert(HeapEntry("A", 1)) pq.insert(HeapEntry("A", 1))
pq.insert(HeapEntry("C", 3)) pq.insert(HeapEntry("C", 3))
pq.insert(HeapEntry("B", 2)) pq.insert(HeapEntry("B", 2))
pq.insert(HeapEntry(42, 4)) print(pq.pop())
pq.insert(HeapEntry(42, 1)) print(pq.pop())
pq.insert(HeapEntry("C", 2)) print(pq.pop())
print(pq)
while not pq.isEmpty():
print(pq.pop())
testQueueRandom(100)

View File

@ -21,8 +21,6 @@ class Literal:
def __eq__(self, other): def __eq__(self, other):
"""Vergleicht den Wert mit einem anderen Wert.""" """Vergleicht den Wert mit einem anderen Wert."""
if other is None:
return False
assert isinstance(other, Literal), "Can only compare with Literal or MemoryCell" assert isinstance(other, Literal), "Can only compare with Literal or MemoryCell"
self.compare_count += 1 self.compare_count += 1
self.read_count += 1 self.read_count += 1
@ -31,8 +29,6 @@ class Literal:
def __ne__(self, other): def __ne__(self, other):
"""Vergleicht den Wert der Speicherzelle mit einem anderen Wert.""" """Vergleicht den Wert der Speicherzelle mit einem anderen Wert."""
if other is None:
return True
assert isinstance(other, Literal), "Can only compare with Literal or MemoryCell" assert isinstance(other, Literal), "Can only compare with Literal or MemoryCell"
self.compare_count += 1 self.compare_count += 1
self.read_count += 1 self.read_count += 1

View File

@ -1,7 +1,7 @@
from utils.literal import Literal from utils.literal import Literal
from utils.memory_cell import MemoryCell from utils.memory_cell import MemoryCell
from utils.memory_manager import MemoryManager from utils.memory_manager import MemoryManager
from utils.project_dir import get_path from pathlib import Path
from random import randint from random import randint
class MemoryArray: class MemoryArray:
@ -88,7 +88,9 @@ class MemoryArray:
@staticmethod @staticmethod
def create_array_from_file(filename, limit=None): def create_array_from_file(filename, limit=None):
"""Erzeugt ein Speicherarray aus einer Datei.""" """Erzeugt ein Speicherarray aus einer Datei."""
filename = get_path(filename) this_dir = Path(__file__).resolve().parent
project_dir = this_dir.parent
filename = project_dir / filename
with open(filename) as f: with open(filename) as f:
lines = f.readlines() lines = f.readlines()
if limit is not None: if limit is not None:
@ -100,13 +102,6 @@ class MemoryArray:
a.reset_counters() a.reset_counters()
return a return a
def __str__(self):
result = "[ "
for cell in self.cells:
result += str(cell) + ", "
result += "]"
return result
if __name__ == "__main__": if __name__ == "__main__":
import random import random
@ -120,7 +115,3 @@ if __name__ == "__main__":
s += cell s += cell
print(s) print(s)
print(f"Anzahl der Additionen: {MemoryManager.count_adds()}") print(f"Anzahl der Additionen: {MemoryManager.count_adds()}")
a = MemoryArray.create_array_from_file("data/seq0.txt")
print(a)

View File

@ -1,40 +0,0 @@
import heapq
class PriorityQueue:
def __init__(self):
self.heap = []
self.entry_finder = {} # map: item -> [priority, item]
self.REMOVED = '<removed>'
self.counter = 0 # unique sequence count to break ties
def add_or_update(self, item, priority):
if item in self.entry_finder:
self.remove(item)
count = self.counter
entry = [priority, count, item]
self.entry_finder[item] = entry
heapq.heappush(self.heap, entry)
self.counter += 1
def remove(self, item):
entry = self.entry_finder.pop(item)
entry[-1] = self.REMOVED # mark as removed
def pop(self):
while self.heap:
priority, count, item = heapq.heappop(self.heap)
if item != self.REMOVED:
del self.entry_finder[item]
return item, priority
return None
if __name__ == "__main__":
pq = PriorityQueue()
pq.add_or_update('task1', 1)
pq.add_or_update('task2', float('inf'))
pq.add_or_update('task3', float('inf'))
print(pq.pop()) # Should print ('task1', 1)
pq.add_or_update('task2', 0) # Update priority of 'task1'
print(pq.pop()) # Should print ('task2', 0)
print(pq.pop()) # Should print ('task3', 3)

View File

@ -1,13 +0,0 @@
from pathlib import Path
def get_path(filename) -> Path:
this_dir = Path(__file__).resolve().parent
project_dir = this_dir.parent
return project_dir / filename
if __name__ == "__main__":
filename = get_path("data/seq0.txt")
print(filename)
print(filename.resolve())
print(filename.is_file())
print(filename.exists())

View File

@ -5,6 +5,7 @@ from utils.memory_range import mrange
from utils.literal import Literal from utils.literal import Literal
def quick_sort_stepwise(z: MemoryArray, l: Literal, r: Literal): def quick_sort_stepwise(z: MemoryArray, l: Literal, r: Literal):
n = z.length()
if l < r: if l < r:
q = partition(z, l, r) q = partition(z, l, r)
yield z yield z
@ -76,6 +77,6 @@ def swap(z: MemoryArray, i: int, j: int):
if __name__ == '__main__': if __name__ == '__main__':
sizes = range(10, 101, 5) sizes = range(10, 101, 10)
#analyze_complexity(quick_sort, sizes) analyze_complexity(quick_sort, sizes)
analyze_complexity(quick_sort, sizes, True) # analyze_complexity(quick_sort, sizes, True)

View File

@ -1,60 +0,0 @@
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 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))
for j in mrange(a.length()):
c[a[j]].set(c[a[j]].succ())
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())
def analyze_complexity(sizes, presorted=False):
"""
Analysiert die Komplexität einer Sortierfunktion.
:param sizes: Eine Liste von Eingabegrößen für die Analyse.
"""
for size in sizes:
MemoryManager.purge() # Speicher zurücksetzen
if presorted:
random_array = MemoryArray.create_sorted_array(size, 0, 100)
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)
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)
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)
sizes = range(10, 101, 10)
analyze_complexity(sizes)
# analyze_complexity(sizes, True)

View File

@ -1 +0,0 @@
from vorlesung.L05_binaere_baeume.bin_tree import BinaryTree

View File

@ -1,26 +0,0 @@
from utils.memory_manager import MemoryManager
from utils.memory_array import MemoryArray
from utils.literal import Literal
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.
"""
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)
MemoryManager.plot_stats(["cells", "compares"])
if __name__ == "__main__":
sizes = range(1, 1001, 2)
analyze_complexity(sizes)

View File

@ -1,26 +0,0 @@
from utils.memory_manager import MemoryManager
from utils.memory_array import MemoryArray
from utils.literal import Literal
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.
"""
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)
MemoryManager.plot_stats(["cells", "compares"])
if __name__ == "__main__":
sizes = range(1, 1001, 2)
analyze_complexity(sizes)

View File

@ -1,94 +0,0 @@
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
class AVLTree(BinaryTree):
def __init__(self):
super().__init__()
def new_node(self, value):
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):
insert_generator = self.insert_stepwise(value)
node, parent = None, None
while True:
try:
node, parent = next(insert_generator)
except StopIteration:
break
return node, parent
def insert_stepwise(self, value):
node, parent = super().insert(value)
yield None, None
node.parent = parent
if parent:
self.balance(parent)
return node, parent
def delete(self, value):
node, parent = super().delete(value)
if node:
node.parent = parent
if parent:
self.balance(parent)
def graph_filename(self):
return "AVLTree"
if __name__ == "__main__":
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)

View File

@ -1,59 +0,0 @@
import random
import pygame
from utils.game import Game
from avl_tree import AVLTree
WHITE = (255, 255, 255)
BLUE = (0, 0, 255)
BLACK = (0, 0, 0)
WIDTH = 800
HEIGHT = 400
MARGIN = 20
class AVLTreeGame(Game):
def __init__(self):
super().__init__("AVLTree Game", fps=10, size=(WIDTH, HEIGHT))
random.seed()
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.height = self.tree.get_height(self.tree.root)
self.generator = None
def update_game(self):
if not self.finished:
if self.generator is None:
self.generator = self.tree.insert_stepwise(self.z.pop())
try:
next(self.generator)
except StopIteration:
self.generator = None
if self.generator is None and len(self.z) == 0:
self.finished = True
self.height = self.tree.get_height(self.tree.root)
return True
def draw_game(self):
self.screen.fill(WHITE)
if self.height > 0:
self.draw_tree(self.tree.root, WIDTH // 2, MARGIN, WIDTH // 4 - MARGIN)
super().draw_game()
def draw_tree(self, node, x, y, x_offset):
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:
pygame.draw.line(self.screen, BLACK, (x, y), (x - x_offset, y + y_offset))
self.draw_tree(node.left, x - x_offset, y + y_offset, x_offset // 2)
if node.right is not None:
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()

View File

@ -1,61 +0,0 @@
from vorlesung.L05_binaere_baeume.bin_tree_node import BinaryTreeNode
class AVLTreeNode(BinaryTreeNode):
def __init__(self, value):
super().__init__(value)
self.parent = None
self.balance = 0
def __repr__(self):
return f"TreeNode(id={id(self)} value={self.value}, left={self.left}, right={self.right})"
def graphviz_rep(self, row, col, dot):
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
right_height = self.right.height() if self.right else 0
self.balance = right_height - left_height
def right_rotate(self):
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 is 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):
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 is 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):
self.right = self.right.right_rotate()
return self.left_rotate()
def left_right_rotate(self):
self.left = self.left.left_rotate()
return self.right_rotate()

View File

@ -1,61 +0,0 @@
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
def binary_search(z: MemoryArray, s: MemoryCell, l: Literal = None, r: Literal = None):
"""
Perform a binary search on the sorted array z for the value x.
"""
if l is None:
l = Literal(0)
if r is None:
r = Literal(z.length().pred())
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
def analyze_complexity(sizes):
"""
Analysiert die Komplexität
:param sizes: Eine Liste von Eingabegrößen für die Analyse.
"""
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"])
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)
if result is not None:
print(f"Value {search_value} found at index {result}.")
else:
print(f"Value {search_value} not found in the array.")
sizes = range(1, 1001, 2)
analyze_complexity(sizes)

View File

@ -1,206 +0,0 @@
from vorlesung.L05_binaere_baeume.bin_tree_node import BinaryTreeNode
from utils.project_dir import get_path
from datetime import datetime
import graphviz
class BinaryTree:
def __init__(self):
self.root = None
self.size = 0
def new_node(self, value):
return BinaryTreeNode(value)
def insert(self, value):
self.size += 1
value = self.new_node(value)
if self.root is None:
self.root = value
return self.root, None
else:
current = self.root
while True:
if value < current:
if current.left:
current = current.left
else:
current.left = value
return current.left, current
elif value >= current:
if current.right:
current = current.right
else:
current.right = value
return current.right, current
else:
return None, None
def search(self, value):
current = self.root
value = self.new_node(value)
while current:
if value < current:
current = current.left
elif value > current:
current = current.right
else:
return current
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)
while current:
if value < current:
parent = current
current = current.left
elif value > current:
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
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 = 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
# Falls es keinen Elternknoten gibt, ist der Ersatzknoten die Wurzel
if not parent:
self.root = child
return child, None
elif parent.left is current:
parent.left = child
return child, parent
else:
parent.right = child
return child, parent
def in_order_traversal(self, callback):
def in_order_traversal_recursive(callback, current):
if current is not None:
in_order_traversal_recursive(callback, current.left)
callback(current)
in_order_traversal_recursive(callback, current.right)
in_order_traversal_recursive(callback, self.root)
def level_order_traversal(self, callback):
if self.root is None:
return
queue = [(self.root, 0)]
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))
def tree_structure_traversal(self, callback):
def tree_structure_traversal_recursive(callback, current, level):
nonlocal line
if current:
tree_structure_traversal_recursive(callback, current.left, level + 1)
callback(current, level, line)
line += 1
tree_structure_traversal_recursive(callback, current.right, level + 1)
line = 0
tree_structure_traversal_recursive(callback, self.root, 0)
def graph_filename(self):
return "BinaryTree"
def graph_traversal(self):
def define_node(node, level, line):
nonlocal dot
if node is not None:
node.graphviz_rep(level, line, dot)
def graph_traversal_recursive(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)
if current.right:
dot.edge(str(id(current)), str(id(current.right)))
graph_traversal_recursive(current.right)
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)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"{self.graph_filename()}_{timestamp}.gv"
filename = get_path(filename)
dot.render(filename)
if __name__ == "__main__":
tree = BinaryTree()
values = [5, 3, 7, 2, 4, 6, 5, 8]
for value in values:
tree.insert(value)
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)

View File

@ -1,54 +0,0 @@
import random
import pygame
from utils.game import Game
from bin_tree import BinaryTree
WHITE = (255, 255, 255)
BLUE = (0, 0, 255)
BLACK = (0, 0, 0)
WIDTH = 800
HEIGHT = 400
MARGIN = 20
class BinTreeGame(Game):
def __init__(self):
super().__init__("BinTree Game", fps=10, size=(WIDTH, HEIGHT))
random.seed()
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.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.height = self.tree.get_height(self.tree.root)
if len(self.z) == 0:
self.finished = True
return True
def draw_game(self):
self.screen.fill(WHITE)
if self.height > 0:
self.draw_tree(self.tree.root, WIDTH // 2, MARGIN, WIDTH // 4 - MARGIN)
super().draw_game()
def draw_tree(self, node, x, y, x_offset):
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:
pygame.draw.line(self.screen, BLACK, (x, y), (x - x_offset, y + y_offset))
self.draw_tree(node.left, x - x_offset, y + y_offset, x_offset // 2)
if node.right is not None:
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()

View File

@ -1,22 +0,0 @@
from utils.memory_cell import MemoryCell
class BinaryTreeNode(MemoryCell):
def __init__(self, value):
super().__init__(value)
self.left = None
self.right = None
def height(self):
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)
def __repr__(self):
return f"TreeNode(value={self.value}, left={self.left}, right={self.right})"
def __str__(self):
return str(self.value)
def graphviz_rep(self, row, col, dot):
dot.node(str(id(self)), label=str(self.value), pos=f"{col},{-row}!")

View File

@ -1,58 +0,0 @@
from utils.memory_manager import MemoryManager
from utils.memory_array import MemoryArray
from utils.literal import Literal
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 analyze_complexity(sizes):
"""
Analysiert die Komplexität
: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)
MemoryManager.plot_stats(["cells", "compares", "loads", "saves"])
if __name__ == "__main__":
sizes = range(1, 1001, 2)
analyze_complexity(sizes)

View File

@ -1,120 +0,0 @@
from utils.literal import Literal
from utils.memory_cell import MemoryCell
from utils.memory_array import MemoryArray
from b_tree_node import BTreeNode
class BTree:
def __init__(self, m: int):
self.m = m
self.root = BTreeNode(m)
def search(self, value, start: BTreeNode = None) -> BTreeNode | None:
if not start:
start = self.root
start.load()
i = 0
if not isinstance(value, MemoryCell):
value = MemoryCell(value)
while i < start.n and value > start.value[Literal(i)]:
i += 1
if i < start.n and value == start.value[Literal(i)]:
return start
if start.leaf:
return None
return self.search(value, start.children[i])
def split_child(self, parent: BTreeNode, i: int):
child = parent.children[i]
child.load()
h = BTreeNode(self.m)
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)]
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
child.save()
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.children[i + 1] = h
parent.value[Literal(i)] = child.value[Literal(self.m - 1)]
parent.n += 1
parent.save()
def insert(self, value):
if not isinstance(value, MemoryCell):
value = MemoryCell(value)
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, value)
else:
self.insert_in_node(r, value)
def insert_in_node(self, start: BTreeNode, value):
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)]
i -= 1
start.value[Literal(i)].set(value)
start.n += 1
start.save()
else:
j = 0
while j < start.n and value > start.value[Literal(j)]:
j += 1
if start.children[j].n == 2 * self.m - 1:
self.split_child(start, j)
if value > start.value[Literal(j)]:
j += 1
self.insert_in_node(start.children[j], value)
def traversal(self, callback):
def traversal_recursive(node, callback):
i = 0
while i < node.n:
if not node.leaf:
traversal_recursive(node.children[i], callback)
callback(node.value[Literal(i)])
i += 1
if not node.leaf:
traversal_recursive(node.children[i], callback)
traversal_recursive(self.root, callback)
def walk(self):
def print_key(key):
print(key, end=" ")
self.traversal(print_key)
def height(self, start: BTreeNode = None):
if not start:
start = self.root
if start.leaf:
return 0
return 1 + self.height(start.children[0])
if __name__ == "__main__":
a = MemoryArray.create_array_from_file("data/seq3.txt")
tree = BTree(3)
for cell in a:
tree.insert(cell)
print(f"Height: {tree.height()}")
tree.walk()
s = tree.search(0)
print(f"\nKnoten mit 0: {str(s)}")

View File

@ -1,29 +0,0 @@
from utils.literal import Literal
from utils.memory_cell import MemoryCell
from utils.memory_array import MemoryArray
class BTreeNode(MemoryCell):
def __init__(self, m: int):
super().__init__()
self.m = m
self.n = 0
self.leaf = True
self.value = MemoryArray(Literal(2 * m - 1))
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
def save(self):
self.saved_count += 1
def __str__(self):
return "(" + " ".join([str(self.value[Literal(i)]) for i in range(self.n)]) + ")"

View File

@ -1,60 +0,0 @@
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 vorlesung.L07_hashtable.hashtable import HashTableOpenAddressing
#Goldener Schnitt
a = Literal((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
# 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 analyze_complexity(sizes):
"""
Analysiert die Komplexität
:param sizes: Eine Liste von Eingabegrößen für die Analyse.
"""
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:
ht.insert(cell)
MemoryManager.reset()
cell = random.choice(random_array.cells)
ht.search(cell)
MemoryManager.save_stats(size)
MemoryManager.plot_stats(["cells", "compares"])
if __name__ == "__main__":
sizes = range(1, 1001, 10)
analyze_complexity(sizes)

View File

@ -1,76 +0,0 @@
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
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
self.f = f
self.table = MemoryArray(m)
for i in mrange(m):
self.table[i].value = UNUSED_MARK
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())
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())
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())
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())
return used / int(self.m)
def is_unused(self, i: Literal):
if self.table[i].value == UNUSED_MARK:
return True
return False
def is_deleted(self, i: Literal):
if self.table[i].value == DELETED_MARK:
return True
return False
def is_free(self, i: Literal):
return self.is_unused(i) or self.is_deleted(i)

View File

@ -1,45 +0,0 @@
from vorlesung.L08_graphen.graph import Graph, AdjacencyMatrixGraph
from utils.project_dir import get_path
graph = AdjacencyMatrixGraph()
start = ""
end = ""
def read_file(filename: str = "data/aoc2212.txt"):
"""Read a file and return the content as a string."""
def adjust_char(char):
"""Adjust character for comparison."""
if char == 'S':
return 'a'
elif char == 'E':
return 'z'
return char
global start, end
with open(get_path(filename), "r") as file:
quest = file.read().strip().splitlines()
for row, line in enumerate(quest):
for col, char in enumerate(line):
label = f"{row},{col}"
graph.insert_vertex(label)
if char == "S":
start = label
if char == "E":
end = label
for row, line in enumerate(quest):
for col, char in enumerate(line):
for neighbor in [(row - 1, col), (row, col - 1), (row + 1, col), (row, col + 1)]:
if 0 <= neighbor[0] < len(quest) and 0 <= neighbor[1] < len(line):
if ord(adjust_char(quest[neighbor[0]][neighbor[1]])) <= ord(adjust_char(char)) + 1:
label1 = f"{row},{col}"
label2 = f"{neighbor[0]},{neighbor[1]}"
graph.connect(label1, label2)
# Lösung des Adventskalenders 2022, Tag 12
read_file("data/aoc2212test.txt")
graph.graph()
distance_map, predecessor_map = graph.bfs(start)
print(distance_map[graph.get_vertex(end)])
print(graph.path(end, predecessor_map))

View File

@ -1,366 +0,0 @@
from collections import deque
from typing import List
from enum import Enum
import graphviz
import math
import heapq
from datetime import datetime
from utils.project_dir import get_path
from utils.priority_queue import PriorityQueue
from vorlesung.L09_mst.disjoint import DisjointValue
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 __str__(self):
return str(self.value)
def __repr__(self):
return f"Vertex({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 dfs(self):
"""
Perform a depth-first search starting at the first vertex.
: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 : dict[Vertex, NodeColor]= {}
enter_map : dict[Vertex, int] = {}
leave_map : dict[Vertex, int] = {}
predecessor_map : dict[Vertex, Vertex | None] = {}
white_vertices = set(self.all_vertices())
time_counter = 0
def dfs_visit(vertex):
nonlocal time_counter
color_map[vertex] = NodeColor.GRAY
white_vertices.remove(vertex)
time_counter += 1
enter_map[vertex] = time_counter
for dest in self.get_adjacent_vertices(vertex.value):
if color_map[dest] == NodeColor.WHITE:
predecessor_map[dest] = vertex
dfs_visit(dest)
color_map[vertex] = NodeColor.BLACK
time_counter += 1
leave_map[vertex] = time_counter
# Initialize the maps
for vertex in self.all_vertices():
color_map[vertex] = NodeColor.WHITE
predecessor_map[vertex] = None
while white_vertices:
v = white_vertices.pop()
dfs_visit(v)
return enter_map, leave_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
def graph(self, filename: str = "Graph"):
dot = graphviz.Digraph( name=filename,
node_attr={"fontname": "Arial"},
format="pdf" )
for vertex in self.all_vertices():
dot.node(str(id(vertex)), label=str(vertex.value))
for edge in self.all_edges():
dot.edge(str(id(self.get_vertex(edge[0]))), str(id(self.get_vertex(edge[1]))), label=str(edge[2]))
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"{filename}_{timestamp}.gv"
filename = get_path(filename)
dot.render(filename)
def dijkstra(self, start_name: str) -> tuple[dict[Vertex, float], dict[Vertex, Vertex | None]]:
"""
Führt den Dijkstra-Algorithmus für kürzeste Pfade durch, implementiert mit Knotenfarben.
Args:
start_name: Name des Startknotens
Returns:
Ein Tupel aus zwei Dictionaries:
- distance_map: Abbildung von Knoten auf ihre kürzeste Distanz vom Startknoten
- predecessor_map: Abbildung von Knoten auf ihre Vorgänger im kürzesten Pfad
"""
def relax(vertex, dest, weight):
"""
Entspannt die Kante zwischen vertex und dest.
Aktualisiert die Distanz und den Vorgänger, wenn ein kürzerer Pfad gefunden wird.
"""
if distance_map[vertex] + weight < distance_map[dest]:
distance_map[dest] = distance_map[vertex] + weight
predecessor_map[dest] = vertex
queue.add_or_update(dest, distance_map[dest])
# Initialisierung der Maps
distance_map = {} # Speichert kürzeste Distanzen
predecessor_map = {} # Speichert Vorgänger
# Initialisiere alle Knoten
queue = PriorityQueue()
for vertex in self.all_vertices():
distance_map[vertex] = float('inf') # Initiale Distanz unendlich
predecessor_map[vertex] = None # Initialer Vorgänger None
queue.add_or_update(vertex, distance_map[vertex]) # Füge Knoten zur Prioritätswarteschlange hinzu
# Setze Startknoten
start_node = self.get_vertex(start_name)
distance_map[start_node] = 0
queue.add_or_update(start_node, distance_map[start_node])
while True:
entry = queue.pop()
if entry is None:
break
vertex = entry[0]
for dest, weight in self.get_adjacent_vertices_with_weight(vertex.value):
relax(vertex, dest, weight)
return distance_map, predecessor_map
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
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

View File

@ -1,18 +0,0 @@
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()

View File