Проверка черепицы - PullRequest
       16

Проверка черепицы

44 голосов
/ 31 января 2011

Для проверки тайлов в скрэббле вы создаете четыре сетки букв 5х5 на общую сумму 100 тайлов.Я хотел бы сделать один, где все 40 горизонтальных и вертикальных слов являются действительными.Набор доступных плиток содержит:

  • 12 x E
  • 9 x A, I
  • 8 x O
  • 6 x N, R, T
  • 4 x D, L, S, U
  • 3 x G
  • 2 x B, C, F, H, M, P, V, W,Y, пустая плитка (подстановочный знак)
  • 1 x K, J, Q, X, Z

Доступен словарь допустимых слов здесь (700 КБ),Имеется около 12 000 правильных 5-буквенных слов.

Вот пример, где допустимы все 20 горизонтальных слов:

Z O W I E|P I N O T
Y O G I N|O C t A D   <= blank being used as 't'
X E B E C|N A L E D
W A I T E|M E R L E
V I N E R|L U T E A
---------+---------
U S N E A|K N O S P
T A V E R|J O L E D
S O F T A|I A M B I
R I D G Y|H A I T h   <= blank being used as 'h'
Q U R S H|G R O U F

Я хотел бы создать такое, где все вертикальные слова также действительны,Вы можете помочь мне решить это?Это не домашняя работа.Это вопрос, с которым мой друг обратился за помощью.

Ответы [ 5 ]

35 голосов
/ 31 января 2011

Окончательное редактирование: Решено! Вот решение.

GNAWN|jOULE
RACHE|EUROS
IDIOT|STEAN
PINOT|TRAvE
TRIPY|SOLES
-----+-----
HOWFF|ZEBRA
AGILE|EQUID
CIVIL|BUXOM
EVENT|RIOJA
KEDGY|ADMAN

Вот его фотография, построенная с моим набором скрэббл.http://twitpic.com/3wn7iu

Этого было легко найти, когда у меня был правильный подход, поэтому, держу пари, вы могли бы найти гораздо больше таким образом.См. Методологию ниже.


Создайте дерево префиксов из словаря из 5 буквенных слов для каждой строки и столбца.Рекурсивно, данное размещение плитки допустимо, если оно формирует действительные префиксы для своего столбца и строки, и если плитка доступна, и если допустимо следующее размещение плитки.Базовый случай заключается в том, что он действителен, если не осталось плитки для размещения.

Возможно, имеет смысл просто найти все действительные доски 5х5, как сказал Гленн, и посмотреть, можно ли объединить любые четыре из них.Повторение на глубину до 100 не похоже на веселье.

Редактировать: Вот мой вариант кода 2 для этого.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

typedef union node node;
union node {
    node* child[26];
    char string[6];
};

typedef struct snap snap;
struct snap {
    node* rows[5];
    node* cols[5];
    char tiles[27];
    snap* next;
};

node* root;
node* vtrie[5];
node* htrie[5];
snap* head;

char bag[27] = {9,2,2,4,12,2,3,2,9,1,1,4,2,6,8,2,1,6,4,6,4,2,2,1,2,1,2};
const char full_bag[27] = {9,2,2,4,12,2,3,2,9,1,1,4,2,6,8,2,1,6,4,6,4,2,2,1,2,1,2};
const char order[26] = {16,23,9,25,21,22,5,10,1,6,7,12,15,2,24,3,20,13,19,11,8,17,14,0,18,4};

void insert(char* string){
    node* place = root;
    int i;
    for(i=0;i<5;i++){
        if(place->child[string[i] - 'A'] == NULL){
            int j;
            place->child[string[i] - 'A'] = malloc(sizeof(node));
            for(j=0;j<26;j++){
                place->child[string[i] - 'A']->child[j] = NULL;
            }
        }
        place = place->child[string[i] - 'A'];
    }
    memcpy(place->string, string, 6);
}

