【问题标题】:Generating all distinct partitions of a number生成数字的所有不同分区
【发布时间】:2013-01-04 18:31:11
【问题描述】:

我正在尝试编写一个 C 代码来生成所有可能的分区(分成 2 个或更多部分),其中包含给定数量的 distinct 元素。给定分区的所有数字的总和应该等于给定的数字。例如,对于输入 n = 6,具有 2 个或更多具有不同元素的元素的所有可能分区是:

  • 1、5
  • 1、2、3
  • 2, 4

我认为递归方法应该可行,但我无法处理不同元素的附加约束。非常感谢 C/C++/Java 中的伪代码或示例代码。

谢谢!

编辑:如果它使事情变得更容易,我可以忽略分区具有至少 2 个元素的限制。这将允许将数字本身添加到列表中(例如,6 本身将是一个微不足道但有效的分区)。

【问题讨论】:

标签: c algorithm recursion numbers decomposition


【解决方案1】:

您尝试做的事情对我来说没有多大意义,但这是我的处理方式。

首先,我将创建一个循环,将 i 从 1 迭代到 n - 1。在第一个循环中,您可以添加分区 1,即。然后我会使用i 中的值进行递归,以获取所有也可以添加到 1 的子分区。

然后继续到2,以此类推。

【讨论】:

  • 我相信在第 i 步,他可以从 i+1 迭代到余数-i;余数-i 以上的所有数字都已被考虑,并属于重复分区。
【解决方案2】:

首先,编写一个递归算法,返回所有分区,包括那些包含重复的分区。

其次,编写一个算法来消除包含重复元素的分区。

编辑:

您可以通过避免对已经看到的数字进行递归调用来避免重复的结果。伪代码:

Partitions(n, alreadySeen)
 1. if n = 0 then return {[]}
 2. else then
 3.    results = {}
 4.    for i = 1 to n do
 5.       if i in alreadySeen then continue
 6.       else then
 7.          subresults = Partitions(n - i, alreadySeen UNION {i})
 8.          for subresult in subresults do
 9.             results = results UNION {[i] APPEND subresult}
10.    return results

编辑:

您还可以避免多次生成相同的结果。通过修改循环的范围来做到这一点,这样您就只能以单调递增的方式添加新元素:

Partitions(n, mustBeGreaterThan)
1. if n = 0 then return {[]}
2. else then
3.    results = {}
4.    for i = (mustBeGreaterThan + 1) to n do
5.       subresults = Partitions(n - i, i)
6.       for subresult in subresults do
7.          results = results UNION {[i] APPEND subresult}
8.    return results

【讨论】:

  • 谢谢!具有重复的分区数量比没有重复的分区数量增长得快得多。我希望避免大量额外的工作。
  • @mayank 请看我的编辑。这应该可以解决您的问题,而不会产生无效的答案。基本上,这解决了“查找不包含任何一组元素的数字的所有分区”的问题。你的问题可以归结为这个问题;用 alreadySeen = {} 调用给定的算法。
  • 谢谢!我试图理解它,这看起来不错。我认为它可能会多次生成相同的分区集,但 results 的 UNION 运算符应该处理它。我试试看。
  • @mayank 确实,它可能。当然,这个问题可以通过只循环大于或等于最后一个数字的数字来解决。实际上,您可以摆脱整个“alreadySeen”业务并解决以下问题:找到所有由严格大于给定数字的数字组成的分区”。您的问题也可以归结为那个;用 0 调用函数作为“必须大于此”参数。然后,将 for 循环更改为从 mustBeGreaterThan + 1 开始,而不是从 1 开始。
  • 如果我们只需要计算有多少个选项,这是否可以优化?
【解决方案3】:

你根本不需要递归。数字列表本质上是一个堆栈,通过按顺序迭代可以确保没有重复。

这里有一个版本,说明了我的意思(你标记了这个 C,所以我用 C 编写了它。在 C++ 中,你可以使用带有 push 和 pop 的动态容器,并对其进行大量整理)。

#include <stdio.h>
#include <stdlib.h>

void partition(int part)
{
int *parts;
int *ptr;
int i;
int idx = 0;
int tot = 0;
int cur = 1;
int max = 1;

    while((max * (max + 1)) / 2 <= part) max++;

    ptr = parts = malloc(sizeof(int) * max);

    for(;;) {
        if((tot += *ptr++ = cur++) < part) continue;

        if(tot == part) {
            for(i = 0 ; i < ptr-parts ; i++) {printf("%d ",parts[i]);}
            printf("\n");
        }

        do {
            if(ptr == parts) {free(parts); return;}
            tot -= cur = *--ptr;
        } while(++cur + tot > part);
    }
}

int main(int argc, char* argv[])
{
    partition(6);
    return 0;
}

【讨论】:

【解决方案4】:

我草拟了这个不应该产生重复的解决方案(它可以被美化和优化):

void partitions(int target, int curr, int* array, int idx)
{
    if (curr + array[idx] == target)
    {
        for (int i=0; i <= idx; i++)
            cout << array[i] << " ";
        cout << endl;       
        return;
    }
    else if (curr + array[idx] > target)
    {
        return;
    }
    else
    {
        for(int i = array[idx]+1; i < target; i++)
        {
            array[idx+1] = i;
            partitions(target, curr + array[idx], array, idx+1);
        }
    }
}

int main(){
    int array[100];
    int N = 6;
    for(int i = 1; i < N; i++)
    {
        array[0] = i;
        partitions(N, 0, array, 0);
    }
}

【讨论】:

  • 非常感谢!这似乎工作得很好!我会尝试理解并测试它。
【解决方案5】:

这是另一种基于迭代算法的解决方案。它比@imreal 的算法快得多,比@JasonD 的算法快一点。

计算 n = 100 所需的时间

$ time ./randy > /dev/null
./randy > /dev/null  0.39s user 0.00s system 99% cpu 0.393 total
$ time ./jasond > /dev/null
./jasond > /dev/null  0.43s user 0.00s system 99% cpu 0.438 total
$ time ./imreal > /dev/null
./imreal > /dev/null  3.28s user 0.13s system 99% cpu 3.435 total
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int next_partition(int *a, int* kp) {
    int k = *kp;
    int i, t, b;

    if (k == 1) return 0;
    if (a[k - 1] - a[k - 2] > 2) {
        b = a[k - 2] + 1;
        a[k - 2] = b;
        t = a[k - 1] - 1;
        i = k - 1;
        while (t >= 2*b + 3) {
            b += 1;
            a[i] = b;
            t -= b;
            i += 1;
        }
        a[i] = t;
        k = i + 1;
    } else {
        a[k - 2] = a[k - 2] + a[k - 1];
        a[k - 1] = 0;
        k = k - 1;
    }
    *kp = k;
    return 1;
}

int main(int argc, char* argv[])
{
    int n = 100;
    int m = floor(0.5 * (sqrt(8*n + 1) - 1));
    int i, k;
    int *a;
    a = malloc(m * sizeof(int));
    k = m;
    for (i = 0; i < m - 1; i++) {
        a[i] = i + 1;
    }
    a[m - 1] = n - m*(m-1)/2;

    for (i = 0; i < k; i++) printf("%d ", a[i]);
    printf("\n");

    while (next_partition(a, &k)) {
        for (i = 0; i < k; i++) printf("%d ", a[i]);
        printf("\n");
    }
    free(a);
    return 0;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-08-01
    • 1970-01-01
    • 2013-09-10
    • 1970-01-01
    • 2019-07-19
    • 2015-05-24
    相关资源
    最近更新 更多