打印所有方式来求和n个整数,以便它们总计给定的总和。

时间:2010-04-07 15:35:15

标签: algorithm math

我正在尝试提出一种算法,该算法将打印出所有可能的方法来求和N个整数,以便它们总计给定值。

实施例。打印所有方法,将4个整数相加,使它们总和为5。

结果应该是:

5 0 0 0
4 1 0 0
3 2 0 0
3 1 1 0
2 3 0 0
2 2 1 0
2 1 2 0
2 1 1 1
1 4 0 0
1 3 1 0 
1 2 2 0
1 2 1 1
1 1 3 0
1 1 2 1
1 1 1 2

7 个答案:

答案 0 :(得分:2)

这是基于Alinium的代码。
我对它进行了修改,因此它打印出所有可能的组合,因为他已经完成了所有的排列 另外,当n = 1时,我认为你不需要for循环,因为在这种情况下,只有一个数字应该导致总和相等。
进行各种其他修改以使边界情况起作用。

def sum(n, value):
    arr = [0]*n  # create an array of size n, filled with zeroes
    sumRecursive(n, value, 0, n, arr);

def sumRecursive(n, value, sumSoFar, topLevel, arr):
    if n == 1:
        if sumSoFar <= value:
            #Make sure it's in ascending order (or only level)
            if topLevel == 1 or (value - sumSoFar >= arr[-2]):
                arr[(-1)] = value - sumSoFar #put it in the n_th last index of arr
                print arr
    elif n > 0:
        #Make sure it's in ascending order
        start = 0
        if (n != topLevel):
            start = arr[(-1*n)-1]   #the value before this element

        for i in range(start, value+1): # i = start...value
            arr[(-1*n)] = i  # put i in the n_th last index of arr
            sumRecursive(n-1, value, sumSoFar + i, topLevel, arr)

Runing sums(4,5)返回:
[0,0,0,5]
[0,0,1,4]
[0,0,2,3]
[0,1,1,3]
[1,1,1,2]

答案 1 :(得分:2)

在纯数学中,将整数求和以获得给定总数的方法称为分区。如果你谷歌的“整数分区”有很多信息。您正在寻找具有特定数量元素的整数分区。我相信你可以采用一种已知的发电机制并适应这种额外的条件。维基百科对主题Partition_(number_theory)有一个很好的概述。 Mathematica甚至可以执行您想要的功能:IntegerPartitions[5, 4]

答案 2 :(得分:1)

解决问题的关键是递归。这是python中的一个有效实现。它打印出所有可能的排列,总计达到总数。你可能想要摆脱重复的组合,可能通过使用一些Set或散列机制来过滤它们。

def sum(n, value):
    arr = [0]*n  # create an array of size n, filled with zeroes
    sumRecursive(n, value, 0, n, arr);

def sumRecursive(n, value, sumSoFar, topLevel, arr):
    if n == 1:
        if sumSoFar > value:
            return False
        else:
            for i in range(value+1): # i = 0...value
                if (sumSoFar + i) == value:
                    arr[(-1*n)] = i # put i in the n_th last index of arr
                    print arr;
                    return True

    else:
        for i in range(value+1): # i = 0...value
            arr[(-1*n)] = i  # put i in the n_th last index of arr
            if sumRecursive(n-1, value, sumSoFar + i, topLevel, arr):
                if (n == topLevel):
                    print "\n"

通过一些额外的努力,这可能会被简化,以摆脱我传递给递归函数的一些参数。正如redcayuga的伪代码所建议的,使用堆栈而不是手动管理数组也是一个更好的主意。

答案 3 :(得分:0)

我没有测试过这个:

  procedure allSum (int tot, int n, int desiredTotal) return int
       if n > 0
           int i = 
           for (int i = tot; i>=0; i--) {
               push i onto stack;
               allSum(tot-i, n-1, desiredTotal);
               pop top of stack
            }
        else if n==0
            if stack sums to desiredTotal then print the stack   end if
        end if

我确信有更好的方法可以做到这一点。

答案 4 :(得分:0)

我发现了一种基于Alinium代码的域规范的红宝石方式

class Domain_partition

    attr_reader :results,
                :domain,
                :sum,
                :size

    def initialize(_dom, _size, _sum)
        _dom.is_a?(Array) ? @domain=_dom.sort : @domain= _dom.to_a
        @results, @sum, @size = [], _sum, _size
        arr = [0]*size  # create an array of size n, filled with zeroes
        sumRecursive(size, 0, arr)
    end

    def sumRecursive(n, sumSoFar, arr)

        if n == 1
            #Make sure it's in ascending order (or only level)
            if sum - sumSoFar >= arr[-2] and @domain.include?(sum - sumSoFar)
                final_arr=Array.new(arr)
                final_arr[(-1)] = sum - sumSoFar #put it in the n_th last index of arr
                @results<<final_arr
            end

        elsif n > 1

            #********* dom_selector ********

            n != size ? start = arr[(-1*n)-1] : start = domain[0]
            dom_bounds=(start*(n-1)..domain.last*(n-1))

            restricted_dom=domain.select do |x|

                if x < start 
                    false; next
                end

                if size-n > 0
                    if dom_bounds.cover? sum-(arr.first(size-n).inject(:+)+x) then true
                    else false end  
                else 
                    dom_bounds.cover?(sum+x) ? true : false
                end
            end # ***************************

            for i in restricted_dom
                _arr=Array.new(arr)
                _arr[(-1*n)] = i 
                sumRecursive(n-1, sumSoFar + i, _arr)
            end
        end
    end