void check_four(){
    snap *a, *b, *c, *d;
    char two_total[27];
    char three_total[27];
    int i;
    bool match;
    a = head;
    for(b = a->next; b != NULL; b = b->next){
        for(i=0;i<27; i++)
            two_total[i] = a->tiles[i] + b->tiles[i];
        for(c = b->next; c != NULL; c = c->next){
            for(i=0;i<27; i++)
                three_total[i] = two_total[i] + c->tiles[i];
            for(d = c->next; d != NULL; d = d->next){
                match = true;
                for(i=0; i<27; i++){
                    if(three_total[i] + d->tiles[i] != full_bag[i]){
                        match = false;
                        break;
                    }
                }
                if(match){
                    printf("\nBoard Found!\n\n");
                    for(i=0;i<5;i++){
                        printf("%s\n", a->rows[i]->string);
                    }
                    printf("\n");
                    for(i=0;i<5;i++){
                        printf("%s\n", b->rows[i]->string);
                    }
                    printf("\n");
                    for(i=0;i<5;i++){
                        printf("%s\n", c->rows[i]->string);
                    }
                    printf("\n");
                    for(i=0;i<5;i++){
                        printf("%s\n", d->rows[i]->string);
                    }
                    exit(0);
                }
            }
        }
    }
}

void snapshot(){
    snap* shot = malloc(sizeof(snap));
    int i;
    for(i=0;i<5;i++){
        printf("%s\n", htrie[i]->string);
        shot->rows[i] = htrie[i];
        shot->cols[i] = vtrie[i];
    }
    printf("\n");
    for(i=0;i<27;i++){
        shot->tiles[i] = full_bag[i] - bag[i];
    }
    bool transpose = false;
    snap* place = head;
    while(place != NULL && !transpose){
        transpose = true;
        for(i=0;i<5;i++){
            if(shot->rows[i] != place->cols[i]){
                transpose = false;
                break;
            }
        }
        place = place->next;
    }
    if(transpose){
        free(shot);
    }
    else {
        shot->next = head;
        head = shot;
        check_four();

    }
}

void pick(x, y){
    if(y==5){
        snapshot();
        return;
    }
    int i, tile,nextx, nexty, nextz;
    node* oldv = vtrie[x];
    node* oldh = htrie[y];
    if(x+1==5){
        nexty = y+1;
        nextx = 0;
    } else {
        nextx = x+1;
        nexty = y;
    }
    for(i=0;i<26;i++){
        if(vtrie[x]->child[order[i]]!=NULL &&
           htrie[y]->child[order[i]]!=NULL &&
           (tile = bag[i] ? i : bag[26] ? 26 : -1) + 1) {
                vtrie[x] = vtrie[x]->child[order[i]];
                htrie[y] = htrie[y]->child[order[i]];
                bag[tile]--;

                pick(nextx, nexty);

                vtrie[x] = oldv;
                htrie[y] = oldh;
                bag[tile]++;
           }
    }
}

int main(int argc, char** argv){
    root = malloc(sizeof(node));
    FILE* wordlist = fopen("sowpods5letters.txt", "r");
    head = NULL;
    int i;
    for(i=0;i<26;i++){
        root->child[i] = NULL;
    }
    for(i=0;i<5;i++){
        vtrie[i] = root;
        htrie[i] = root;
    }

    char* string = malloc(sizeof(char)*6);
    while(fscanf(wordlist, "%s", string) != EOF){
        insert(string);
    }
    free(string);
    fclose(wordlist);
    pick(0,0);

    return 0;
}

Сначала пробуются нечастые буквы, которые я 'Я больше не уверен, что это хорошая идея.Он начинает увязать до того, как превратится в доски, начинающиеся с x.После просмотра количества блоков 5x5 я изменил код, чтобы просто перечислить все действительные блоки 5x5.Теперь у меня есть текстовый файл объемом 150 МБ со всеми 4 430 974 решениями 5x5.

