用于从一组数字中确定所有可能的和的非递归算法
Non-recursive algorithm for determine all sums possible from set of numbers
我正在寻找一种非递归算法(最好是在 C# 中),它将生成一组正数的所有可能和的列表。
例如对于一组三个数字“1,2,3”,可能有以下七个和:
1
2
3
1+2=3
1+3=4
2+3=5
1+2+3=6
最大集合大小将在50左右。我知道如何递归解决这个问题,但过去在处理类似问题时受到调用堆栈的限制,所以这次想避免它。
如果你只需要所有可能的总和,那么你可以使用这个函数。
public static IEnumerable<int> GetSums(List<int> list)
{
return from m in Enumerable.Range(0, 1 << list.Count)
select
(from i in Enumerable.Range(0, list.Count)
where (m & (1 << i)) != 0
select list[i]).Sum();
}
然后,就这样称呼它:
var result = GetSums(myList).ToList();
附加信息:
您也可以使用此方法生成组合(source):
public static IEnumerable<IEnumerable<T>> GetPowerSet<T>(List<T> list)
{
return from m in Enumerable.Range(0, 1 << list.Count)
select
from i in Enumerable.Range(0, list.Count)
where (m & (1 << i)) != 0
select list[i];
}
并借助 System.Linq
命名空间中的 Sum()
方法求出所有组合的总和:
var result = GetPowerSet(myList).Select(x => x.Sum()).ToList();
子集的和与子集直接对应,子集也与二进制序列直接对应。如果您的集合中有五个项目,您想要迭代从 00000 到 11111 的所有位序列。等效地,您想要从 0 迭代到 2^5-1。如果某位设置为 1,则应将该值包括在总和中。所以,像这样:
for i = 0 to 2^n-1
sum = 0
for j = 0 to n - 1
if i & (1 << j) then
sum += items[j]
yield return sum
显然,这是伪代码,不处理大于 i 使用的位数的 n 值,但这将是一次长时间的迭代。这至少应该让你开始。
如果所有数字的总和受到可靠值的限制,则存在复杂度为 O(N * MaxSum) 的 DP 解决方案,否则有 O(2^N) 种可能的总和。
DP方案(Delphi):
procedure GenerateAllSums(const A: array of Integer);
var
ASums: array of Boolean;
S, i, j: Integer;
begin
//find maximal possible sum
S := 0;
for i := 0 to High(A) do
S := S + A[i];
//make array for possible sums
SetLength(ASums, S + 1);
ASums[0] := True; // all others - false
for i := 0 to High(A) do
for j := S - A[i] downto 0 do
if ASums[j] then
ASums[j + A[i]] := True;
//Now 'True' elements of ASums denote possible sum values
end;
我正在寻找一种非递归算法(最好是在 C# 中),它将生成一组正数的所有可能和的列表。
例如对于一组三个数字“1,2,3”,可能有以下七个和:
1
2
3
1+2=3
1+3=4
2+3=5
1+2+3=6
最大集合大小将在50左右。我知道如何递归解决这个问题,但过去在处理类似问题时受到调用堆栈的限制,所以这次想避免它。
如果你只需要所有可能的总和,那么你可以使用这个函数。
public static IEnumerable<int> GetSums(List<int> list)
{
return from m in Enumerable.Range(0, 1 << list.Count)
select
(from i in Enumerable.Range(0, list.Count)
where (m & (1 << i)) != 0
select list[i]).Sum();
}
然后,就这样称呼它:
var result = GetSums(myList).ToList();
附加信息:
您也可以使用此方法生成组合(source):
public static IEnumerable<IEnumerable<T>> GetPowerSet<T>(List<T> list)
{
return from m in Enumerable.Range(0, 1 << list.Count)
select
from i in Enumerable.Range(0, list.Count)
where (m & (1 << i)) != 0
select list[i];
}
并借助 System.Linq
命名空间中的 Sum()
方法求出所有组合的总和:
var result = GetPowerSet(myList).Select(x => x.Sum()).ToList();
子集的和与子集直接对应,子集也与二进制序列直接对应。如果您的集合中有五个项目,您想要迭代从 00000 到 11111 的所有位序列。等效地,您想要从 0 迭代到 2^5-1。如果某位设置为 1,则应将该值包括在总和中。所以,像这样:
for i = 0 to 2^n-1
sum = 0
for j = 0 to n - 1
if i & (1 << j) then
sum += items[j]
yield return sum
显然,这是伪代码,不处理大于 i 使用的位数的 n 值,但这将是一次长时间的迭代。这至少应该让你开始。
如果所有数字的总和受到可靠值的限制,则存在复杂度为 O(N * MaxSum) 的 DP 解决方案,否则有 O(2^N) 种可能的总和。
DP方案(Delphi):
procedure GenerateAllSums(const A: array of Integer);
var
ASums: array of Boolean;
S, i, j: Integer;
begin
//find maximal possible sum
S := 0;
for i := 0 to High(A) do
S := S + A[i];
//make array for possible sums
SetLength(ASums, S + 1);
ASums[0] := True; // all others - false
for i := 0 to High(A) do
for j := S - A[i] downto 0 do
if ASums[j] then
ASums[j + A[i]] := True;
//Now 'True' elements of ASums denote possible sum values
end;