C 2765 (optimal)
Bearbeiten
Jetzt alles in einer einzigen C-Datei. Dies findet nur die optimalen Lösungen. Sie müssen aus 6 Wörtern mit 15 Buchstaben und einem Wort mit 10 Buchstaben bestehen, das aus 8 Buchstaben mit dem Wert 1 und zwei Leerzeichen besteht. Dafür muss ich nur einen Bruchteil des Wörterbuchs laden und muss nicht nach Wörtern mit 15 Buchstaben und Leerzeichen suchen. Der Code ist eine einfache, erschöpfende Tiefensuche.
#include <stdio.h>
#include <stdint.h>
#include <string.h>
struct w {
struct lc { uint64_t hi,lo; } lc;
char w[16];
} w15[6000], w10[40000];
int n15,n10;
struct lc pool = { 0x12122464612, 0x8624119232c4229 };
int pts[27] = {0,1,3,3,2,1,4,2,4,1,8,5,1,3,1,1,3,10,1,1,1,1,4,4,8,4,10};
int f[27],fs[26], w15c[27],w15l[27][6000];
int count(struct lc a, int l) { return (l < 16 ? a.lo << 4 : a.hi) >> 4*(l&15) & 15; }
int matches_val(uint64_t a, uint64_t b) {
uint64_t mask = 0x1111111111111111ll;
return !((a - b ^ a ^ b) & mask);
}
int matches(struct lc all, struct lc a) { return matches_val(all.hi,a.hi) && matches_val(all.lo,a.lo); }
int picks[10];
void try(struct lc cur, int used, int level) {
int c, i, must;
if (level == 6) {
for (i = 0; i<27; i++) if (count(cur, i) && pts[i]>1) return;
for (i = 0; i < n10; i++) if(!(used & (1 << (w10[i].w[0] & 31))) && matches(w10[i].lc, cur)) {
for (c = 0; c<level; c++) printf("%s ",w15[picks[c]].w);
printf("%s\n",w10[i].w);
}
return;
}
for (i = 0; i < 26;i++) if (count(cur,fs[i])) break;
must = fs[i];
for (c = 0; c < w15c[must]; c++) { i = w15l[must][c]; if(!(used & (1 << (w15[i].w[0] & 31))) && matches(cur, w15[i].lc)) {
struct lc b = { cur.hi - w15[i].lc.hi, cur.lo - w15[i].lc.lo };
picks[level] = i;
try(b, used + (1 << (w15[i].w[0] & 31)), level+1);
}}
}
int cmpfs(int *a, int *b){return f[*a]-f[*b];}
void ins(struct w*w, char *s, int c) {
int i;
strcpy(w->w,s);
for (;*s;s++)
if (*s&16) w->lc.hi += 1ll << 4*(*s&15); else w->lc.lo += 1ll << 4*(*s&15) - 4;
if (c) for (i = 0; i < 27;i++) if (count(w->lc,i)) f[i]++, w15l[i][w15c[i]++] = w-w15;
}
int main() {
int i;
char s[20];
while(scanf("%s ",s)>0) {
if (strlen(s) == 15) ins(w15 + n15++,s,1);
if (strlen(s) == 10) ins(w10 + n10++,s,0);
}
for (i = 0; i < 26;i++) fs[i] = i+1;
qsort(fs, 26, sizeof(int), cmpfs);
try(pool, 0, 0);
}
Verwendung:
$time ./scrab <sowpods.txt
cc -O3 scrab.c -o scrab
JUXTAPOSITIONAL DEMISEMIQUAVERS ACKNOWLEDGEABLY WEATHERPROOFING CONVEYORIZATION FEATHERBEDDINGS LAURUSTINE
JUXTAPOSITIONAL DEMISEMIQUAVERS ACKNOWLEDGEABLY WEATHERPROOFING CONVEYORIZATION FEATHERBEDDINGS LUXURIATED
JUXTAPOSITIONAL DEMISEMIQUAVERS ACKNOWLEDGEABLY WEATHERPROOFING CONVEYORIZATION FEATHERBEDDINGS LUXURIATES
JUXTAPOSITIONAL DEMISEMIQUAVERS ACKNOWLEDGEABLY WEATHERPROOFING CONVEYORIZATION FEATHERBEDDINGS ULTRAQUIET
JUXTAPOSITIONAL DEMISEMIQUAVERS ACKNOWLEDGEABLY WEATHERPROOFING CONVEYORIZATION FEATHERBEDDINGS UTRICULATE
JUXTAPOSITIONAL DEMISEMIQUAVERS WEATHERPROOFING ACKNOWLEDGEABLY CONVEYORIZATION FEATHERBEDDINGS LAURUSTINE
JUXTAPOSITIONAL DEMISEMIQUAVERS WEATHERPROOFING ACKNOWLEDGEABLY CONVEYORIZATION FEATHERBEDDINGS LUXURIATED
JUXTAPOSITIONAL DEMISEMIQUAVERS WEATHERPROOFING ACKNOWLEDGEABLY CONVEYORIZATION FEATHERBEDDINGS LUXURIATES
JUXTAPOSITIONAL DEMISEMIQUAVERS WEATHERPROOFING ACKNOWLEDGEABLY CONVEYORIZATION FEATHERBEDDINGS ULTRAQUIET
JUXTAPOSITIONAL DEMISEMIQUAVERS WEATHERPROOFING ACKNOWLEDGEABLY CONVEYORIZATION FEATHERBEDDINGS UTRICULATE
OVERADJUSTMENTS QUODLIBETARIANS ACKNOWLEDGEABLY WEATHERPROOFING EXEMPLIFICATIVE HYDROGENIZATION RUBIACEOUS
OVERADJUSTMENTS QUODLIBETARIANS WEATHERPROOFING ACKNOWLEDGEABLY EXEMPLIFICATIVE HYDROGENIZATION RUBIACEOUS
real 0m1.754s
user 0m1.753s
sys 0m0.000s
Beachten Sie, dass jede Lösung zweimal gedruckt wird, da beim Hinzufügen eines 15-Buchstaben-Worts 2 Aufträge erstellt werden, da es 2 W-Kacheln gibt.
Die erste Lösung, die mit der Punktaufschlüsselung gefunden wurde:
JUXTAPOSITIONAL 465
DEMISEMIQUAVERS 480
ACKNOWLEDGEABLY 465
WEATHERPROOFING 405
CONVEYORIZATION 480
FEATHERBEDDINGS 390
LAURUSTINE (LAURU?TI?E) 80
no tiles left
Bearbeiten: Erklärung
Was macht das Durchsuchen des gesamten Raumes möglich? Beim Hinzufügen eines neuen Wortes berücksichtige ich nur Wörter mit dem seltensten verbleibenden Buchstaben. Dieser Buchstabe muss sowieso in einem Wort stehen (und ein Wort mit 15 Buchstaben, da dies ein nicht einwertiger Buchstabe ist, obwohl ich das nicht überprüfe). Also beginne ich mit Wörtern, J, Q, W, W, X, Z
die zählen 50, 100, 100, 100, 200, 500
. Auf niedrigeren Ebenen bekomme ich mehr Cutoff, weil einige Wörter durch das Fehlen von Buchstaben eliminiert werden. Breite des Suchbaums auf jeder Ebene:
0: 1
1: 49
2: 3046
3: 102560
4: 724040
5: 803959
6: 3469
Natürlich wird eine Menge Cutoff erzielt, wenn nicht optimale Lösungen nicht überprüft werden (Leerzeichen in Wörtern mit 15 Buchstaben oder kürzeren Wörtern). Es ist also ein Glück, dass mit diesem Wörterbuch eine 2765-Lösung erzielt werden kann (aber es war knapp, nur 2 Kombinationen von 15-Buchstaben-Wörtern ergaben einen vernünftigen Rest). Andererseits ist es einfach, den Code zu modifizieren, um Kombinationen mit niedrigerer Punktzahl zu finden, bei denen nicht alle verbleibenden 10 Buchstaben 1-wertig sind, obwohl es schwieriger wäre, zu beweisen, dass dies eine optimale Lösung wäre.
Der Code zeigt auch den klassischen Fall einer vorzeitigen Optimierung. Diese Version der matches
Funktion verlangsamt den Code nur um 30%:
int matches(struct lc all, struct lc a) {
int i;
for (i = 1; i < 27; i++) if (count(a, i) > count(all, i)) return 0;
return 1;
}
Ich habe sogar herausgefunden, wie ich den Bit Magic Parallel-Vergleich noch kürzer machen kann als in meinem ursprünglichen Code (das höchste Nibble kann in diesem Fall nicht verwendet werden, aber das ist kein Problem, da ich nur 26 von 32 Nibbles benötige):
int matches_val(uint64_t a, uint64_t b) {
uint64_t mask = 0x1111111111111111ll;
return !((a - b ^ a ^ b) & mask);
}
Aber es gibt keinen Vorteil.
Bearbeiten
Beim Schreiben der obigen Erklärung wurde mir klar, dass die meiste Zeit für das Durchsuchen der Wortliste nach Wörtern verwendet wird, die einen bestimmten Buchstaben enthalten, der nicht in der matches
Funktion enthalten ist. Die Berechnung der Listen im Voraus ergab eine 10-fache Beschleunigung.