对于给定的整数a,找到总和为a的所有唯一正整数组合

时间:2014-06-04 20:04:44

标签: c++ recursion combinations permutation

不是作业问题。 我正在处理问题here,我遇到了这个问题。 有人回答了。我已经尝试了很多来理解使用的递归,但我无法得到它。有人可以向我解释一下吗?

  

编写一个函数,对于给定的数字,通过使用加法和任何等于或小于此数字且大于零的数字,打印出所有不同的方法来制作此数字。

例如,给定a = 5,我们有以下七种方法来弥补5:

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

该网站的解决方案是C ++:

void printSeq( int num , int a[] , int len , int s )
{
    if( num <= 0 )
    {
        for( int j = 0 ; j < len ; j++ )
            cout << a[ j ] << "," ;
        cout << endl;

        return;
    }

    for(int i = s ; i <= num ; i++)
    {
        a[ len ] = i;
        printSeq( num - i , a , len + 1 , i );
    }
}

int main()
{
    int a[5];
    printSeq(5,a,0,1);
    cin.get();
    return 0;
} 

2 个答案:

答案 0 :(得分:13)

当遇到这样的问题时,通常最好从编辑器/ IDE退一步,通过在白板上绘制一个简单的案例来思考问题。甚至还没做伪代码,只是绘制一个流程图,说明这个问题的一个简单案例(例如a = 3)将如何一直向下。此外,首先不要担心重复组合。尝试找到一个解决方案,为您提供所有需要的组合,然后改进您的解决方案,不给您重复。在这种情况下,为什么不查看a = 3的可管理案例?让我为你画一幅画。绿色复选标记表示我们已达到有效组合,红叉表示组合无效。

enter image description here

如您所见,我们从三个空子组合开始,然后通过在每个子组合中附加一个数字来构建三个新的子组合。我们想要检查所有可能的路径,因此我们选择1,2和3,最后得到[1][2][3]。如果组合中的数字总和等于3,我们找到了一个有效的组合,所以我们可以停下来检查这条路径。如果组合中的数字总和超过3,则组合无效,我们也可以停止。如果不是这种情况,我们只需继续构建组合,直到我们得出有效或无效的解决方案。

由于您的问题似乎主要是关于如何为这类问题制定递归解决方案而不是特定语法,而您恰好找到了C ++解决方案,我将提供Python解决方案(它几乎看起来像伪代码一样,它知道它。)

def getcombs(a, combo = None):
    # initialize combo on first call of the function
    if combo == None:
        combo = []

    combosum = sum(combo) # sum of numbers in the combo, note that sum([]) == 0
    # simple case: we have a valid combination of numbers, i.e. combosum == a
    if combosum == a:
        yield combo # this simply gives us that combination, no recursion here!
    # recursive case: the combination of numbers does not sum to a (yet)
    else:
        for number in range(1, a + 1): # try each number from 1 to a               
            if combosum + number <= a:  # only proceed if we don't exceed a
                extcombo = combo + [number] # append the number to the combo
                # give me all valid combinations c that can be built from extcombo
                for c in getcombs(a, extcombo):
                    yield c

让我们测试一下代码!

>>> combos = getcombs(3)
>>> for combo in combos: print(combo)
... 
[1, 1, 1]
[1, 2]
[2, 1]
[3]

这似乎工作得很好,是a = 5的另一个测试:

>>> combos = getcombs(5)
>>> for combo in combos: print(combo)
... 
[1, 1, 1, 1, 1]
[1, 1, 1, 2]
[1, 1, 2, 1]
[1, 1, 3]
[1, 2, 1, 1]
[1, 2, 2]
[1, 3, 1]
[1, 4]
[2, 1, 1, 1]
[2, 1, 2]
[2, 2, 1]
[2, 3]
[3, 1, 1]
[3, 2]
[4, 1]
[5]

解决方案包括我们正在寻找的所有七种组合,但代码仍然会产生重复。您可能已经注意到,没有必要使用小于先前所选数字的数字来生成所有组合。因此,让我们添加一些代码,这些代码只会为不小于组合中当前最后一个数字的数字开始构建extcombo。如果组合为空,我们只需将前一个数字设置为1。

def getcombs(a, combo = None):
    # initialize combo on first call of the function
    if combo == None:
        combo = []

    combosum = sum(combo) # sum of numbers in combo, note that sum([]) == 0
    # simple case: we have a valid combination of numbers, i.e. combosum == a
    if combosum == a:
        yield combo # this simply gives us that combination, no recursion here!
    # recursive case: the combination of numbers does not sum to a (yet)
    else:
        lastnumber = combo[-1] if combo else 1 # last number appended
        for number in range(lastnumber, a + 1): # try each number between lastnumber and a
            if combosum + number <= a:
                extcombo = combo + [number] # append the number to the combo
                # give me all valid combinations that can be built from extcombo
                for c in getcombs(a, extcombo):
                    yield c

再次,让我们测试一下代码!

>>> combo = getcombs(5)
>>> for combo in combos: print(combo)
... 
[1, 1, 1, 1, 1]
[1, 1, 1, 2]
[1, 1, 3]
[1, 2, 2]
[1, 4]
[2, 3]
[5]

所提出的解决方案可能不是最有效的解决方案,但希望它会鼓励您递归思考。逐步解决问题,为小输入绘制一个简单的案例,一次解决一个问题。

答案 1 :(得分:0)

暂时搁置解决方案并查看问题本身:

将此问题与数组(或任何递归算法)的插入排序进行比较。在执行期间的任何时候插入排序我们有一个排序的数组的一部分和另一个未排序的部分。我们从未分类的部分中选择一个元素,并在排序的部分中找到它,从而扩展已排序的部分,使问题变小。

如果出现这个问题,我们可以选择固定数量的元素,即整数1到问题中的数字(让我们称之为N),作为总结的序列的一部分到N。

在任何时候我们都收集了一些总和小于N的数字(比如X),将问题减少到NX大小,同时也减少了我们从1..N到1 ...(NX)的选择以进行下一次递归

解决方案显而易见,每个选择从1到(N-X)并递归地进行直到X = N.每次算法达到X = N时,意味着找到排列。

注意:我在解决方案中看到的一个问题是,它需要知道预先找到的排列数。 int a[5]; 如果该值未知,这可能会导致问题。