C# 我的迭代将永远持续下去。寻找更好的方法

C# 我的迭代将永远持续下去。寻找更好的方法,c#,arrays,loops,nested-loops,C#,Arrays,Loops,Nested Loops,给定一组16个字母和一个英语词典文件,需要找到一个解决方案,将这16个字母放入一个4x4网格中,以便在每一行和每一列中读取一个有效单词 我当前的解决方案: 1) 获取所有可能由这些字母组成的4个字母单词的列表(字谜生成器),并将它们分配到数组中 2) 循环遍历每个单词,在每一行中尝试,同时检查每个字母的数字是否正确 3) 检查每列中创建的单词是否存在于字谜数组中 逻辑是可行的,但它已经运行了一个多小时,我在400+字谜数组中的word 200上。有什么建议吗 namespace GridWord

给定一组16个字母和一个英语词典文件,需要找到一个解决方案,将这16个字母放入一个4x4网格中,以便在每一行和每一列中读取一个有效单词

我当前的解决方案:

1) 获取所有可能由这些字母组成的4个字母单词的列表(字谜生成器),并将它们分配到数组中

2) 循环遍历每个单词,在每一行中尝试,同时检查每个字母的数字是否正确

3) 检查每列中创建的单词是否存在于字谜数组中

逻辑是可行的,但它已经运行了一个多小时,我在400+字谜数组中的word 200上。有什么建议吗

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;
        }
    }
}
但该问题适用于4x4网格

更新:谢谢你的帮助,但我是个白痴。我没有修复复制/粘贴的变量(我想这要追溯到建议我重构的那个人)。此外,我比较阵列的方式也有缺陷。修复了这些问题,并与已知的工作单词列表相冲突,工作起来很有魅力。根据我的原始数据再次运行,耗时13秒。没有结果。再次感谢你的帮助

更新2:由于我没有足够的代表回答我自己的问题,这是我的工作代码(…代码已删除…请参阅下面dasblinklight的答案)


更新3:请参阅下面dasblinkenlight的答案。更加优雅,更少的循环。谢谢

使用回溯算法

首先阅读我的系列文章,介绍如何使用回溯算法解决图形着色问题:

你没有完全相同的问题,但它非常接近

这是你要做的。假设网格是

11 12 13 14
21 22 23 24
31 32 33 34
41 42 43 44
你从你的十六个字母开始:IDNVJGIEKGEESSSO,比如说

猜一猜第11行是什么。我说

现在,这限制了12和21年可能发生的事情。只有那些以I开头的单词中的字母,以及其余字母DNVJGIEGEESSSO中的第二个字母,才能在12和21中。这极大地限制了问题的解决

现在猜12个。比如说,D。这就限制了13和22,进一步限制了问题

现在猜测21,比如N,它约束22(同样)和31

现在猜22。它不能是V、J或G,因为没有单词以NV、NJ或NG开头。也许是我


继续这样做,直到你找到一个解决方案,或者你在一个没有可能的解决方案的情况下结束。如果有解决办法,你就完了。如果根据您所做的猜测没有可能的解决方案,您必须返回到上一个猜测,并做出不同的猜测,直到所有可能的猜测都用尽为止。然后再次回溯。等等关键是要迅速降低每次猜测的可能性

如果可能,最重要的是减少嵌套

如果这是不可能的,您至少应该尝试从循环中取出尽可能多的重物。
我首先想到的是所有的
.tocharray()
东西。为
words
中的每个
word
预先计算
word.tocharray()
,并将它们存储在一个新数组
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();

等等,以后再使用这些值,以节省大量重复计算。

归根结底,这只是一个16个字符的排列。因此,您将对字符进行排序

 abcd efgh ijkl mnop
在这里,正方形几乎是一个用词不当的地方。除了每个4个字母的分组外,每个分组的第n个字符还必须组成一个单词。因此,您可以开始根据输入形成排列,而不是试图将字母放入框中,这将是一种浪费时间的行为,并且这种事情可以并行化。给定你的一组字谜,这是一个快速的基于集合的操作,可以找到那些不包含分组的排列,这是一个字谜。一旦找到候选字符,就可以检查第n个字符中的单词。以这种方式继续,直到第n个字符匹配生效


找到匹配项后,可以将字符串写入二维数组

您不需要嵌套七个级别,只需要五个级别:四个循环来尝试适合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;
}
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);
}
Sub(string)
尝试从计数中“减去”单词,如果成功,则返回
true
<代码>添加(字符串)将单词添加回计数

现在,您可以编写代码的四向嵌套框架,如下所示:

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;
}
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();
}

你可以找到。它在我的电脑上几分钟就完成了,没有产生解决方案。我验证了它在解决方案存在时会找到解决方案(当你注释掉
单词
字母
并取消注释最后两行时,程序会找到有效的组合和它的转置图像)。

哇,你嵌套得够深了吗?我的眼睛好痛!七个嵌套的
for
循环肯定需要很长时间……可以说,我建议作为元帮助的一件事是,将条件逻辑的块分解为单独的方法/类。即使您编写了这样的代码,也很难对其进行推理。我想你会发现,仅此练习就可以帮助你看到可以优化的地方。清理代码并使用VS重构功能