Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

LInsoDeTeh

Treue Seele

  • »LInsoDeTeh« ist der Autor dieses Themas

Beiträge: 372

Wohnort: Essen, Deutschland

Beruf: Team Lead Inhouse-Entwicklung

  • Private Nachricht senden

1

27.08.2013, 13:05

Rechtecke in einer Matrix finden

Hallo zusammen,
ich habe mal wieder ein kleines algorithmisches Problem. Und zwar möchte ich in einem Gitter der größe 11x11, in dem Felder frei (weiß) oder bereits belegt (schwarz) sein können, ausgehend von einem Feld, alle Rechteckgrößen (über freie (weiße) Felder hinweg) finden, die das Startfeld enthalten. Das Setup sieht also in etwa so aus:

(Link)


Das Kreuz ist das Startfeld (ohne Einschränkung hat dies die Koordinaten (0,0)). Von den grauen Feldern um das Startfeld hinweg wissen wir, dass mindestens eines davon belegt ist, in der Praxis werden es meistens zwei sein.
Die schwarzen Felder stellen bereits belegte Felder dar. Ob, wie viele und welche Felder in dem Gitter belegt sind, ist unbekannt, abgesehen von dem mindestens einen Feld in Nachbarschaft zum Startfeld.
Nun möchte ich alle Rechteckgrößen herausfinden, die im freien Bereich liegen, die das Startfeld enthalten, sowie die Koordinaten der rechten unteren Ecke dieses Rechtecks.

Ein Beispiel aus der Praxis:

(Link)

Da das Feld (0,-1) unter dem Startfeld blockiert ist, muss ich nur in der oberen Hälfte suchen (also in dem roten Bereich). Hier finde ich natürlich das triviale 1x1-Rechteck mit den Koordinaten (0,0) als "rechte untere Ecke". Wenn ich nach oben gehe, finde ich desweiteren 1x2, 1x3 und 1x4 Rechtecke mit (0,0) als Koordinaten in der rechten unteren Ecke.
Weiter geht es mit 2x2 und 2x3. Weitere 2x1 Rechtecke kann ich ignorieren, da es für mich keine Rolle spielt, ob das Rechteck liegt oder steht, da ich Objekte darin drehen kann und wir ja schon ein 1x2-Rechteck am Anfang gefunden haben. Dann finde ich noch ein 2x4-Rechteck mit den Koordinaten (3,0) in der rechten unteren Ecke, und am Ende sogar ein 2x8-Rechteck mit den Koordinaten (5,0). Auch ein 3x3 (Koordinaten (1,0)) und ein 3x4-Rechteck (Koordinaten (2,0)) können in dem freien Raum Platz finden. Eine gültige Ausgabe des Problems unter obigen Voraussetzungen wäre also:

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
1x1 Rechteck => (0,0) rechts unten
1x2 Rechteck => (0,0) rechts unten
1x3 Rechteck => (0,0) rechts unten
1x4 Rechteck => (0,0) rechts unten
2x2 Rechteck => (0,0) rechts unten
2x3 Rechteck => (0,0) rechts unten
2x4 Rechteck => (3,0) rechts unten
2x5 Rechteck => (4,0) rechts unten
2x6 Rechteck => (5,0) rechts unten
2x7 Rechteck => (5,0) rechts unten
2x8 Rechteck => (5,0) rechts unten
3x3 Rechteck => (1,0) rechts unten
3x4 Rechteck => (2,0) rechts unten


Hat jemand eine Idee, wie ich das algorithmisch anfassen kann, oder geht da nur Trial&Error? Als Eingabe habe ich eine Matrix mit 1en und 0en, die jeder Koordinate zwischen (-5,-5) und (5,5) zuordnet, ob das Feld bereits belegt ist, ... und (0,0) ist immer das (freie!) Startfeld.
Rechtecke, die das Startfeld als Ecke haben, sind leicht zu ermitteln, etwas schwerer tue ich mich mit solchen wie das 3x4-Rechteck, das die Koordinaten (2,0) als rechte untere Ecke hat. Denn damit geht meine erste Lösung, mit Quadranten zu arbeiten, nicht mehr direkt auf. Oder komme ich nicht drum herum, die am Ende wieder aneinander zu puzzeln? Das Ganze sollte natürlich möglichst schnell berechenbar sein.

Oder gibt es dafür schon eine schönere Methode, oder einen Namen eines Algorithmus, nach dem ich mal googlen könnte?
Vielen Dank für Input :)

Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von »LInsoDeTeh« (27.08.2013, 13:13)


David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

2

28.08.2013, 20:23

Das wäre eigentlich eine gute Aufgabe für einen Contest! :)
Darf ich fragen, was das für ein Spiel(?) werden soll?

Mein Ansatz wäre Folgender:

Zuerst schaue ich, wie weit ich von der Startposition aus nach links und rechts gehen kann.
Nun durchlaufe ich sämtliche gültige Rechtecke der Höhe 1, die meinen Startpunkt enthalten.

C-/C++-Quelltext

1
2
3
4
5
6
7
int minX = LinksFreiBis(startX, startY);
int maxX = RechtsFreiBis(startX, startY);