end 

a=Domain_partition.new (-6..6),10,0 
p a

b=Domain_partition.new [-4,-2,-1,1,2,3],10,0 
p b

答案 5 :(得分:0)

如果您对生成(词汇)有序整数分区感兴趣,即总和为N的唯一无序S正整数(无0),请尝试以下方法。 (无序仅表示[1,2,1]和[1,1,2]是相同的分区)

这个问题不需要递归,并且很快得到处理,因为找到下一个词法限制分区的概念实际上非常简单......

概念:从最后一个加数(整数)开始,找到两个加数之差大于1的第一个实例。此时将分区拆分为两个。从较高的整数中删除1(这将是一个部分中的最后一个整数)并将1加到较低的整数(后一部分的第一个整数)。然后找到后一部分的第一个词法排序分区,其中新的最大整数作为最大加数值。我使用Sage找到第一个词法分区,因为它快速闪亮,但没有它就很容易完成。最后,加入这两个部分瞧!你有N的下一个词法分区有S部分。

e.g。 [6,5,3,2,2] - &gt; [6,5],[3,2,2] - &gt; [6,4],[4,2,2] - &gt; [6,4],[4,3,1] - &gt; [6,4,4,3,1]

所以,在Python中并调用Sage来完成给定n和s部分的第一个词法分区的次要任务......

from sage.all import *

def most_even_partition(n,s): # The main function will need to recognize the most even partition possible (i.e. last lexical partition) so it can loop back to the first lexical partition if need be
    most_even = [int(floor(float(n)/float(s)))]*s
    _remainder = int(n%s)

    j = 0
    while _remainder > 0:
        most_even[j] += 1
        _remainder -= 1
        j += 1
    return most_even

def portion(alist, indices): 
    return [alist[i:j] for i, j in zip([0]+indices, indices+[None])]


def next_restricted_part(p,n,s):
    if p == most_even_partition(n,s):return Partitions(n,length=s).first()

    for i in enumerate(reversed(p)):
        if i[1] - p[-1] > 1:
            if i[0] == (s-1):
                return Partitions(n,length=s,max_part=(i[1]-1)).first()
            else:
                parts = portion(p,[s-i[0]-1]) # split p (soup?)
                h1 = parts[0]
                h2 = parts[1]
                next = list(Partitions(sum(h2),length=len(h2),max_part=(h2[0]-1)).first())
                return h1+next

如果你想要零(不是实际的整数分区),那么这些函数只需要很小的修改。

答案 6 :(得分:0)

试试这段代码。我希望它更容易理解。我测试了它,它产生了正确的序列。

void partition(int n, int m = 0)
{
    int i;
    // if the partition is done
    if(n == 0){
        // Output the result
        for(i = 0; i < m; ++i)
            printf("%d ", list[i]);
        printf("\n");
        return;
    }
    // Do the split from large to small int
    for(i = n; i > 0; --i){
        // if the number not partitioned or
        // willbe partitioned no larger than 
        // previous partition number
        if(m == 0 || i <= list[m - 1]){
            // store the partition int
            list[m] = i;
            // partition the rest
            partition(n - i, m + 1);
        }
    }
}

如果需要,请求澄清。

是输出之一

6 
5 1 
4 2 
4 1 1 
3 3 
3 2 1 
3 1 1 1 
2 2 2 
2 2 1 1 
2 1 1 1 1 
1 1 1 1 1 1 


10 
9 1 
8 2 
8 1 1 
7 3 
7 2 1 
7 1 1 1 
6 4 
6 3 1 
6 2 2 
6 2 1 1 
6 1 1 1 1 
5 5 
5 4 1 
5 3 2 
5 3 1 1 
5 2 2 1 
5 2 1 1 1 
5 1 1 1 1 1 
4 4 2 
4 4 1 1 
4 3 3 
4 3 2 1 
4 3 1 1 1 
4 2 2 2 
4 2 2 1 1 
4 2 1 1 1 1 
4 1 1 1 1 1 1 
3 3 3 1 
3 3 2 2 
3 3 2 1 1 
3 3 1 1 1 1 
3 2 2 2 1 
3 2 2 1 1 1 
3 2 1 1 1 1 1 
3 1 1 1 1 1 1 1 
2 2 2 2 2 
2 2 2 2 1 1 
2 2 2 1 1 1 1 
2 2 1 1 1 1 1 1 
2 1 1 1 1 1 1 1 1 
1 1 1 1 1 1 1 1 1 1