C# 如何优化这个递归方法
我正在尝试做一个字谜游戏,为此我使用递归方法找到给定字母中所有可能的单词。 这些字母在一个4x4板上 像这样:C# 如何优化这个递归方法,c#,optimization,recursion,C#,Optimization,Recursion,我正在尝试做一个字谜游戏,为此我使用递归方法找到给定字母中所有可能的单词。 这些字母在一个4x4板上 像这样: ABCD EFGH HIJK LMNO 在此循环内调用递归方法: for (int y = 0; y < width; y++) { for (int x = 0; x < height; x++) { myScabble.Search(letters, y, x, width
ABCD
EFGH
HIJK
LMNO
在此循环内调用递归方法:
for (int y = 0; y < width; y++)
{
for (int x = 0; x < height; x++)
{
myScabble.Search(letters, y, x, width, height, "", covered, t);
}
}
我意识到我可以添加字母作为属性,但由于它是一种引用类型,因此不需要花费太多时间相反的方向可能更有效。迭代你可能的单词列表,在网格中查找单词的第一个字母,如果你发现它,在相邻的正方形中查找第二个字母,如果你发现它,继续沿着这条线进一步匹配 通过这种方式,您可以快速地完全删除单词,而不是在每个可能的位置重复检查每个单词 只要看看你在某个将要重复数千次的程序中使用的代码,LINQ可能就是一个禁忌。你在使用字符串操作,因此你在内存中生成了数千个字符串,并可能导致垃圾收集器在递归过程中运行,您应该切换到字符串生成器或某种字符数组 在这一点上,你可以做大量的优化,例如,在你的列表上做一个快速的第一步,检查每个单词的每个字母是否都在网格中的某个位置(只需将网格中的所有字母放入一个字符串,以使这一点变得简单),然后,您可以快速删除并非所有字母都在网格中的单词,甚至不用担心它们的顺序是否正确
您可以将网格转换为一系列字符串,每个字符串表示网格的特定方向,然后只使用普通字符串搜索。如果您找到匹配项,您只需确保匹配项没有跨越网格边界,这可以通过快速检查匹配的起点和终点来完成。一个优化是在您的定向搜索中。只需按对角线方向向下、向左和向下搜索,因为按对角线方向向上、向右和向上搜索将得到与简单反转字符串相同的结果 编辑: 有趣的还有使用C#和LINQ编写的解决方案 这是我试图描述的一个完全未经测试的例子
static List<string> Solve()
{
// Declaring a empty list of strings to hold our results up front.
List<string> words = new List<string>();
// I'm using set as the term for your grid of letters.
string set = @"ABCD
EFGH
HIJK
LMNO";
// i'm explicitly defining the dimensions, you need to figure this out.
int sizeX = 4;
int sizeY = 4;
// i'm also specifying a maximum word length. you might find a word like
// supercalifragilisticexpialidocious, but i doubt it so lets not waste time.
int maximumWordSize = 3;
// first, our trie/wordlist/etc. assume `GetWordList()` gets a list of all
// valid words with indicated number of characters.
List<string> wordList = GetWordList(3);
// second, we load a character array with the set.
char[,] data = new char[sizeX, sizeY];
string[] lines = set.Split('\n');
for (int i = 0; i <= lines.Count() -1; i++)
{
string line = lines[i].Trim();
for (int j = 0; j <= line.Length - 1; j++)
{
char[j,i] = lines[j];
}
}
// third, we iterate over the data
for(int x = 0; x <= sizeX - maximumWordSize; x++)
{
for (int y = 0; y <= sizeY - maximumWordSize; y++)
{
// check to see if we even have any words starting with our cursor
var validWords = wordList.Where(w=>w.Contains(data[x,y]));
if (validWords.Count() > 0)
{
// ok, we have words. continue on our quest!
// (this is where your initial qualifier changes if you use a trie
// or other search method)
char[] characters = char[maximumWordSize];
// search left
for (int i = x; i <= x + maximumWordSize - 1; i++)
characters[i] = data[i, y];
words.AddRange(Search(characters, wordList));
// search down
for (int i = y + maximumWordSize - 1; i <= y; i--)
characters[i] = data[x, y];
words.AddRange(Search(characters, wordList));
// search diagonal right
for (int i = x; i <= x + maximumWordSize - 1; i++)
for (int j = y + maximumWordSize - 1; j <= y; j--)
characters[i] = data[i, j];
words.AddRange(Search(characters, wordList));
// search diagonal left
for (int i = x; i <= x - MaximumWordSize + 1; i++)
for (int j = y + maximumWordSize - 1; j <= y; j--)
characters[i] = data[i, j];
words.AddRange(Search(characters, wordList));
}
}
}
return words;
}
static List<string> Search(char[] Input, List<string> WordList)
{
List<string> result = new List<string>();
string word = "";
// find forwards
for (int i = 0; i <= Input.Length - 1; i++)
{
word += Input[i];
if (WordList.Contains(word))
result.Add(word);
}
// find backwards
Array.Reverse(Input);
for (int i = 0; i <= Input.Length - 1; i++)
{
word += Input[i];
if (WordList.Contains(word))
result.Add(word);
}
return result;
}
静态列表求解()
{
//声明一个空字符串列表以预先保存结果。
列表单词=新列表();
//我用set作为你的字母网格的术语。
字符串集=@“ABCD
EFGH
HIJK
LMNO”;
//我在明确定义尺寸,你需要弄清楚这一点。
int-sizeX=4;
int-sizeY=4;
//我还指定了一个最大字长
//超级加利福尼亚州的经验丰富,但我对此表示怀疑,所以我们不要浪费时间。
int-maximumWordSize=3;
//首先,我们的trie/wordlist/etc.假设`GetWordList()`获取所有
//具有指定字符数的有效字。
List wordList=GetWordList(3);
//其次,我们用集合加载一个字符数组。
字符[,]数据=新字符[sizeX,sizeY];
string[]line=set.Split('\n');
对于(int i=0;i创建不变参数(字母、宽度、高度和tt)字段。不要使用Linq,它在多次迭代中都很慢。另外,将单词列表更改为更有效的字典或某种类型的排序列表。另一个优化是数据结构:
List<aWord> t = tt.Where(w => w.word.StartsWith(pass)).ToList();
List t=tt.Where(w=>w.word.StartsWith(pass)).ToList();
这是所有有效字的O(n),您可以通过使用即trie来提高性能
使用准备得更好的数据结构。不要使用列表
来存储所有单词,而是使用某种树状结构。或者至少使用排序列表+二进制搜索。这一点应该会显著提高性能。我建议使用有序集合并通过递归方法传递范围索引。例如,您将我寻找第一个字母,确定以该字母开头的单词有123-456个索引。下次你添加新字母时,你将使这个范围更窄,你不需要遍历整个集合
预先筛选单词集合。您只有16个字母,这意味着缺少10个以上的字母。您可以筛选单词集合并删除所有缺少数字的单词
Lazy LINQ。如果您想使用LINQ,那么不要强制迭代整个集合(我的意思是不要调用ToList
方法)。您可以使用它的惰性来迭代,直到您需要为止,因为如果这个数字大于2,您不关心有多少单词以这个子单词开头
不要复制cov。而不是每次都复制整个数组,只需设置cov[x,y]=true;
并在嵌套搜索尝试后放回false
值:cov[x,y]=false;
使用探查器;-)
<>我不太了解C,但是这里我是如何在C++中完成的(有些东西是伪代码,使它更容易阅读);它应该是容易翻译的:
struct word_search {
const set<string>& words; // This is a balanced search tree
const vector<vector<char> >& letters;
const int width, height;
vector<vector<bool> > marks;
string word_so_far;
// Add constructor
void search(int x, int y) {
if (x < 0 || x >= width || y < 0 || y >= height || marks[x][y]) return;
word_so_far += letters[x][y];
set<string>::const_iterator it = words.lower_bound(word_so_far);
if (it == words.end() ||
it->substr(0, word_so_far.size()) != word_so_far) {
word_so_far.resize(word_so_far.size() - 1);
return;
}
marks[x][y] = true;
for (int dx = -1; dx <= 1; ++dx)
for (int dy = -1; dy <= 1; ++dy)
search(x + dx, y + dy);
marks[x][y] = false;
word_so_far.resize(word_so_far.size() - 1);
}
};
struct word\u搜索{
const set&words;//这是一个平衡的搜索树
常量向量和字母;
const int宽度、高度;
矢量标记;
到目前为止的字符串单词;
//添加构造函数
无效搜索(整数x,整数y){
如果(x<0 | | x>=宽度| | y<0 | | y>=高度| |标记[x][y])返回;
单词_sou_far+=字母[x][y];
set::const\u迭代器it=words.lower\u bound(word\u至今);
if(it==words.end()||
it->substr(0,word\u sou\u far.size())!=word\u sou\u far){
word\u so\u far.resize(word\u so\u far.size()-1);
返回;
}
标记[x][y]=正确;
对于(intdx=-1;dx来说,其他答案是正确的:您应该完全放弃这个算法,重新开始
在文字游戏中处理这些问题的方法是
static List<string> Solve()
{
// Declaring a empty list of strings to hold our results up front.
List<string> words = new List<string>();
// I'm using set as the term for your grid of letters.
string set = @"ABCD
EFGH
HIJK
LMNO";
// i'm explicitly defining the dimensions, you need to figure this out.
int sizeX = 4;
int sizeY = 4;
// i'm also specifying a maximum word length. you might find a word like
// supercalifragilisticexpialidocious, but i doubt it so lets not waste time.
int maximumWordSize = 3;
// first, our trie/wordlist/etc. assume `GetWordList()` gets a list of all
// valid words with indicated number of characters.
List<string> wordList = GetWordList(3);
// second, we load a character array with the set.
char[,] data = new char[sizeX, sizeY];
string[] lines = set.Split('\n');
for (int i = 0; i <= lines.Count() -1; i++)
{
string line = lines[i].Trim();
for (int j = 0; j <= line.Length - 1; j++)
{
char[j,i] = lines[j];
}
}
// third, we iterate over the data
for(int x = 0; x <= sizeX - maximumWordSize; x++)
{
for (int y = 0; y <= sizeY - maximumWordSize; y++)
{
// check to see if we even have any words starting with our cursor
var validWords = wordList.Where(w=>w.Contains(data[x,y]));
if (validWords.Count() > 0)
{
// ok, we have words. continue on our quest!
// (this is where your initial qualifier changes if you use a trie
// or other search method)
char[] characters = char[maximumWordSize];
// search left
for (int i = x; i <= x + maximumWordSize - 1; i++)
characters[i] = data[i, y];
words.AddRange(Search(characters, wordList));
// search down
for (int i = y + maximumWordSize - 1; i <= y; i--)
characters[i] = data[x, y];
words.AddRange(Search(characters, wordList));
// search diagonal right
for (int i = x; i <= x + maximumWordSize - 1; i++)
for (int j = y + maximumWordSize - 1; j <= y; j--)
characters[i] = data[i, j];
words.AddRange(Search(characters, wordList));
// search diagonal left
for (int i = x; i <= x - MaximumWordSize + 1; i++)
for (int j = y + maximumWordSize - 1; j <= y; j--)
characters[i] = data[i, j];
words.AddRange(Search(characters, wordList));
}
}
}
return words;
}
static List<string> Search(char[] Input, List<string> WordList)
{
List<string> result = new List<string>();
string word = "";
// find forwards
for (int i = 0; i <= Input.Length - 1; i++)
{
word += Input[i];
if (WordList.Contains(word))
result.Add(word);
}
// find backwards
Array.Reverse(Input);
for (int i = 0; i <= Input.Length - 1; i++)
{
word += Input[i];
if (WordList.Contains(word))
result.Add(word);
}
return result;
}
List<aWord> t = tt.Where(w => w.word.StartsWith(pass)).ToList();
struct word_search {
const set<string>& words; // This is a balanced search tree
const vector<vector<char> >& letters;
const int width, height;
vector<vector<bool> > marks;
string word_so_far;
// Add constructor
void search(int x, int y) {
if (x < 0 || x >= width || y < 0 || y >= height || marks[x][y]) return;
word_so_far += letters[x][y];
set<string>::const_iterator it = words.lower_bound(word_so_far);
if (it == words.end() ||
it->substr(0, word_so_far.size()) != word_so_far) {
word_so_far.resize(word_so_far.size() - 1);
return;
}
marks[x][y] = true;
for (int dx = -1; dx <= 1; ++dx)
for (int dy = -1; dy <= 1; ++dy)
search(x + dx, y + dy);
marks[x][y] = false;
word_so_far.resize(word_so_far.size() - 1);
}
};
root
/ \
A C
/ \ |
B C A
| / \ |
$ E T T
| | |
$ $ $
public class Trie : Dictionary<char, Trie>
{
public int Frequency { get; set; }
public void Add(IEnumerable<char> chars)
{
if (chars == null) throw new System.ArgumentNullException("chars");
if (chars.Any())
{
var head = chars.First();
if (!this.ContainsKey(head))
{
this.Add(head, new Trie());
}
var tail = this.GetSafeTail(head, chars.Skip(1));
if (tail.Any())
{
this[head].Add(tail);
}
}
}
public bool Contains(IEnumerable<char> chars)
{
if (chars == null) throw new System.ArgumentNullException("chars");
var @return = false;
if (chars.Any())
{
var head = chars.First();
if (this.ContainsKey(head))
{
var tail = this.GetSafeTail(head, chars.Skip(1));
@return = tail.Any() ? this[head].Contains(tail) : true;
}
}
return @return;
}
private IEnumerable<char> GetSafeTail(char head, IEnumerable<char> tail)
{
return ((!tail.Any()) && (head != '$')) ? new [] { '$', } : tail;
}
}
var trie = new Trie();
var before = trie.Contains("Hello"); // == false
trie.Add("Hello");
var after = trie.Contains("Hello"); // == true
var matches =
from w in this.GetPossibleWords(letters)
where trie.Contains(w)
select w;
public IEnumerable<string> GetPossibleWords(char[,] letters)
{
return
from ws in this.GetPossibleWordLists(letters)
from w in ws
select w;
}
private IEnumerable<IEnumerable<string>> GetPossibleWordLists(char[,] letters)
{
Func<int, int> inc = x => x + 1;
Func<int, int> nop = x => x;
Func<int, int> dec = x => x - 1;
for (var r = letters.GetLowerBound(0);
r <= letters.GetUpperBound(0); r++)
{
for (var c = letters.GetLowerBound(1);
c <= letters.GetUpperBound(1); c++)
{
yield return new [] { letters[r, c].ToString(), };
yield return this.GetPossibleWords(letters, r, c, dec, dec);
yield return this.GetPossibleWords(letters, r, c, inc, dec);
yield return this.GetPossibleWords(letters, r, c, nop, dec);
yield return this.GetPossibleWords(letters, r, c, dec, nop);
yield return this.GetPossibleWords(letters, r, c, inc, nop);
yield return this.GetPossibleWords(letters, r, c, nop, inc);
yield return this.GetPossibleWords(letters, r, c, dec, inc);
yield return this.GetPossibleWords(letters, r, c, inc, inc);
}
}
}
private IEnumerable<string> GetPossibleWords(char[,] letters,
int r, int c,
Func<int, int> rd, Func<int, int> cd)
{
var chars = new List<char>();
while (r >= letters.GetLowerBound(0) && r <= letters.GetUpperBound(0)
&& c >= letters.GetLowerBound(1) && c <= letters.GetUpperBound(1))
{
chars.Add(letters[r, c]);
if (chars.Count > 1)
{
yield return new string(chars.ToArray());
}
r = rd(r);
c = cd(c);
}
}