C, 2765 (tối ưu)
Chỉnh sửa
Bây giờ tất cả trong một tập tin C duy nhất. Điều này chỉ tìm thấy tất cả các giải pháp tối ưu. Tất cả đều phải có 6 từ gồm 15 chữ cái và một từ 10 chữ cái bao gồm 8 chữ cái có giá trị 1 và hai khoảng trống. Vì vậy, tôi chỉ cần tải một phần của từ điển và tôi không phải tìm 15 chữ cái có khoảng trống. Mã này là một tìm kiếm sâu đầu tiên đầy đủ đơn giản.
#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);
}
Sử dụng:
$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
Lưu ý mọi giải pháp được in hai lần vì khi thêm một đơn đặt hàng 'W' word 2 chữ cái được tạo vì có 2 gạch 'W'.
Giải pháp đầu tiên được tìm thấy với sự cố điểm:
JUXTAPOSITIONAL 465
DEMISEMIQUAVERS 480
ACKNOWLEDGEABLY 465
WEATHERPROOFING 405
CONVEYORIZATION 480
FEATHERBEDDINGS 390
LAURUSTINE (LAURU?TI?E) 80
no tiles left
Chỉnh sửa: giải thích
Điều gì làm cho việc tìm kiếm toàn bộ không gian có thể? Trong khi thêm một từ mới, tôi chỉ tính đến những từ có chữ cái hiếm nhất còn lại. Thư này dù sao cũng cần phải có một từ (và một từ 15 chữ cái vì đây sẽ là một chữ cái không có giá trị 1, mặc dù tôi không kiểm tra điều đó). Vì vậy, tôi bắt đầu với những từ có chứa J, Q, W, W, X, Z
xung quanh 50, 100, 100, 100, 200, 500
. Ở cấp độ thấp hơn, tôi nhận được nhiều điểm hơn vì một số từ bị loại bỏ do thiếu chữ cái. Bề rộng của cây tìm kiếm ở mỗi cấp:
0: 1
1: 49
2: 3046
3: 102560
4: 724040
5: 803959
6: 3469
Tất nhiên rất nhiều điểm cắt giảm có được bằng cách không kiểm tra các giải pháp không tối ưu (khoảng trắng trong 15 chữ cái hoặc từ ngắn hơn). Vì vậy, thật may mắn khi giải pháp 2765 có thể đạt được với từ điển này (nhưng nó đã gần, chỉ có 2 kết hợp 15 từ chữ cái cho một phần còn lại hợp lý). Mặt khác, thật dễ dàng để sửa đổi mã để tìm các kết hợp cho điểm thấp hơn trong đó không phải tất cả 10 chữ cái còn lại đều có giá trị 1, mặc dù khó chứng minh đây sẽ là một giải pháp tối ưu.
Ngoài ra mã cho thấy trường hợp cổ điển của tối ưu hóa sớm. Phiên bản matches
chức năng này làm cho mã chỉ chậm hơn 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;
}
Tôi thậm chí đã tìm ra cách làm cho phép so sánh song song bit thậm chí ngắn hơn so với mã ban đầu của tôi (không thể sử dụng nibble cao nhất trong trường hợp này, nhưng đây không phải là vấn đề, vì tôi chỉ cần 26 trên 32 nibble):
int matches_val(uint64_t a, uint64_t b) {
uint64_t mask = 0x1111111111111111ll;
return !((a - b ^ a ^ b) & mask);
}
Nhưng nó mang lại lợi thế bằng không.
Chỉnh sửa
Viết lời giải thích ở trên tôi nhận ra rằng phần lớn thời gian dành cho việc quét danh sách các từ dành cho những từ có chứa một chữ cái cụ thể không có trong matches
hàm. Tính toán các danh sách trả trước đã tăng tốc 10 lần.