Я также попробовал его, просто повторяя все 100 плиток, и он все еще работает.

Редактировать 2: Вотсписок всех действительных блоков 5x5, которые я сгенерировал.http://web.cs.sunyit.edu/~levyt/solutions.rar

Редактировать 3: Хм, кажется, в моем отслеживании использования тайла была ошибка, потому что я только что нашел блок в моем выходном файле, который использует 5 Zs.

COSTE
ORCIN
SCUZZ
TIZZY
ENZYM

Редактировать 4: Вот конечный продукт.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

typedef union node node;
union node {
    node* child[26];
    char string[6];
};

node* root;
node* vtrie[5];
node* htrie[5];
int score;
int max_score;

char block_1[27] = {4,2,0,2, 2,0,0,0,2,1,0,0,2,1,2,0,1,2,0,0,2,0,0,1,0,1,0};//ZEBRA EQUID BUXOM RIOJA ADMAN
char block_2[27] = {1,0,1,1, 4,2,2,1,3,0,1,2,0,1,1,0,0,0,0,1,0,2,1,0,1,0,0};//HOWFF AGILE CIVIL EVENT KEDGY
char block_3[27] = {2,0,1,1, 1,0,1,1,4,0,0,0,0,3,2,2,0,2,0,3,0,0,1,0,1,0,0};//GNAWN RACHE IDIOT PINOT TRIPY
                                                                            //JOULE EUROS STEAN TRAVE SOLES
char bag[27] =     {9,2,2,4,12,2,3,2,9,1,1,4,2,6,8,2,1,6,4,6,4,2,2,1,2,1,2};
const char full_bag[27] = {9,2,2,4,12,2,3,2,9,1,1,4,2,6,8,2,1,6,4,6,4,2,2,1,2,1,2};
const char order[26] = {16,23,9,25,21,22,5,10,1,6,7,12,15,2,24,3,20,13,19,11,8,17,14,0,18,4};
const int value[27] = {244,862,678,564,226,1309,844,765,363,4656,909,414,691,463,333,687,11998,329,218,423,536,1944,1244,4673,639,3363,0};

void insert(char* string){
    node* place = root;
    int i;
    for(i=0;i<5;i++){
        if(place->child[string[i] - 'A'] == NULL){
            int j;
            place->child[string[i] - 'A'] = malloc(sizeof(node));
            for(j=0;j<26;j++){
                place->child[string[i] - 'A']->child[j] = NULL;
            }
        }
        place = place->child[string[i] - 'A'];
    }
    memcpy(place->string, string, 6);
}

void snapshot(){
    static int count = 0;
    int i;
    for(i=0;i<5;i++){
        printf("%s\n", htrie[i]->string);
    }
    for(i=0;i<27;i++){
            printf("%c%d ", 'A'+i, bag[i]);
    }
    printf("\n");
    if(++count>=1000){
        exit(0);
    }
}


void pick(x, y){
    if(y==5){
        if(score>max_score){
            snapshot();
            max_score = score;
        }
        return;
    }
    int i, tile,nextx, nexty;
    node* oldv = vtrie[x];
    node* oldh = htrie[y];
    if(x+1==5){
        nextx = 0;
        nexty = y+1;
    } else {
        nextx = x+1;
        nexty = y;
    }
    for(i=0;i<26;i++){
        if(vtrie[x]->child[order[i]]!=NULL &&
           htrie[y]->child[order[i]]!=NULL &&
           (tile = bag[order[i]] ? order[i] : bag[26] ? 26 : -1) + 1) {
                vtrie[x] = vtrie[x]->child[order[i]];
                htrie[y] = htrie[y]->child[order[i]];
                bag[tile]--;
                score+=value[tile];

                pick(nextx, nexty);

                vtrie[x] = oldv;
                htrie[y] = oldh;
                bag[tile]++;
                score-=value[tile];
           }
    }
}

