生成由个别数字之和排序的n位数字(无递归)

时间:2009-11-11 13:53:47

标签: java php c++ combinatorics permutation

我希望按以下顺序生成所有可能的n位数值,其中序列由各个数字的总和决定。

例如,使用n = 3

111     sum = 3
112     sum = 4
121
211
122     sum = 5
212
221
113
131
311
114     sum = 6
141
411
:::
999     sum = 27

总和组中的顺序并不重要。

任何帮助,意见将不胜感激

4 个答案:

答案 0 :(得分:6)

如果您维护自己的重要数据堆栈,那么总是将递归问题转换为迭代问题 - 如果避免递归的原因是语言不支持它。

但是,如果语言 支持它,那么递归解决方案会更加优雅。

我能想到避免递归的另一个原因是有限的堆栈深度。在这种情况下,递归解决方案的迭代转换将通过不需要尽可能多的堆栈空间来缓解问题。

但您需要了解处理n个数字的堆栈深度仅相对于log 10 n增长。换句话说,每个数字只能获得一个额外的堆栈帧(只有10个堆栈帧来处理全部32位整数)。

除了:当你到达那一点时,你的算法将花费这么长的时间来运行,堆栈帧将是你的问题中最少的: - )

这是一个递归的Python解决方案:

def recur (numdigits,sum,pref="",prefsum=0):
    if numdigits == 0:
        if prefsum == sum:
            print "%s, sum=%d"%(pref,prefsum)
    else:
        for i in range (1,10):
            recur (numdigits-1,sum,"%s%d"%(pref,i),prefsum+i)

def do (n):
    for i in range (1,n*9+1):
        recur (n,i)

do (2)
do (3)

输出(2和3):

11, sum=2          111, sum=3
12, sum=3          112, sum=4
21, sum=3          121, sum=4
13, sum=4          211, sum=4
22, sum=4          113, sum=5
31, sum=4          122, sum=5
14, sum=5          131, sum=5
23, sum=5          212, sum=5
32, sum=5          221, sum=5
41, sum=5          311, sum=5
15, sum=6          114, sum=6
 :    :             :     :
89, sum=17         989, sum=26
98, sum=17         998, sum=26
99, sum=18         999, sum=27

请记住,解决方案仍然可以进行一些优化 - 我将其保留为初始形式,以显示优雅的递归方式。接下来是一个纯迭代的解决方案,但我仍然更喜欢递归的解决方案。

运行以下程序并在UNIX下使用sortawk来获取所需的顺序。例如:

go | sort | awk '{print $2}'

请注意,这使用外部工具进行排序,但您可以在C代码中轻松排序(内存允许)。

#include <stdio.h>

int main (void) {
    int i, sum, carry, size;
    int *pDigit;

    // Choose your desired size.

    size = 2;

    // Allocate and initialise digits.

    if ((pDigit = malloc (size * sizeof (int))) == NULL) {
        fprintf (stderr, "No memory\n");
        return 1;
    )

    for (i = 0; i < size; i++)
        pDigit[i] = 1;

    // Loop until overflow.

    carry = 0;
    while (carry != 1) {
        // Work out sum, then output it with number.
        // Line is sssssssssssssssssss ddddd
        //   where sss...sss is the fixed-width sum, zero padded on left (for sort)
        //   and ddd...ddd is the actual number.

        sum = 0;
        for (i = 0; i < size; i++)
            sum += pDigit[i];

        printf ("%020d ", sum);
        for (i = 0; i < size; i++)
            printf ("%d", pDigit[i]);
        printf ("\n");

        // Advance to next number.

        carry = 1;
        for (i = 0; i < size; i++) {
            pDigit[size-i-1] = pDigit[size-i-1] + carry;
            if (pDigit[size-i-1] == 10)
                pDigit[size-i-1] = 1;
            else
                carry = 0;
        }
    }

    return 0;
}

答案 1 :(得分:4)

您可以使用std::next_permutation吗?

  

next_permutation()函数   试图改变给定的范围   元素[开始,结束]进入下一个   按字典顺序排列更大的排列   元素。如果成功,那就是   返回true,否则返回   假的。

     

如果有严格的弱订货功能   提供了对象cmp,用于   代替&lt;算术时比较   元件。

请参阅:previous SO answer

答案 2 :(得分:0)

如果你使用什么样的模式并不重要,只要有一个模式(你的帖子中并不完全清楚你是否有特定的模式)那么对于n = 3,从111开始并递增,直至到达999

顺便说一下,你所要求的术语并不完全是“排列”。

答案 3 :(得分:0)

您可以尝试将问题减少到两个存储桶:

两个桶拆分很简单:从桶A中的所有减号和桶B中的一个开始,然后从A到B放一个,直到A只包含一个。

然后只有三个桶分裂:从桶A中的所有减去2开始,在B和C中分别开始一个。将A减少一个并收集B和C中的三个桶分裂,重复直到A只包含一个。