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

发布于 2024-08-11 00:45:32 字数 267 浏览 10 评论 0原文

我希望按以下顺序生成 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

求和组内的顺序并不重要。

任何帮助,想法将不胜感激

I'm looking to generate all possible values of n-digit number, in the following order, where the sequence is dictated by the sum of the individual digits.

For example, with 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

The order within the sum group is not important.

Any help, ideas would be appreciated

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

你的背包 2024-08-18 00:45:32

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

但是,如果语言确实支持它,那么递归解决方案要优雅得多。

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

但您需要了解,处理 n 个数字的堆栈深度仅相对于 log10n 增长。换句话说,每个数字只能获得一个额外的堆栈帧(只有 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;
}

You can always turn a recursive problem into an iterative one if you maintain your own stack of important data - that's if the reason for avoiding recursion is that the language doesn't support it.

But, if the language does support it, then recursive solutions are far more elegant.

The only other reason I can think of for avoiding recursion is limited stack depth. In that case an iterative conversion of a recursive solution will mitigate the problem by not requiring as much stack space.

But you need to understand that the stack depth for processing n numbers only grows relative to log10n. In other words, you only get an extra stack frame per digit (only 10 stack frames to handle the full range of 32-bit integers).

Aside: by the time you get to that point, you're algorithm will be taking so long to run, stack frames will be the least of your problems :-)

Here's a recursive Python solution:

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)

which outputs (for 2 and 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

Keep in mind that solution could still be optimized somewhat - I left it in its initial form to show how elegant recursion can be. A pure-iterative solution follows, but I still prefer the recursive one.

Run the following program and use sort and awk under UNIX to get the desired order. For example:

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

Note that this uses external tools to do the sorting but you could just as easily sort within the C code (memory permitting).

#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;
}
九局 2024-08-18 00:45:32

您可以使用 std::next_permutation 吗?

next_permutation() 函数
尝试改变给定范围
元素 [start,end) 进入下一个
字典顺序更大排列
的元素。如果成功的话
返回 true,否则返回
错误。

如果严格弱排序函数
提供了 cmp 对象,它用于
代替<比较时的运算符
元素。

请参阅:上一个答案

Can you use std::next_permutation?

The next_permutation() function
attempts to transform the given range
of elements [start,end) into the next
lexicographically greater permutation
of elements. If it succeeds, it
returns true, otherwise, it returns
false.

If a strict weak ordering function
object cmp is provided, it is used in
lieu of the < operator when comparing
elements.

See this: previous SO answer

梦回梦里 2024-08-18 00:45:32

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

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

If it doesn't matter what pattern you use so long as there is a pattern (it's not entirely clear from your post whether you have a specific pattern in mind) then for n=3, start with 111 and increment until you reach 999.

By the way, the term for what you're asking for isn't exactly "permutations".

痞味浪人 2024-08-18 00:45:32

您可以尝试将问题简化为两个桶:

两个​​桶的拆分很简单:从桶 A 中全部减一、桶 B 中减 1 开始,然后将 A 中的 1 放入 B 中,直到 A 只包含 1。

三个存储桶拆分就是:从存储桶 A 中全部减去 2 开始,B 和 C 中各减 1。将 A 减 1,并收集 B 和 C 中所有两个存储桶拆分为 3 个,重复直到 A 只包含 1。

You can try to reduce your problem to two buckets:

Two bucket splits are simple: Start with all minus one in bucket A and one in bucket B, then put one from A into B until A contains only one.

Three bucket splits are then just: Start with all minus two in bucket A and one each in B and C. Reduce A by one and collect all two bucket splits of three in B and C, repeat until A contains only one.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文