int main(int argc, char** argv){
    root = malloc(sizeof(node));
    FILE* wordlist = fopen("sowpods5letters.txt", "r");
    score = 0;
    max_score = 0;
    int i;
    for(i=0;i<26;i++){
        root->child[i] = NULL;
    }
    for(i=0;i<5;i++){
        vtrie[i] = root;
        htrie[i] = root;
    }
    for(i=0;i<27;i++){
        bag[i] = bag[i] - block_1[i];
        bag[i] = bag[i] - block_2[i];
        bag[i] = bag[i] - block_3[i];

        printf("%c%d ", 'A'+i, bag[i]);
    }

    char* string = malloc(sizeof(char)*6);
    while(fscanf(wordlist, "%s", string) != EOF){
        insert(string);
    }
    free(string);
    fclose(wordlist);
    pick(0,0);

    return 0;
}

После того, как я узнал, сколько было блоков (почти 2 миллиарда и все еще считалось), я переключился на попытку найти блоки определенных типов, в частноститрудно построить те, которые используют необычные буквы.Я надеялся, что, если я получу достаточно мягкий набор букв, идущих к последнему блоку, у огромного пространства допустимых блоков, вероятно, будет один для этого набора букв.

Я присвоил каждому фрагменту значениеобратно пропорционально количеству пятибуквенных слов, в которых оно встречается. Затем, когда я нашел действительный блок, я суммировал бы значения тайлов, и если бы результат был лучшим, который я когда-либо видел, я распечатал блок.

Для первого блока я удалил пустые плитки, полагая, что последнему блоку потребуется эта гибкость больше всего.Дав ему поработать, пока я некоторое время не видел лучшего блока, я выбрал лучший блок, вынул плитки из него и снова запустил программу, получив второй блок.Я повторил это для 3-го блока.Затем для последнего блока я снова добавил пробелы и использовал первый найденный блок.

2 голосов
/ 01 февраля 2011

Вот как я бы попробовал это. Сначала создайте дерево префиксов.

Выберите слово и поместите его горизонтально сверху. Выберите слово и поместите его вертикально. Чередуйте их, пока не исчерпаны варианты. Чередуясь, вы начинаете исправлять первые буквы и устраняете множество несоответствующих слов. Если вы действительно нашли такой квадрат, то проверьте, можно ли его сделать с помощью этих кусочков.

Для квадратов 5x5: после некоторого размышления не может быть хуже, чем O (12000! / 11990!) Для случайных текстовых слов. Но думать об этом немного больше. Каждый раз, когда вы исправляете букву (в обычном тексте), вы исключаете около 90% (оптимистичное предположение) ваших слов. Это означает, что после трех итераций вы получите 12 слов. Таким образом, фактическая скорость будет

