递归生成任意长度的所有“单词”

Recursive generation of all “words” of arbitrary length

我有一个工作函数可以生成所有可能的特定长度的“单词”,即

AAAAA
BAAAA
CAAAA
...
ZZZZX
ZZZZY
ZZZZZ

我想概括此函数以适用于任意 长度。

在下面的可编译 C++ 代码中
iterative_generation() 是工作函数,
recursive_generation() 是 WIP 替换。

请记住,这两个函数的输出不仅略有不同,而且是镜像的(这对我的实现并没有真正的影响)。

#include <iostream>
using namespace std;

const int alfLen = 26; // alphabet length
const int strLen = 5;  // string length
char word[strLen];     // the word that we generate using either of the
                       // functions
void iterative_generation() {          // all loops in this function are
    for (int f=0; f<alfLen; f++) {     // essentially the same
        word[0] = f+'A';
        for (int g=0; g<alfLen; g++) {
            word[1] = g+'A';
            for (int h=0; h<alfLen; h++) {
                word[2] = h+'A';
                for (int i=0; i<alfLen; i++) {
                    word[3] = i+'A';
                    for (int j=0; j<alfLen; j++) {
                        word[4] = j+'A';
                        cout << word << endl;
                    }
                }
            }
        }
    }
}

void recursive_generation(int a) {
    for (int i=0; i<alfLen; i++) { // the i variable should be accessible
        if (0 < a) {               // in every recursion of the function
            recursive_generation(a-1); // will run for a == 0
        }
        word[a] = i+'A';
        cout << word << endl;
    }
}

int main() {
    for (int i=0; i<strLen; i++) {
        word[i] = 'A';
    }
    // uncomment the function you want to run
    //recursive_generation(strLen-1); // this produces duplicate words
    //iterative_generation(); // this yields is the desired result
}

我认为问题可能在于我在所有递归中使用了相同的 i 变量。在迭代函数中,每个 for 循环都有自己的变量。

这个的确切后果是什么,我不能说,但是递归函数有时会产生重复的单词(例如 ZAAAA 连续出现两次,并且 **AAA 被生成两次).

你能帮我改一下递归函数,让它的结果和迭代函数的结果一样吗?

编辑

我意识到我只需要打印最里面函数的结果。这是我将其更改为:

#include <iostream>
using namespace std;

const int alfLen = 26;
const int strLen = 5;
char word[strLen];

void recursive_generation(int a) {
        for (int i=0; i<alfLen; i++) {
                word[a] = i+'A';
                if (0 < a) {
                        recursive_generation(a-1);
                }
                if (a == 0) {
                        cout << word << endl;
                }
        }
}

int main() {
        for (int i=0; i<strLen; i++) {
        word[i] = 'A';
        }
        recursive_generation(strLen-1);
}

事实证明你根本不需要递归来将你的算法推广到任意长度的单词。

您需要做的就是 "count" 通过可能的单词。给定一个任意词,你将如何转到下一个词?

记住自然数的计数方式。如果你想从 123999 到它的后继 124000,你用零替换尾随的九,然后递增下一个数字:

123999
     |
123990
    |
123900
   |
123000
  |
124000

请注意我们如何将数字视为从 0 到 9 的数字串。我们可以对字符串使用完全相同的想法而不是其他字母表,例如从 A 到 Z 的字母表:

ABCZZZ
     |
ABCZZA
    |
ABCZAA
   |
ABCAAA
  |
ABDAAA

我们所做的只是用 As 替换尾随的 Zs,然后递增下一个字符。没什么神奇的。

我建议你现在用 C++ 自己实现这个想法。为了比较,这是我的解决方案:

#include <iostream>
#include <string>

void generate_words(char first, char last, int n)
{
    std::string word(n, first);
    while (true)
    {
        std::cout << word << '\n';
        std::string::reverse_iterator it = word.rbegin();
        while (*it == last)
        {
            *it = first;
            ++it;
            if (it == word.rend()) return;
        }
        ++*it;
    }
}

int main()
{
    generate_words('A', 'Z', 5);
}

如果您想改为从左到右计数(正如您的示例所暗示的那样),只需将 reverse_iterator 替换为 iterator,将 rbegin 替换为 begin,然后rendend.

你的递归解有两个错误:

  1. 如果需要按字母顺序打印,'a'需要从0开始,而不是相反

  2. 只需要最后一层打印,否则重复

    void recursive_generation(int a) {
    for (int i=0; i<alfLen; i++) 
    { // the i variable should be accessible
        word[a] = i+'A';
        if (a<strLen-1) 
            // in every recursion of the function
            recursive_generation(a+1); // will run for a == 0
        else
            cout << word << '\n';
    }
    

    }

受@fredoverflow 的回答启发,我创建了以下代码,它可以相对更快地完成同样的事情。

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <cmath>

void printAllPossibleWordsOfLength(char firstChar, char lastChar, int length) {
    char *word = new char[length];
    memset(word, firstChar, length);
    char *lastWord = new char[length];
    memset(lastWord, lastChar, length);
    int count = 0;

    std::cout << word << " -> " << lastWord << std::endl;

    while(true) {
        std::cout << word << std::endl;
        count += 1;
        if(memcmp(word, lastWord, length) == 0) {
            break;
        }
        if(word[length - 1] != lastChar) {
            word[length - 1] += 1;
        } else {
            for(int i=1; i<length; i++) {
                int index = length - i - 1;
                if(word[index] != lastChar) {
                    word[index] += 1;
                    memset(word+index+1, firstChar, length - index - 1);
                    break;
                }
            }
        }
    }

    std::cout << "count: " << count << std::endl;

    delete[] word;
    delete[] lastWord;
}

int main(int argc, char* argv[]) {
    int length;
    if(argc > 1) {
        length = std::atoi(argv[1]);
        if(length == 0) {
            std::cout << "Please enter a valid length (i.e., greater than zero)" << std::endl;
            return 1;
        }
    } else {
        std::cout << "Usage: go <length>" << std::endl;
        return 1;
    }
    clock_t t = clock();
    printAllPossibleWordsOfLength('A', 'Z', length);
    t = clock() - t;
    std:: cout << "Duration: " << t << " clicks (" << ((float)t)/CLOCKS_PER_SEC << " seconds)" << std::endl;
    return 0;
}