我正在尝试提出一种算法,该算法将打印出所有可能的方法来求和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
答案 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