O(n * n/10 * n/10 * n/100 * n/100 * n/1000 * n/1000 ...
which for 12000 elements acts something like n^4 algorithm

что не так уж и плохо.

Возможно, кто-то сможет лучше проанализировать проблему. Но поиск слов должен все же сходиться довольно быстро.

Там может быть больше устранения, злоупотребляя редкими буквами. По сути, найти все слова, которые имеют редкие буквы. Попробуйте сделать соответствующие позиции для каждой буквы. Составьте набор допустимых букв для каждой позиции.

Например, допустим, у нас есть четыре слова с буквой Q.

 AQFED, ZQABE, EDQDE, ELQUO

 this means there are two valid positionings of those:

 xZxxx
 AQFED
 xAxxx   ---> this limits our search for words that contain [ABDEFZ] as the second letter
 xBxxx
 xExxx

 same for the other

 EDQDE   ---> this limits our search for words that contain [EDLU] as the third letter
 ELQUO

 all appropriate words are in union of those two conditions

Таким образом, в принципе, если у нас есть несколько слов, которые содержат редкую букву X в слове S в позиции N, это означает, что другие слова, которые находятся в этой матрице, должны иметь букву, которая также находится в S в позиции n.

Формула:

  • Найти все слова, содержащие нечастую букву X в позиции 1 (следующая итерация 2, 3 ...)
  • Составьте набор A из букв в этих словах
  • Сохраняйте только те слова из словаря, которые имеют букву из множества A в позиции 1
  • Попробуйте вписать их в матрицу (первым способом)
  • Повторите с позиции 2
2 голосов
/ 31 января 2011

Я бы подошел к проблеме (наивно, чтобы быть уверенным), приняв пессимистический взгляд.Я бы попытался доказать, что не было решения 5x5, и поэтому, конечно, нет решений four 5x5.Чтобы доказать, что не было решения 5х5, я постараюсь построить одно из всех возможных.Если моя гипотеза не удалась, и я смог построить решение 5x5, тогда у меня был бы способ построить решения 5x5, и я попытался бы построить все (независимых) решений 5x5.Если бы было хотя бы 4, то я бы определил, удовлетворяет ли какая-то комбинация ограничениям на количество букв.

[Править] Нулевой набор определил, что существует «4 430 974 5x5 решения».Они действительны?Я имею в виду, что у нас есть ограничение на количество букв, которые мы можем использовать.Это ограничение может быть выражено в виде граничного вектора BV = [9, 2, 2, 4, ...], соответствующего ограничениям на A, B, C и т. Д. (Вы видите этот вектор в коде нулевого набора).Решение 5x5 действительно, если каждый член его вектора счета букв меньше соответствующего члена в BV.Было бы легко проверить, является ли решение 5х5 действительным, как оно было создано.Возможно, число 4 430 974 можно уменьшить, скажем, до N.

Несмотря на это, мы можем сформулировать проблему следующим образом: найти четыре числа буквенных чисел среди N, сумма которых равна BV.Есть (N, 4) возможных сумм («N выбрать 4»).С N, равным 4 миллионам, это по-прежнему порядка 10 ^ 25 - не обнадеживающее число.Возможно, вы могли бы искать четыре, чьи первые слагаемые составляют 9, и, если это так, проверяя, что их вторые слагаемые равны 2, и т. Д.

Я бы отметил, что после выбора 4 из N вычисления независимы, поэтомуу вас есть многоядерный компьютер, вы можете сделать это быстрее с помощью параллельного решения.

[Edit2] Распараллеливание, вероятно, не будет иметь большого значения, хотя.На данный момент я могу взглянуть оптимистично: решений 5х5, безусловно, больше, чем я ожидал, поэтому и окончательных решений может быть больше, чем ожидалось.Возможно, вам не нужно заходить далеко в 10 ^ 25, чтобы попасть в него.

0 голосов
/ 05 февраля 2011

Я начинаю с чего-то более простого.

Вот некоторые результаты:

   3736 2x2 solutions
8812672 3x3 solutions

The 1000th 4x4 solution is
   A A H S
   A C A I
   L A I R
   S I R E

The 1000th 5x5 solution is
   A A H E D
   A B U N A
   H U R S T
   E N S U E
   D A T E D

The 1000th 2x4x4 solution is
   A A H S | A A H S
   A B A C | A B A C
   H A I R | L E K U
   S C R Y | S T E D
   --------+--------
   D E E D | D E E M
   E I N E | I N T I
   E N O L | O V E R
   T E L T | L Y N E

Обратите внимание, что транспонирование 'A' и пробела, который используется в качестве 'A', должно рассматриваться как одно и то же решение. Но транспонирование строк со столбцами должно рассматриваться как другое решение. Я надеюсь, что это имеет смысл.

0 голосов
/ 01 февраля 2011

Здесь много предварительно вычисленных 5х5. В качестве упражнения читателю предлагается найти 4 совместимых: -)

http://www.gtoal.com/wordgames/wordsquare/all5

...