C, 2765 (optimal)
Éditer
Maintenant le tout dans un seul fichier C. Cela trouve juste toutes les solutions optimales. Ils doivent tous avoir 6 mots de 15 lettres et un mot de 10 lettres composé de 8 lettres de valeur 1 et deux blancs. Pour cela, je n'ai besoin que de charger une fraction du dictionnaire et je n'ai pas besoin de chercher des mots de 15 lettres avec des blancs. Le code est une simple recherche approfondie approfondie en premier.
#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);
}
Usage:
$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
Notez que chaque solution est imprimée deux fois car lors de l'ajout d'un mot «W» à 15 lettres, 2 commandes sont créées car il y a 2 tuiles «W».
La première solution trouvée avec la répartition des points:
JUXTAPOSITIONAL 465
DEMISEMIQUAVERS 480
ACKNOWLEDGEABLY 465
WEATHERPROOFING 405
CONVEYORIZATION 480
FEATHERBEDDINGS 390
LAURUSTINE (LAURU?TI?E) 80
no tiles left
Edit: explication
Qu'est-ce qui rend possible la recherche dans tout l'espace? Lors de l'ajout d'un nouveau mot, je ne prends en compte que les mots qui ont la lettre restante la plus rare. Cette lettre doit de toute façon être dans un mot (et un mot de 15 lettres car ce sera une lettre non 1 valeur, bien que je ne vérifie pas cela). Je commence donc par des mots contenant des mots J, Q, W, W, X, Z
qui comptent 50, 100, 100, 100, 200, 500
. Aux niveaux inférieurs, j'obtiens plus de coupure car certains mots sont éliminés par le manque de lettres. Étendue de l'arborescence de recherche à chaque niveau:
0: 1
1: 49
2: 3046
3: 102560
4: 724040
5: 803959
6: 3469
Bien sûr, beaucoup de coupures sont obtenues en ne vérifiant pas les solutions non optimales (blancs en mots de 15 lettres ou mots plus courts). Il est donc heureux que la solution 2765 puisse être obtenue avec ce dictionnaire (mais c'était proche, seules 2 combinaisons de mots de 15 lettres donnent un reste raisonnable). D'un autre côté, il est facile de modifier le code pour trouver des combinaisons avec un score inférieur où les 10 lettres restantes ne sont pas toutes à 1, mais il serait plus difficile de prouver que ce serait une solution optimale.
Le code montre également un cas classique d'optimisation prématurée. Cette version de la matches
fonction rend le code seulement 30% plus lent:
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;
}
J'ai même compris comment rendre la comparaison parallèle de bits magique encore plus courte que dans mon code d'origine (le quartet le plus élevé ne peut pas être utilisé dans ce cas, mais ce n'est pas un problème, car je n'ai besoin que de 26 sur 32 quartets):
int matches_val(uint64_t a, uint64_t b) {
uint64_t mask = 0x1111111111111111ll;
return !((a - b ^ a ^ b) & mask);
}
Mais cela ne donne aucun avantage.
Éditer
En écrivant l'explication ci-dessus, j'ai réalisé que la plupart du temps est consacré à la recherche dans la liste de mots de ceux contenant une lettre spécifique qui n'est pas dans la matches
fonction. Le calcul initial des listes a donné une accélération de 10 fois.