Моя итерация занимает вечность.В поисках лучшего пути - PullRequest
6 голосов
/ 19 января 2012

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

Мое текущее решение:

1) Получить список всех возможных 4-буквенных слов, которые можно составить с помощью этих букв (генератор анаграмм), и назначить их в массив.

2) Перебирайте каждое слово, пробуя его в каждой строке, проверяя, используется ли правильное число каждой буквы.

3) Проверка, существует ли слово, созданное в каждом столбце, в массиве анаграммы.

Логика работает, но она работает более часа, и я нахожусь на слове 200 из массива 400+ анаграмм.Любые предложения?

namespace GridWords
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] words = new string[] { "zoon", "zonk", "zone", "zona", "zoea", "zobo", "zero", "zerk", "zeal", "zack", "rore", "roon", "rook", "rood", "rone", "role", "roke", "roed", "rode", "rock", "roch", "robe", "roar", "roan", "road", "rhea", "rend", "redo", "reck", "rear", "rean", "real", "reak", "read", "raze", "rare", "rank", "rand", "rana", "rale", "rake", "rade", "rack", "rach", "race", "raca", "orzo", "orra", "orle", "ordo", "orca", "oral", "orad", "ooze", "oner", "once", "oleo", "olea", "olde", "okra", "okeh", "ohed", "odor", "odea", "odal", "odah", "oche", "obol", "oboe", "nork", "noob", "nook", "nolo", "nole", "noel", "node", "nock", "nerk", "nerd", "neck", "near", "neal", "naze", "nark", "nare", "nard", "narc", "nala", "nada", "nach", "nabk", "nabe", "lorn", "lore", "lord", "loor", "loon", "look", "lone", "loke", "lode", "loco", "lock", "loch", "loca", "lobo", "lobe", "loan", "load", "leno", "lend", "lehr", "lech", "lear", "lean", "leak", "lead", "lazo", "laze", "larn", "lark", "lare", "lard", "lank", "lane", "land", "lana", "lakh", "lake", "laer", "lade", "lack", "lace", "krab", "kore", "kora", "kond", "kolo", "kola", "kohl", "koel", "kobo", "koan", "knob", "knar", "khor", "khan", "kern", "kerb", "keno", "kbar", "karn", "kara", "kaon", "kane", "kana", "kale", "kaed", "kade", "horn", "hore", "hora", "hoon", "hook", "hood", "honk", "hone", "hond", "holk", "hole", "hold", "hoke", "hoer", "hoed", "hock", "hobo", "hoar", "hero", "hern", "herl", "herd", "herb", "hend", "helo", "held", "heck", "hear", "heal", "head", "haze", "haro", "harn", "harl", "hark", "hare", "hard", "hank", "hand", "halo", "hale", "hake", "haka", "haen", "haed", "hade", "hack", "haar", "eorl", "eoan", "enol", "elan", "ecod", "echo", "ecad", "ebon", "earn", "earl", "eard", "each", "dzho", "drek", "drab", "doze", "dorr", "dork", "dore", "door", "dool", "dook", "doob", "done", "dona", "dole", "doer", "doen", "doek", "dock", "doab", "dhal", "dhak", "dern", "deco", "deck", "dear", "dean", "deal", "daze", "darn", "darl", "dark", "dare", "darb", "dank", "dale", "dahl", "dace", "daal", "czar", "cred", "cran", "crab", "coze", "corn", "cork", "core", "cord", "coon", "cool", "cook", "conk", "cone", "cond", "cole", "cold", "cola", "coke", "coho", "coed", "code", "coda", "coal", "clon", "clod", "clan", "clad", "chon", "chez", "cher", "char", "chao", "chal", "chad", "cero", "carr", "carn", "carl", "cark", "care", "card", "carb", "cane", "calo", "calk", "cake", "cade", "caba", "broo", "brod", "brer", "bren", "bred", "bran", "brae", "brad", "bozo", "born", "bork", "bore", "bord", "bora", "boor", "boon", "bool", "book", "booh", "bonk", "bone", "bond", "bona", "bolo", "bole", "bold", "bola", "boko", "boke", "boho", "bode", "bock", "boar", "boak", "bloc", "bled", "blah", "blae", "blad", "bhel", "berk", "bend", "beck", "bear", "bean", "beak", "bead", "barn", "bark", "bare", "bard", "bank", "bane", "band", "banc", "balk", "bale", "bald", "bake", "bael", "bade", "back", "bach", "baal", "azon", "azan", "arna", "arle", "ared", "area", "arco", "arch", "arba", "arar", "arak", "anoa", "ankh", "ance", "anal", "aloe", "alod", "alec", "albe", "alba", "alar", "alan", "alae", "aked", "ahed", "aero", "aeon", "adze", "acre", "acne", "ache", "acer", "aced", "able", "abed", "abac" };
            char[] letters = new char[] { 'a', 'a', 'b', 'c', 'd', 'e', 'h', 'k', 'l', 'n', 'o', 'o', 'o', 'r', 'r', 'z' };
            for (int z = 0; z < words.Length; z++)
            {
                Console.WriteLine(z);
                for (int y = 0; y < words.Length; y++)
                {
                    bool letterCountCorrect0 = true;
                    char[] wordLetters0 = words[z].ToCharArray().Concat(words[y].ToCharArray()).ToArray();
                    for (int a = 0; a < wordLetters0.Length; a++)
                    {
                        if (countInstances(wordLetters0, wordLetters0[a]) != countInstances(letters, wordLetters0[a]))
                        {
                            letterCountCorrect0 = false;
                            break;
                        }
                    }
                    if (y != z && letterCountCorrect0)
                    {
                        for (int x = 0; x < words.Length; x++)
                        {
                            bool letterCountCorrect1 = true;
                            char[] wordLetters1 = words[z].ToCharArray().Concat(words[y].ToCharArray()).Concat(words[x].ToCharArray()).ToArray();
                            for (int a = 0; a < wordLetters0.Length; a++)
                            {
                                if (countInstances(wordLetters0, wordLetters0[a]) != countInstances(letters, wordLetters0[a]))
                                {
                                    letterCountCorrect1 = false;
                                    break;
                                }
                            }
                            if (x != y && x != z && letterCountCorrect1)
                            {
                                for (int w = 0; w < words.Length; w++)
                                {
                                    bool letterCountCorrect2 = true;
                                    char[] wordLetters2 = words[z].ToCharArray().Concat(words[y].ToCharArray()).Concat(words[x].ToCharArray()).Concat(words[w].ToCharArray()).ToArray();
                                    for (int a = 0; a < wordLetters0.Length; a++)
                                    {
                                        if (countInstances(wordLetters0, wordLetters0[a]) != countInstances(letters, wordLetters0[a]))
                                        {
                                            letterCountCorrect2 = false;
                                            break;
                                        }
                                    }
                                    if (w != x && w != y && w != z && letterCountCorrect2)
                                    {
                                        char[] row1 = words[z].ToCharArray();
                                        char[] row2 = words[y].ToCharArray();
                                        char[] row3 = words[x].ToCharArray();
                                        char[] row4 = words[w].ToCharArray();
                                        char[] wordLetterArray = row1.Concat(row2).Concat(row3).Concat(row4).ToArray();
                                        Array.Sort(wordLetterArray);
                                        if (wordLetterArray == letters)
                                        {
                                            string col1 = new string(new char[] { row1[0], row2[0], row3[0], row4[0] });
                                            if (col1 != words[z] && col1 != words[y] && col1 != words[x] && col1 != words[w])
                                            {
                                                string col2 = new string(new char[] { row1[1], row2[1], row3[1], row4[1] });
                                                if (col2 != words[z] && col2 != words[y] && col2 != words[x] && col2 != words[w])
                                                {
                                                    string col3 = new string(new char[] { row1[2], row2[2], row3[2], row4[2] });
                                                    if (col3 != words[z] && col3 != words[y] && col3 != words[x] && col3 != words[w])
                                                    {
                                                        string col4 = new string(new char[] { row1[3], row2[3], row3[3], row4[3] });
                                                        if (col4 != words[z] && col4 != words[y] && col4 != words[x] && col4 != words[w])
                                                        {
                                                            if (words.Contains<String>(col1.ToLower()) && words.Contains<String>(col2.ToLower()) && words.Contains<String>(col3.ToLower()) && words.Contains<String>(col4.ToLower()))
                                                            {
                                                                Console.WriteLine(new string(row1) + " " + new string(row2) + " " + new string(row3) + " " + new string(row4));
                                                                //Console.WriteLine(col1.ToString() + " " + col2.ToString() + " " + col3.ToString() + " " + col4.ToString());
                                                            }
                                                        }
                                                    }
                                                }

                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        private static int countInstances(char[] arrToSearch, char charToFind)
        {
            int count = 0;
            for (int x = 0; x < arrToSearch.Length; x++)
            {
                if (arrToSearch[x] == charToFind)
                {
                    count++;
                }
            }
            return count;
        }
    }
}

Вот пример, как было запрошено:

Учитывая буквы "N, O, O и T", найдите решение, где эти буквы помещаются в сетку 2x2так, чтобы при чтении поперек и вниз, английские слова могли быть созданы.Ответ будет таким:

T O
O N

За исключением этой проблемы для сетки 4x4.

ОБНОВЛЕНИЕ: Спасибо за вашу помощь, но я идиот.Я не исправил свои копии / вставленные переменные (что, я полагаю, восходит к парню, который предложил мне провести рефакторинг).Кроме того, способ сравнения массивов был ошибочным.Исправил эти проблемы и столкнулся с известным рабочим списком слов, работал как шарм.Пробежал снова по моим исходным данным, заняло 13 секунд.Нет результатов.Еще раз спасибо за вашу помощь.

ОБНОВЛЕНИЕ 2: Поскольку у меня недостаточно повторений, чтобы ответить на мой собственный вопрос, вот мой рабочий код (... код удален ... см. Ответ dasblinklight ниже)

ОБНОВЛЕНИЕ 3: Пожалуйста, смотрите ответ dasblinkenlight ниже.Гораздо элегантнее, меньше петель.Спасибо!

Ответы [ 4 ]

10 голосов
/ 19 января 2012

Использовать алгоритм возврата.

Начните с прочтения моей серии статей о том, как использовать алгоритм обратного отслеживания для решения проблемы раскраски графа:

http://blogs.msdn.com/b/ericlippert/archive/tags/graph+colouring/

У вас нет точно такой же проблемы, но она очень близко.

Вот что вы делаете. Предположим, что сетка

11 12 13 14
21 22 23 24
31 32 33 34
41 42 43 44

Вы начинаете с шестнадцати букв: IDNVJGIEKGEESSSO, скажем.

угадать, что происходит в 11. Скажи, я.

Это теперь ограничивает то, что может идти в 12 и 21. Только те буквы из слов, которые начинаются с I и имеют вторую букву из оставшихся букв DNVJGIEKGEESSSO, могут быть в 12 и 21. Это чрезвычайно ограничивает проблему.

Теперь сделайте предположение на 12. Скажите, D. Затем это ограничивает 13 и 22, еще больше ограничивая проблему.

Теперь сделайте предположение для 21, скажем, N, что ограничивает 22 (снова) и 31.

Теперь сделайте предположение для 22. Это не может быть V, J или G, потому что слова не начинаются с NV, NJ или NG. Может быть, это я ...

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

1 голос
/ 19 января 2012

Вам не нужно вкладывать семь уровней, все, что вам нужно, это пять: четыре цикла, чтобы попробовать все четырехсторонние комбинации слов, которые соответствуют 16-буквенному набору, и дополнительный цикл для проверки четырех вертикальных комбинаций, подразумеваемых выбор горизонтальных слов.

Вам нужен эффективный способ управления набором букв, которые вы используете в данный момент. Один из способов справиться с этим - создать массив счетчиков, например:

static readonly int[] Counts = new int[256];
static void Add(string s) {
    foreach (var c in s) {
        Counts[c]++;
    }
}
static bool Sub(string s) {
    var res = true;
    foreach (var c in s) {
        res &= --Counts[c] >= 0;
    }
    if (!res) {
        Add(s);
    }
    return res;
}

Sub(string) пытается «вычесть» слово из отсчетов и возвращает true, если оно выполнено успешно. Add(string) добавляет слово обратно на счет.

Теперь вы можете написать четырехсторонний вложенный скелет вашего кода, например:

foreach (var w0 in Words) {
    if (!Sub(w0)) continue;
    foreach (var w1 in Words) {
        if (!Sub(w1)) continue;
        foreach (var w2 in Words) {
            if (!Sub(w2)) continue;
            foreach (var w3 in Words) {
                if (!Sub(w3)) continue;
                // Check if the w0..w3 combination yields four valid words
                // when you read it vertically, and restore the state
                Add(w3);
            }
            Add(w2);
        }
        Add(w1);
    }
    Add(w0);
}

Проверка вертикальных слов добавляет пятый и последний уровень вложенности. Я преобразовал слова в хэш-набор, чтобы ускорить проверку:

var allExist = true;
for (var i = 0; allExist && i != 4; i++) {
    vert[0] = w0[i];
    vert[1] = w1[i];
    vert[2] = w2[i];
    vert[3] = w3[i];
    allExist = Words.Contains(new string(vert));
}
if (allExist) {
    found = true;
    Console.WriteLine(w0);
    Console.WriteLine(w1);
    Console.WriteLine(w2);
    Console.WriteLine(w3);
    Console.WriteLine();
}

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

1 голос
/ 19 января 2012

Самое главное, если возможно, уменьшить вложенность.

Если это невозможно, вы должны хотя бы попытаться вырвать как можно больше тяжелых предметов из петель.
Первое, что приходит мне на ум, это все .ToCharArray() вещи. Просчитайте word.ToCharArray() для каждого word в words и сохраните их в новом массиве char[][] wordCharacterArrays. Тогда вы сохраняете много расчетов.

Также выделите все col1.ToLower() и т. Д. Чуть ниже каждого определения, например

  string col1 = new string(new char[] { row1[0], row2[0], row3[0], row4[0] });
  string col1Lower = col1.ToLower();
  if (col1 != words[z] && col1 != words[y] && col1 != words[x] && col1 != words[w])
  {
      string col2 = new string(new char[] { row1[1], row2[1], row3[1], row4[1] });
      string col2Lower = col2.ToLower();

и т. Д., И затем повторно используйте эти значения, чтобы сохранить множество повторяющихся вычислений и здесь.

0 голосов
/ 19 января 2012

Когда дело доходит до вещей, это просто перестановка из 16 символов.Таким образом, у вас будет порядок символов

 abcd efgh ijkl mnop

Квадрат здесь на самом деле почти неправильный.В дополнение к каждой 4-буквенной группировке n-й символ каждой группы также должен образовывать слово.Таким образом, вместо того, чтобы пытаться помещать буквы в ящики, что было бы пустой тратой времени, вы могли бы вместо этого начать формировать перестановки на основе входных данных, и подобные вещи можно распараллелить.Учитывая ваш набор анаграмм, это операция на основе быстрого набора, чтобы найти те перестановки, которые не содержат группировки, которая является анаграммой.Как только кандидат найден, вы можете проверить n-ые символы на наличие слов.Продолжайте в том же духе, пока не сработает n-й символьный матч.

Как только совпадение найдено, вы можете записать строку в 2d массив.

...