Announcement

Collapse
No announcement yet.

Kombinatorik-Problem: Zeichen in 2-dimensionalem Array miteinander kombinieren

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Kombinatorik-Problem: Zeichen in 2-dimensionalem Array miteinander kombinieren

    Hallo!

    Ich bin dabei, einen Algorithmus zu entwickeln, der aus einer angegebenen Menge an Buchstaben (Buchstaben-Pool) alle möglichen Kombinationen aus diesem erstellt. (für die Computergegner in einem Scrabble-Spiel)

    Z.B. wenn ABC gegeben sind, dass er daraus folgende einmalige Kombinationen erstellt:
    <pre>
    CAB
    BAC
    ACB
    ABC
    BCA
    CBA
    </pre>

    Ich habe eine funktionierende Version hinbekommen, die die möglichen Kombinationen anhand von Zufallszahlen im Bereich der Länge des Zeichenpools ermittelt und prüft, ob eine doppelte Kombination erstellt wurde. Allerdings ist diese Variante bei "größeren" Bereichen (z.B. bei 7 Stellen) ziemlich langsam.

    Nun dachte ich daran, dieses Array "einfach" Vertikal von links nach rechts zu durchsuchen und zu prüfen, welcher Buchstabe aus dem Pool noch nicht auf der Y- <b>UND</b> X-Achse vorkommt und diesen dann einfach da einzusetzen.

    <pre>
    <font face="Fixedsys">
    . x | 1 | 2 | 3 | 4 | 5 | 6 | 7 | ...<br>
    ---------------------------------------------------------<br>
    y 1 | a | b | c | d | e | f | g |<br>
    . 2 | b | a | d | c | f | g | e |<br>
    . 3 | d | c | a | b | g | e | f |<br>
    . 4 | ...<br>
    . 5 |<br>
    . . |<br>
    </font>
    </pre>

    Theoretisch eigentlich kein Problem und für das Problem eine effektive Lösung, finde ich. Allerdings scheitert bei mir die Implementierung an Logik- und Denkfehlern. Wäre daher für Hilfestellungen echt dankbar!

    MfG
    Mario

  • #2
    Hi Mario

    Wofür benötigst Du diese Kombinatorik in einem Scrabble Spiel ??
    Wenn Du alle diese Kombinations durchgespielt hast dann bringt dich das auch nicht weiter, da viel zu viele Kombinationen KEINE gültigen Wörter sind.<br>

    Besser wäre es sich auf eine effizient Wortdatenbank zu konzentrieren, und per Patternmatching alle gültigen Wörter rauszusuchen. <br>
    Ich hatte ein ähnliches Problem und habe dazu ein DAWG = Directed Acyclic Word Graph, benutzt. Eine Wort List mit 544.000 Wörtern hat im Speicher ca. 1.6 Mb belegt. Die Pattern-Matching-Suche ist extrem schnell, ca. 0.02 ms bis 50 ms jenachdem was man sucht.<br>
    Als Resultat hat man dann eine Liste der passenden und gültigen Wörter.<bR>

    Gruß Hage

    Comment


    • #3
      Hi Hagen!

      Danke für deine Antwort!

      Ich benutze nebenher auch eine Wortliste, wo abgeglichen wird, ob die generierten Kombinationen darin vorkommen ... Aber du hast recht, is recht langsam und umständlich, jedoch wars das erste, was mir dazu eingefallen is *g*

      Kannst du das Pattern-Matching genauer erklären? Es hat bei mir noch nich klick gemacht ...

      Grüße, Mari

      Comment


      • #4
        Pattern-Matching ist eine Suche nach Teilstrings, z.B. *HAUS, und der findet KRANKENHAUS usw. Beim Scrabble muss das Pattern-Matchning abgeändert werden, so das für die Wildcards Buchstaben Set's benutzt werden. Z.b. auf dem Scrabble-Rack liegen 7 Buchstaben und man sucht alle Wörter die man daraus legen kann. <br>
        Dies kann mit der falschen Datenstruktur für die Wortliste extrem ineffizient werden. Die schnellste Suche versprechen Datenstrukturen die die Wortliste als Baum repräsentieren. D.h. es gibt z.B. eine Node zum Buchstaben 'A' als ersten Buchstaben des Wortes. D.h. man fasst die Wörter zusammen die mit gleichen Prefix beginnen. Eine Suche nach 'A*' würde mit einer Abfrage diejenige Node finden die ALLE Worte die mit 'A' beginnen enthalten, usw. usw.<br>
        Nun die DAWG's sind dafür die effizienteste Struktur. Eine 544.000 Wortliste würde 1.6 Mb verbrauchen, diese Wortliste als Textdatei aber 6.5 Mb. Um schnell suchen zu können ist die Kompressions-Dichte entscheidend um die komplette Wortliste im Speicher halten zu können.<br> Eine sehr große Wortliste ist wiederum entscheidend um dem Computergegner eine Vorteil gegenüber dem Menschen zu verschaffen. An Intelligenz und Schnelligkeit in der Findung optimaler Züge wird man dem mensch. Hirn nicht das Wasser reichen können. Einzigste Vorteile eines Computer-Scrable Spielers sind:
        <li>enorm großer Wortschatz ca. 250.000 Wörter zu ca. 2500 Wörtschatz des Menschens<br>
        <li>Emotionslosigkeit des Computers, wir Menschen halten uns viel zu lange mit dem Versuch auf ein einmal gefundenes und schönes Wort auf dem Scrabblebrett irgendwie unterzubekommen Der Computer versucht X'mal ein Wort unterzubekommen und dann nimmt er das nächste Wort, der Inhalt oder die subjektive Bedeutung des Wortes ist ihm egal, er kennt sie nicht.<br>

        Nun, da ich mich an Kreuzwörträtsel-Code probiert habe, habe ich auch das DAWG und den Pattern-Matchning Code fertig. Ich könnte ihn dir mailen, inklusive der Wortlisten, für englisch,deutsch,französisch.<br>
        Eines ist noch wichtig. Ein DAWG trifft im Normalfalle eine JA/NEIN Entscheidung, sprich ist gesuchtes Wort vorhanden JA/NEIN. Es kann keinerlei zusätzliche Informationen zum Wort speichern, keinen Index, keine verlinktes Object o.ä. In jedem dieser Fälle würde ein DAWG ineffizient werden. Dies ist der Nachteil des DAWG's.

        gruß Hage

        Comment


        • #5
          Es bietet sich auch ein Trie an. Das ist ein Baum aus Woertern. Der erste Knoten enthaelt den ersten Buchstaben des alphabetisch ersten Wortes. Die Verzweigung nach unten gibt den naechsten ersten Buchstaben eines Wortes. Nach rechts geht es zum zweiten Buchstaben.<br>
          Alle Worte des Woerterbuchs werden jetzt so in den Baum eingetragen. Das erste Wort waere "ab". Also erster Knoten 'a'. Nach rechts 'b'. Das zweite Wort "ach" hat den Knoten 'c' unter dem 'b'. Das 'h' ist rechts vom 'b'.<br>
          Jetzt muss man nur mit den vorhandenen Scrabblebuchstaben den Baum entlang klettern. Erreicht man ein Endezeichen (ein #0 bietet sich an) so hat man ein Wort gefunden

          Comment


          • #6
            Hi Robert
            ein DAWG ist ein Trie, aber mit dem Unterschied das er kompakter als ein Trie ist. Im Trie werden nur die gemeinsamen Wort-Prefixe zusammengefasst, in einem DAWG werden zusätzlich noch die gemeinsammen Wort-Suffixe zusammengefasst.
            Das Ende-Zeichen kann man sich sparen. In meinem DAWG benutze ich pro Buchstaben 1 Cardinal. 8 Bits für's Symbol, 1 Bit für EON = End of Node, 1 Bit für EOW = End of Word und 22 Bits als Index/Offset für die Childnode.<br>
            Würdest Du ein #0 Zeichen als Endezeichen setzen bekommst Du probleme da der Trie eben nicht mehr gemeinsamme Wortprefixe speichern kann. Angenommen: 'MIT'#0, und 'MITTAG'#0. Ohne #0 können 'MIT' und 'MIT'-'TAG' die beiden 'MIT' als ein Nodebaum mit 3 Nodes gespeichrt werden.
            Dann käme das #0 aus 'MIT'#0 und 'MITTAG' hat aber kein #0. Besser ist es zu Node das EOW zu speichern. D.h. wir hätten:<br>
            'M' + 'I' + 'T' or EOW + 'T' + 'A' + 'G' or EOW;

            gruß Hage

            Comment


            • #7
              Ich hatte eigentlich an #0 als Zeichen gedacht = eigener Node. C strings eben. Als Optimierung kann man jedem Node eine Zeichenkette zum Verwalten geben. Man kann dann alle waagrechten Knotenketten zusammenfassen, bei denen keine Verkettung nach unten besteht. Das optimiert dann viele Suffixe laengerer Worte.<br>
              Ich glaube dir aber unbesehen das die DAWG-Struktur besser ist. Ich habe versucht eine einfachere Struktur praktisch zu erklaeren, damit man sie auch implementieren kann

              Comment


              • #8
                Ja, aber genau das ist doch das Problem. Angenommen das Wort "EIN"#0 würde aus 4 Nodes zusammengesetzt, also Node "E" -> Node "I" -> Node "N" -> Node "#0". Das Wort "EINER"#0 würde in den ersten drei Nodes mit "EIN" identisch sein. Dann als zweite Childnode bekäme Node "N" die Childs Node "E" -> Node "R" -> Node "#0". Damit heist das, daß wir immer auf Node #0 verzichten können wenn wir zur Node noch ein Wort-Stopbit führen.

                Gruß Hage

                Comment


                • #9
                  Reges treiben hier Danke für eure Antworten!

                  Würde mich freuen, wenn du mir den Code + Wordlist mailen könntest, Hagen. Wollte dich in meiner ersten Antwort schon fragen (aber nur nach der Wordlist :P). Meine Mail ist: [email protected]

                  tHx
                  Mari

                  Comment


                  • #10
                    Du bist dir aber über die größe der mail schon im klaren ?

                    Comment


                    • #11
                      Wie groß wäre sie denn?

                      Ich hab auch an einen Algorithmus gedacht, der die Buchstaben der Wörter im Wordbook einzeln mit dem Buchstabenpool vergleicht, z.B.
                      das Wort LIEBE mit dem Buchstabenpool NEIBEL ...

                      Kommt ein L im Pool vor? -> Ja
                      Kommt ein I im Pool vor? -> Ja
                      Kommt ein E im Pool vor? -> Ja
                      ...
                      Alle Ja? Wort gefunden ... eins Nein? Nächstes Wort ... verstehst du, was ich meine?

                      Grüße,
                      Mari

                      Comment


                      • #12
                        Also erstmal zum eigentlichen Problem<br>

                        <pre>

                        <code><font size=2 face="Courier New"><b>procedure </b>DoCombi(Pattern,Pos,Stop: PChar);
                        <i>// Erzeuge alle Kombinationen ohne Duplikate aus Pattern von der
                        // Zeichenposition Pos angefangen bis zur Zeichenposition Stop.
                        // Pattern muß alpha. sortiert sein.
                        // 'AABCDEEXYZ' ist korrekt, aber 'KABA..' ist falsch.
                        // Pattern enthält nach R&uuml;ckkehr von DoCombi() wieder die urspr&uuml;ngliche
                        // Sortierung, wird aber während der Rekursion modifiziert.
                        // Die Kombinationen werden alpha. aufsteigend enumeriert.
                        </i><b>var
                        </b>Cur: PChar;
                        Tmp,Last: Char;
                        <b>begin
                        if </b>Pos &gt;= Stop <b>then
                        begin
                        </b>WriteLn( Pattern );
                        Exit;
                        <b>end</b>;
                        Last := #0;
                        Cur := Pos;
                        <b>while </b>Cur &lt;= Stop <b>do
                        begin
                        </b>Tmp := Cur^; Cur^ := Pos^; Pos^ := Tmp;
                        <b>if </b>Tmp &gt; Last <b>then
                        </b><i>// verhindere Duplikate !
                        // Falls alle Kombinationen, inklusive Duplikate enumeriert werden sollen
                        // muß diese Abfrage entfernt werden. Die Restriktion der alpha. Sortierung
                        // ist dann auch nicht mehr erforderlich.
                        </i><b>begin
                        </b>DoCombi(Pattern, Pos +1, Stop);
                        Last := Tmp;
                        <b>end</b>;
                        Inc(Cur);
                        <b>end</b>;
                        Tmp := Pos^;
                        <b>while </b>Pos &lt; Stop <b>do
                        begin
                        </b>Pos^ := Pos[1];
                        Inc(Pos);
                        <b>end</b>;
                        Pos^ := Tmp;
                        <b>end</b>;
                        <br>
                        <b>var
                        </b>Test: <b>String</b>;
                        <b>begin
                        </b>Test := 'ABCDEEN';
                        DoCombi(@Test[1], @Test[1], @Test[Length(Test)]);
                        <b>end</b>;
                        </font>
                        </code></pre>
                        &#10

                        Comment


                        • #13
                          obiger Code kann auch über Teilmengen die Kombinationen berechnen, z.B.<br>

                          <pre>

                          Test := 'ABCDE';
                          DoCombi(@Test[1], @Test[2], @Test[4]);

                          </pre>

                          enumierert nur die Kombinationen von 'BCD' und erzeugt<br>

                          <pre>

                          ABCDE
                          ABDCE
                          ACBDE
                          ACDBE
                          ADBCE
                          ADCBE

                          </pre>

                          Gruß Hagen

                          </pre&gt

                          Comment


                          • #14
                            Krass kurzer und schneller Algo! Ich hab viiieeel komplizierter gedacht, als nötig gewesen wäre. *g* Vielen Dank!
                            Wie groß wäre denn die Wordlist? Hab zwar eine, aber die hat nur ~ 100.000 Worte.

                            Grüße,
                            Mari

                            Comment


                            • #15
                              Was heist nur ?? Meines hat auch "nur" 200000 Worte. Das schöne am Scrabble ist das auch jede Beugungsform, Mehrzahlen usw. der Wörter erlaubt sind. Im Wörterbuch sollten natürlich auch diese Wörter enthalten sein.<br>
                              Die Mail hab ich rausgeschickt.<br>
                              Interessant wäre wenn wir aus den 100000 + 200000 eine gemeinsamme Wortliste erzeugen würden.

                              Gruß Hage

                              Comment

                              Working...
                              X