I realized a recursive function to solve this problem:
- With a 4×4 Matrix composed of letters (A…Z), for every letter get all the n-lenght
words by following only adiacent and diagonal cells.
My function’s idea is to do a recursive call for every letter to every possible direction and then concatenate the string that it is going to be formed. When the lenght of this string will be n, it stops. To prevent the returning back to a letter already used, I created and array of pointers to letter’s class which compares every new possible letter with all the letters used.
For doing this I created a special class for every letter.
This is the function: (sorry, it’s very long)
dirSu means Up – dirGiu Down – dirDx right – dirSx left – and all the Others the diagonal positions
void decisione(lettera *start, lettera *next, int count, string seq, vector <lettera*> visitate, int quanti) {
if (next == nullptr) return ;
if (count == quanti) {
bool test = false;
for (int k = 0; k < start->sequenze.size(); k++) { //To prevent same letter to be used
if (start->sequenze[k] == seq) {
test = true;
break;
}
}
if (!test) start->sequenze.push_back(seq);
return ;
}
seq = seq + next->chiave; //String concatenating every call
count++; //Counter to decide when stop
visitate.push_back(next); //Array of used letters
int i;
bool test;
//Decide direction
test = false;
for(i = 0; i < visitate.size(); i++) {
if (visitate[i] == next->dirSu) {
test = true;
break;
}
}
if (!test) decisione(start, next->dirSu, count, seq, visitate, quanti);
test = false;
for(i = 0; i < visitate.size(); i++) {
if (visitate[i] == next->dirSu) {
test = true;
break;
}
}
if (!test) decisione(start, next->dirSu, count, seq, visitate, quanti);
test = false;
for(i = 0; i < visitate.size(); i++) {
if (visitate[i] == next->dirGiu) {
test = true;
break;
}
}
if (!test) decisione(start, next->dirGiu, count, seq, visitate, quanti);
test = false;
for(i = 0; i < visitate.size(); i++) {
if (visitate[i] == next->dirSx) {
test = true;
break;
}
}
if (!test) decisione(start, next->dirSx, count, seq, visitate, quanti);
test = false;
for(i = 0; i < visitate.size(); i++) {
if (visitate[i] == next->dirDx) {
test = true;
break;
}
}
if (!test) decisione(start, next->dirDx, count, seq, visitate, quanti);
test = false;
for(i = 0; i < visitate.size(); i++) {
if (visitate[i] == next->dirAdx) {
test = true;
break;
}
}
if (!test) decisione(start, next->dirAdx, count, seq, visitate, quanti);
test = false;
for(i = 0; i < visitate.size(); i++) {
if (visitate[i] == next->dirAsx) {
test = true;
break;
}
}
if (!test) decisione(start, next->dirAsx, count, seq, visitate, quanti);
test = false;
for(i = 0; i < visitate.size(); i++) {
if (visitate[i] == next->dirBdx) {
test = true;
break;
}
}
if (!test) decisione(start, next->dirBdx, count, seq, visitate, quanti);
test = false;
for(i = 0; i < visitate.size(); i++) {
if (visitate[i] == next->dirBsx) {
test = true;
break;
}
}
if (!test) decisione(start, next->dirBsx, count, seq, visitate, quanti);
}
Well, when I activate the function for words of 3 letters, it is really fast, 4 letters: 0.028s of calculation time per letter in the matrix, 5 letters: 0.225s, 6 letters: 1.891s, 7 letters: 14.914s.
Is there a way to reduce calculation time by optimizing my function? (or removing my stupid logical errors?)
Thanks a lot!
The Matrix I used for my tests is:
C A S A
C O S A
B A C I
O T A T
I used this for every speed test (without calculating it by frequency or randomly) and with this (with my recursive function improved since my question) I get in about 90 seconds all the words with 5, 6, 7 letters for every starting letter. There are a lot of words, although it is only a 4×4 Matrix!
If I can send it to you privately it could be interesting to see also your solution to the same problem. (I don’t know how to contact you here)
Change your algorithm from recursive to iterative (with a loop). It’s usually faster. After that, the other optimizations are related to the algorithm and the data structures, in order to reduce the complexity. It’s much more difficult to find such optimizations (but is possible).
EDIT: some thoughts on the code, followed by a version of the word-search I’ve done today:
So, here is the code. It’s a standalone, you just have to compile it, execute with the english (or italian or french) words file (text, one word per line):
Note: the previous code was a code-search resolver. It now removed from the answer, but you can still get it by searching in the edit revisions of this answer.
EDIT2:
Some verbal explanations
The dictionary. It’s a kind of a radix-tree, but each of the node have only one letter to store. It can have up to 26 children (more if you include accents and/or digits), one for each letter of the considered alphabet. The basic layout is the following:
Word tree http://img571.imageshack.us/img571/2281/wtree.png
Example:
An example of a dictionary http://img706.imageshack.us/img706/1244/wordsr.png
Each node also contains a boolean which indicates whether it’s the end of a word.
Additionnally, each node store the minimum and maximum size of its children branchs, its parent, and its level from the root (= size of the prefix). It allows to stop the search of words when we see that no children branch can give a word with a specific length.
The matrix. The matrix is a double array of letters. But, for more efficiency, I added some data: its corresponding node in the dictionary (see algorithm), the direction to its children (see algorithm) and to its parent (see algorithm).
The algorithm. The idea is to ‘circle’ in the map, by ‘descending’ (increasing the path representing the current found string). When descending is not possible, we have to ‘ascend’:
An image explanation:
Algorithm unrolled http://img822.imageshack.us/img822/1374/algow.png
Steps (number indicated in the losanges):