- Nach der Lektüre des Kapitels zu B-Trees im Buchclub zu Database Internals wurde die Datenstruktur nicht als Code, sondern als Fabrikstruktur in Factorio implementiert, um das Konzept visuell zu überprüfen.
- Ein BST kann nur dann nach links und rechts verzweigen, wenn Schlüssel sortierbar sind; häufen sich Werte auf einer Seite, kann die Sucheffizienz auf das Niveau einer linearen Liste sinken.
- Bei plattenbasierter Speicherung sind die Rebalancing-Kosten eines BST und das Lesen mehrerer Pages problematisch; ein B-Tree reduziert dieses Problem, indem er mehrere Schlüssel in einem Node speichert.
- Die Factorio-Implementierung stellt Nodes und Vergleichsoperationen mit Holzkisten und violetten Filtergreifarmen dar und legt eine willkürliche Sortierreihenfolge für Items fest, um Suchpfade zu erzeugen.
- Die B-Tree-Version verwendet 3 Schlüssel und 4 Pointer pro Node und kann auf 2 Ebenen deutlich mehr Schlüssel aufnehmen als ein BST; Probleme bei der Wertdarstellung und der manuellen Sortierung bleiben jedoch bestehen.
Unterschied zwischen BST und B-Tree
- Ein binärer Suchbaum (BST) speichert in jedem Node einen Schlüssel; niedrigere Schlüssel gehen zum linken Node, höhere Schlüssel zum rechten Node.
- Das Beispiel beginnt mit dem Root-Schlüssel
8, links3und rechts10. - Es funktioniert nur mit sortierbaren Werten, bei denen sich höhere und niedrigere Schlüssel vergleichen lassen.
- Das Beispiel beginnt mit dem Root-Schlüssel
- Wenn viele Werte nur auf einer Seite hinzugefügt werden, gerät der BST aus dem Gleichgewicht.
- Im schlimmsten Fall wird er fast zu einer linear sortierten Liste wie
8 -> 10 -> 14. - Die Unwucht lässt sich korrigieren, indem man etwa
10als Pivot an die Root setzt und8sowie14auf beide Seiten verteilt.
- Im schlimmsten Fall wird er fast zu einer linear sortierten Liste wie
- Bei plattenbasierter Speicherung ist ein BST im Nachteil.
- Wenn ständig rebalanciert wird, müssen Platte und Pointer häufig aktualisiert werden.
- Benachbarte Nodes können auf unterschiedlichen Pages gespeichert sein, sodass selbst für eine einzelne Suche mehrere Pages gelesen werden müssen.
- Ein B-Tree speichert mehrere Schlüssel in einem Node und verweist mit
Anzahl der Schlüssel + 1Pointern auf Child-Nodes.- Der Beispiel-Node
[17 | 24]verzweigt zu drei Child-Nodes: Schlüssel kleiner als17, Schlüssel zwischen17und24sowie Schlüssel größer als24.
- Der Beispiel-Node
Der in Factorio implementierte Suchbaum
- Factorio ist ein Fabrikbau-Spiel; in der Implementierung wird jeder Tree-Node als Struktur im Spiel dargestellt.
- Zunächst wird ein einfacher BST gebaut.
- Jeder Node hat eine Holzkiste, die einen Schlüssel enthält, sowie zwei Pfade zu anderen Nodes.
- Da es zwischen Materialien keine eingebaute Vergleichsmethode gibt, wird mit
wood, coal, stone, brick, copper, iron, steeleine willkürliche Sortierreihenfolge festgelegt. - Violette Filtergreifarme übernehmen die Vergleichsprüfung.
- Im ersten Node prüft ein Greifarm, ob das Item
brickentspricht. - Der zweite Greifarm prüft, ob es kleiner als
brickist, etwawood, coal, stone. - Der dritte Greifarm filtert größere Werte wie
copper, iron, steelheraus.
- Im ersten Node prüft ein Greifarm, ob das Item
- Oben rechts gibt es außerdem einen Garbage Collector, der Items entfernt, die fälschlich auf das Förderband geraten sind.
- Die B-Tree-Implementierung benötigt in einem einzelnen Node mehr Strukturen.
- Jeder Node enthält 3 Schlüssel, 3 Filtergreifarme, 3 Holzkisten und 4 Child-Pointer.
- Auf derselben Tiefe kann er mehr Informationen speichern.
- Auf 2 Ebenen speichert ein BST 2 Schlüssel, ein B-Tree dagegen 12 Schlüssel.
- Auf 3 Ebenen wächst der B-Tree auf bis zu 48 Schlüssel.
- Da es unpraktisch wäre, 48 Items in Factorio manuell auszuwählen und zu sortieren, bleibt der B-Tree leer, bis eine bessere Methode zur Wertdarstellung gefunden ist.
- BST und B-Tree werden nebeneinander verglichen; außerdem ist ein YouTube-Video beigefügt.
1 Kommentare
Hacker-News-Kommentare
Es ist ein ineffizientes Design, aber Informatiktheorie in Factorio umzusetzen bedeutet zwangsläufig auch, auf nicht optimale Weise zu spielen.
Factorio ist kein Spiel, das dafür gemacht wurde, einen B-Tree vorzuführen; auch die Werkzeuge sind letztlich dafür ausgelegt, Factorio zu spielen.
Die Meta, nach der man in Factorio suchen sollte, scheinen Designs mit „gemischten Bändern“ zu sein.
Manche Designs nehmen neue Items nur in einem festen Verhältnis auf, andere balancieren sich tatsächlich wieder aus, wenn sie aus dem Gleichgewicht geraten. Mir persönlich gefällt dieses hier am besten: https://www.youtube.com/watch?v=7Gt5Zx0bsOQ
Dieses Beispiel nutzt die Schaltungslogik im Spiel, aber im Factorio-Forum gibt es auch einen Bereich ohne Schaltungen: https://forums.factorio.com/viewforum.php?f=202
Interessant ist, dass das „fish“-Objekt in Factorio ein nutzloses Scherz-Item ist. Weil es nirgendwo verwendet wird, dient es manchmal als Nullwert, als Flag dafür, dass ein Band eine Runde abgeschlossen hat, oder als Debugging-Werkzeug: https://forums.factorio.com/viewtopic.php?p=544302#p544302
Dann ließen sich nicht nur die einzufügenden oder zu suchenden Objekte, sondern auch der B-Tree selbst mit Förderbändern und Insertern bewegen.
Man könnte eine rekursive Suchfunktion als Förderband-Loop durch die Fabrik schreiben, die den Baum Level für Level abträgt, bis sie ein Blatt erreicht, dann die Schleife unterbricht und das Ergebnis ausgibt.
Das wäre ein interessantes Ausführungsmodell, eher Datenfluss als Standard-JavaScript. Sollte man zulassen, dass verschiedene Förderbänder, Inserter und Fabriken über mehrere Referenzen auf dasselbe zugrunde liegende JSON-Objekt zeigen und damit „Quantentunneln“ oder „Fernwirkung“ erlauben? Das könnte nützlich sein, aber Factorio behandelt traditionell jedes physische Item als etwas mit eigener Identität; keine mehrfachen Referenzen zu unterstützen, wäre daher vielleicht „realistischer“. Oder man könnte erst die Technologie „Quantum Tunneling JSON“ erforschen und mehrere Referenzen nur in einer „JSON Reference Entangler Factory“ erzeugen lassen.
Es müsste auch möglich sein, die Ausgabe zu verändern, indem man die Ressourcendichte gewichtet, die an einer bestimmten Position ankommt. Mit dem hier gezeigten Mechanismus [2] könnte man vermutlich durch Zusammenführen, Aufteilen und die drei Bandgeschwindigkeiten dichtegewichtete Entscheidungen bauen.
[1] https://www.asimovinstitute.org/wp-content/uploads/2019/04/N...
[2] https://wiki.factorio.com/Belt_transport_system#Splitters
Spiele, die Gehirnleistung im Austausch gegen Zahlen auf dem Bildschirm verlangen, stehen ganz unten auf meiner Liste. Ich möchte etwas Neues lernen.
Es mag Puzzle-Elemente geben, und wir können beschließen, dass das Spaß macht, aber könnten wir nicht genauso beschließen, dass Lernen Spaß macht?
Großartige Arbeit.
„Database Internals“ wird gerade in einem Buchclub gelesen, und diese Woche war Kapitel 2 zu B-Trees dran.
Nebenbei: Die Anmeldung ist zwar geschlossen, aber wer möchte, kann sich Database Internals besorgen und hier anhand des Zeitplans und der Notizen „nur lesend“ mitmachen: https://eatonphil.com/2023-database-internals.html
Die Gründe dafür, dass „binäre Suchbäume schlecht für festplattenbasierte Speicher sind“, gelten auch für Speicher im RAM.
Einen einzelnen B-Tree-Knoten zu durchsuchen ist schneller, als in einem binären Baum dieselbe Anzahl an Pointern zu verfolgen. Natürlich steigt die Implementierungskomplexität, aber wenn man nicht gerade C verwendet, implementiert man baumbasierte Maps normalerweise ohnehin nicht selbst.
Varianten sind ebenfalls möglich, etwa mehr Einträge in interne Knoten zu legen und Werte nur in den Blättern zu speichern. Vorausgesetzt, man baut nicht nur eine Menge statt einer Map. Verknüpft man zusätzlich benachbarte Knoten, nähert sich das im Grunde einer Skip List an.
[0] https://abseil.io/blog/20190812-btree
[1] https://opensource.googleblog.com/2013/01/c-containers-that-...
Ich weiß nicht, warum ausgerechnet hier Factorio-Content auftaucht und wieder den Drang auslöst, ungefähr 100 Stunden darin zu versenken. Dieses Jahr gibt es ohnehin schon zu viele gute Spiele, die man spielen könnte.
Das ließe sich alles auch mit Splittern machen; Kisten oder Filter-Inserter scheinen nicht nötig zu sein. Die Erklärung ist gut.
Es geht nicht einfach darum, Ausgaben auf mehrere Linien aufzuteilen. Die Kisten repräsentieren hier die Items, die in dem jeweiligen „Knoten“ des zweidimensional angeordneten B-Trees gespeichert sind.
Ich hatte keine Zeit, das Video anzusehen, aber nach Text und Screenshots steckt die relevante Logik in den Insertern: Sie schicken Items über den passenden Pfad zum Kindknoten, sodass die „sortierte“ Eigenschaft des Baums erhalten bleibt.
Nach der Wahl der Schlüsselwerte im Originalbeitrag wäre eine Aufteilung mit Splittern zwar möglich, aber soweit ich mich erinnere, kann ein Splitter nur einen Filter haben, sodass man an jedem Verzweigungspunkt mehrere bräuchte. Das heißt: so viele, wie es Items an diesem Verzweigungspunkt gibt. Filter-Inserter erlauben mehrere Filter und sind hier daher etwas besser; das sieht man auch im ersten Screenshot.
Natürlich könnte man das B-Tree-Design komplett aufgeben und mit n Splittern in n Kisten sortieren, aber das wäre langweilig und scheint auch nicht die Absicht des Originalbeitrags zu sein.
Ein Splitter-Filter schickt nur ein bestimmtes Item auf eine Seite und alles andere auf die andere. Dieses Beispiel ist aber anders: Mehrere Typen gehen auf die eine Seite und mehrere Typen auf die andere.
Ich frage mich, ob Factorio wirklich so ein gutes Spiel ist. Alle sagen, es sei großartig, aber das Thema Fabriken bauen wirkt etwas langweilig, und ich habe Sorge, dass das Spiel zu repetitiv ist.
Wirklich cool, aber unter Leuten, die schreiben wollen: Es wirkt ziemlich ablenkend, am Satzanfang keine Großbuchstaben zu verwenden.
Ich dachte, das würde mit Factorios Schaltungssystem umgesetzt.