for (int rechteckStartX = minX; rechteckStartX <= startX; ++rechteckStartX)
{
    for (int rechteckEndeX = startX; rechteckEndeX < spielfeldGroesse; ++rechteckEndeX)
    {

Beim Durchlaufen mache ich Folgendes für jedes Rechteck:
Ich schaue, wie weit nach oben und unten ich das Rechteck erweitern kann, ohne dass es ungültig wird.
Jetzt alle Kombinationen in Y-Richtung durchgehen (analog zu den Schleifen oben, aber in Y-Richtung) und die Rechtecke sammeln:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
        int minY = ObenFreiBis(rechteckStartX, rechteckEndeX, startY);
        int maxY = UntenFreiBis(rechteckStartX, rechteckEndeX, startY);

        for (int rechteckStartY = minY; rechteckStartY <= startY; ++rechteckStartY)
        {
            for (int rechteckEndeY = startY; rechteckEndeY < spielfeldGroesse; ++rechteckEndeY)
            {
                // Gültiges Rechteck:
                // (rechteckStartX, rechteckStartY) - (rechteckEndeX, rechteckEndeY)

Für die Funktionen ObenFreiBis und UntenFreiBis wäre eine Lookup-Tabelle hilfreich.

LInsoDeTeh

Treue Seele

  • »LInsoDeTeh« ist der Autor dieses Themas

Beiträge: 372

Wohnort: Essen, Deutschland

Beruf: Team Lead Inhouse-Entwicklung

  • Private Nachricht senden

3

28.08.2013, 21:00

Ja an den Contest habe ich beim Erstellen des Posts auch schon gedacht :D Darfst du gerne dafür verwenden, wirklich eilig ist die Lösung nicht.

Darf ich fragen, was das für ein Spiel(?) werden soll?

Klar darfst du. Es geht um einen Teil des Terrain-Generators für mein Spiel Space Engies. Dieser arbeitet mit Chunks, wobei die schwarzen Felder in obiger Aufgabe bereits generierte Chunks und die weißen noch nicht existente Chunks sind. Mit einer gewissen Wahrscheinlichkeit soll der Generator in einem neuen Chunk (der wäre das X in der obigen Aufgabe) ein Objekt generieren, statt einen Standard-Zufalls-Chunk mit Gesteinen, wobei ein Objekt, z.B. eine Raumstation, eben auch größer als ein Chunk sein kann. Ich möchte auf diesem Weg herausfinden, welche Rechteck-Formen an freien Chunks mir um den aktuellen Chunk zur Verfügung stehen, um daraus dann eine Liste der möglichen Objekte, die dort generiert werden können, zu erstellen, um daraus eines zufällig auszuwählen. Deswegen brauche ich auch die Position des Rechtecks.

Deine Methode sieht recht brute-force aus, ähnelt meinem ersten Ansatz. Ich habe jedoch überlegt, ob man das nicht noch effizienter hinbekommen kann. Zum Beispiel dadurch, dass ich ja die Belegungen der Nachbarfelder des X kenne, kann ich das Gitter ja schon einmal auf die Hälfte des Gitters, ein Viertel, oder sogar nur einen Streifen der Breite 1 eingrenzen. Wobei ich das neben der Möglichkeit, dass es nur ein 1x1-Rechteck gibt, als trivial abstemplet.

Ich habe überlegt, ob man nicht vielleicht (Voraussetzung nur 1 oder 2 blockierte Nachbarfelder) mit Quadranten arbeiten kann, in denen man deinen Algorithmus durchführt, um dann die Rechtecke benachbarter Quadranten zusammenzupuzzlen. Das könnte eventuell ein paar redundante Abfragen auf schwarz oder weiß ersparen.. Was denkst du? Oder vielleicht noch andere Ideen?

David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

4

28.08.2013, 21:50

Nun, ich behaupte mal es gibt §O(n^4)§ gültige Rechtecke, die es zu finden gilt, wobei §n§ = Größe des Spielfelds.
Da jedes gültige Rechteck gefunden werden muss, ist §O(n^4)§ damit auch eine untere Schranke für die Laufzeit eines optimalen Algorithmus.
Der von mir (und dir) vorgeschlagene "Brute Force"-Algorithmus läuft in §O(n^4)§, womit er komplexitätsmäßig optimal ist.
Du kannst also vielleicht noch einen konstanten Faktor rausholen, aber etwas wesentlich Besseres kann es nicht geben ... ;)
Ist es denn nötig, dass das super schnell läuft?

LInsoDeTeh

Treue Seele

  • »LInsoDeTeh« ist der Autor dieses Themas

Beiträge: 372

Wohnort: Essen, Deutschland

Beruf: Team Lead Inhouse-Entwicklung

  • Private Nachricht senden

5

29.08.2013, 11:20

In der Theorie hast du mit den Komplexitätsklassen natürlich Recht, in der Praxis finde ich es jedoch ziemlich wichtig, ob ich §n^4§ oder §1/2*n^4§ Rechenoperationen brauche.
Nein, eigentlich muss es nicht sonderlich schnell sein, zumal man sogar noch weitere Einschränkungen zur obigen Aufgabe machen kann (z.B. brauche ich nur Rechtecke mit maximaler Kantenlänge von 5, das ergibt ein maximales Rechteck von 5x5 und damit könnte man das Feld sogar auf 9x9 reduzieren), aber man hat ja auch einen gewissen Optimierungsehrgeiz :-)

Werbeanzeige