设计生成所有 n 位数字组合的递归函数的最佳方法是啥?

Posted

技术标签:

【中文标题】设计生成所有 n 位数字组合的递归函数的最佳方法是啥?【英文标题】:What's the best way to design a recursive function that generates all combinations of n-digits numbers?设计生成所有 n 位数字组合的递归函数的最佳方法是什么? 【发布时间】:2016-05-30 20:59:27 【问题描述】:

作为我大学算法课程的一部分,我们必须设计一个递归函数来生成所有 n 位数字的组合。

这是一个例子:

示例输入:

3

样本输出:

111

112

113

121

122

123

131

132

133

211

212

213

221

222

223

231

232

233

311

312

313

321

322

323

331

332

333

我想指出我已经解决了这个问题,所以人们不会评论这是家庭作业,我必须自己做。 无论如何,这是我的 C++ 代码:

void S6_1(int r, int n, int p, int d)

    if (d == 0)
        return;
    S6_1(r, n, p, d - 1);
    r = r * 10 + d;
    if (p == 1)
        cout << r << endl;
    else
        S6_1(r, n, p - 1, n);


void S6(int n)

    S6_1(0, n, n, n);

我发表这篇文章的主要原因是我坚信必须有另一种方法来解决这个问题。

非常感谢您提供的任何帮助

【问题讨论】:

请使用真实的变量名。 我坚信,除了运用战争之外,还必须有另一种解决政治冲突的方法。请给我一个解决方案。 您的输出不正确。你错过了 131、132、133、231、232、233。 如果你有 n 位数字,则有 n^n 个 n 位数字。思考这个问题的另一种方法是从 0 计数到 (n^n)-1,并将每个数字转换为 base-n。您可以轻松地将 0 映射到 111、1 到 112 等。 请注意,这可以迭代解决,没有太大的困难。毕竟,你可以不使用递归数到 1000。 【参考方案1】:

归纳思考:如何将生成所有具有 [1..n] 中的数字的 n 位数字的问题分解为一个更小的自身实例以及更多的工作?也许最明显的是将所有可能的数字添加到递归生成的所有 (n-1) 位数字的前面。我将使用 C(但 C++ 类似)将数字一次累积到缓冲区中。

// In buf[i..n-1], enumerate all numbers having n-i digits with values in [1..n]. 
void print_all(char *buf, int i, int n) 
  if (i == n)                // Here n - i == 0, so we're done. Print.
    printf("%.*s\n", n, buf);
    return;
  
  for (int digit = 1; digit <= n; ++digit) 
    buf[i] = digit + '0';     // put a digit at position i
    print_all(buf, i + 1, n); // recursively put the rest
  

称呼它:

char buf[n];
print_all(buf, 0, n);

也可以像您一样在int 中构建号码。代码有点短,但它将int分解为字符串的工作留给printf,所以从某种意义上说,这是双重工作:

void print_all(int b, int i, int n) 
  if (i == n) 
    printf("%d\n", b);
    return;
  
  for (int digit = 1; digit <= n; ++digit) 
    print_all(10 * b + digit, i + 1, n);
  

并称它为...

print_all(0, 0, n);

编辑

正如我在 cmets 中指出的那样,用难以理解的递归替换明显的循环应用程序并不明智。但是既然你问了,

// Enumerate all numbers formed by appending to the number in b all (n-i)-digit
// numbers starting with a digit in [d..n] and all other digits in [1..n].
void print_all(int d, int b, int i, int n) 
  if (d > n) return;
  if (i == n) 
    printf("%d\n", b);
    return;
  
  print_all(1, 10 * b + d, i + 1, n); // enumerate all with more digits
  print_all(d + 1, b, i, n); // enumerate other values of i'th digit

然后调用:

print_all(1, 0, 0, n);

【讨论】:

感谢您回复我的帖子。我最近在读一本关于算法的书,它说递归函数不应该有迭代或循环。 @Drako 我会把那本书扔掉。这是一个愚蠢的说法。虽然可以用递归函数调用替换任何循环,但没有理由这样做。当递归使您的程序更易于编写和理解时,请使用递归。否则使用循环。【参考方案2】:

您的解决方案很好,但变量太多。我会像这样使用递归:

#include <cstdio>

using namespace std;

void rec(int i, int n, int number)
    if(i == n) 
        printf("%d\n", number); //newline
        return; //another n digit number completed
    

    for(int j = 1; j <= n; j++)
        rec(i + 1, n, number * 10 + j); // *10 + --> adds the new digit
    
    return;


int main(int argc, char *args[])

    int n = 3; //read n from input
    rec(0, n, 0); //this will go n level in depth and before going on each level prints one digit

    return 0;

【讨论】:

如果你尝试运行它,你会发现它不起作用......即使你将n 设置为有一些价值。 对不起,你是对的。我编辑了我的代码:)。现在可以了。我也应该保留整个数字。如果您可能想要处理 n >= 10 或符号 A 到 F(对于基数 16)。您可以使用向量 而不是 int 作为数字参数,并且当 i == n 时打印整个向量。

以上是关于设计生成所有 n 位数字组合的递归函数的最佳方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

swift算法:数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

Java实现随机生成由字母数字组合的N位随机数

生成C ++中N个数字中的所有R位数字(组合,迭代)?

LeetCode22:括号生成

如何通过能够递归地多次使用数字来创建n个数字的所有组合[重复]

递归函数生成全排列