Reputation: 190
I am trying to find the all possible values which are result of sum of values of given array. For example if given array is a = [50,100,120,260,360]
then result will be [0,50,100,120,150,170,200,220,240,250,260,....]
. How to implement it ?
I found one article realted to it but that is about to find the value which can't be formed using given array.
Find smallest number which can't be formed by values of given array
I found one more discussion related to something this but it is all about mathematics and I am still unable to understand how to implement it. You can have a look at it Find all possible values which can be formed using some values
Any algorithm or any code in C# could help.
Edit
We can use a single value many times.
more results could be 270 (50*1 + 100*1 + 120) , 300 (100*3), 310 (50 * 1 + 260 *1) etc.
Upvotes: 1
Views: 201
Reputation: 205579
Here is the most efficient way to do that:
public static class Algorithms
{
public static IEnumerable<int> AllSums(this int[] source)
{
var indices = new int[source.Length];
for (int count = 0, sum = 0, next = 0; ; next++)
{
if (next < source.Length)
{
indices[count++] = next;
sum += source[next];
yield return sum;
}
else
{
if (count == 0) break;
next = indices[--count];
sum -= source[next];
}
}
}
}
Sample usage:
var source = new[] { 50, 100, 120, 260, 360 };
Console.WriteLine("Source: {" + string.Join(", ", source.Select(n => n.ToString())) + "}");
Console.WriteLine("Sums: {" + string.Join(", ", source.AllSums().Select(n => n.ToString())) + "}");
or
var source = new[] { 50, 100, 120, 260, 360 };
foreach (var sum in source.AllSums())
{
// do something with the sum
}
Upvotes: 0
Reputation: 117027
This is what I use:
Func<IEnumerable<int>, IEnumerable<IEnumerable<int>>> getAllSubsets = null;
getAllSubsets = xs =>
(xs == null || !xs.Any())
? Enumerable.Empty<IEnumerable<int>>()
: xs.Skip(1).Any()
? getAllSubsets(xs.Skip(1))
.SelectMany(ys => new[] { ys, xs.Take(1).Concat(ys) })
: new[] { Enumerable.Empty<int>(), xs.Take(1) };
Then you can do this:
var a = new [] { 50, 100, 120, 260, 360 };
Console.WriteLine(String.Join(", ", getAllSubsets(a).Select(x => x.Sum()).OrderBy(x => x)));
I get this:
0, 50, 100, 120, 150, 170, 220, 260, 270, 310, 360, 360, 380, 410, 410, 430, 460, 480, 480, 510, 530, 530, 580, 620, 630, 670, 720, 740, 770, 790, 840, 890
Knowing that values can be repeated then this is a way to go:
public IEnumerable<int> GenerateAllSums(int[] array)
{
var buffer = new LinkedList<int>();
buffer.AddFirst(0);
while (true)
{
var first = buffer.First;
var nexts = array.Select(a => first.Value + a);
foreach (var next in nexts)
{
var x = buffer.First;
while (x.Value < next)
{
x = x.Next;
if (x == null)
{
break;
}
}
if (x == null)
{
buffer.AddLast(next);
}
else if (x.Value != next)
{
buffer.AddBefore(x, next);
}
}
buffer.RemoveFirst();
yield return first.Value;
}
}
I can call it like so:
var a = new [] { 50, 100, 120, 260, 360, };
Console.WriteLine(String.Join(", ", GenerateAllSums(a).Take(100)));
It's important to note that the .Take(...)
is now vital as the sequence is infinite.
Given the .Take(100)
I get this result:
0, 50, 100, 120, 150, 170, 200, 220, 240, 250, 260, 270, 290, 300, 310, 320, 340, 350, 360, 370, 380, 390, 400, 410, 420, 430, 440, 450, 460, 470, 480, 490, 500, 510, 520, 530, 540, 550, 560, 570, 580, 590, 600, 610, 620, 630, 640, 650, 660, 670, 680, 690, 700, 710, 720, 730, 740, 750, 760, 770, 780, 790, 800, 810, 820, 830, 840, 850, 860, 870, 880, 890, 900, 910, 920, 930, 940, 950, 960, 970, 980, 990, 1000, 1010, 1020, 1030, 1040, 1050, 1060, 1070, 1080, 1090, 1100, 1110, 1120, 1130, 1140, 1150, 1160, 1170
Upvotes: 3
Reputation: 456
var repeat = 8;
int[] source = new int[] {
50, 100, 120, 260, 360
};
List < int > results = new List < int > ();
for (int i = 0; i < Math.Pow(repeat, source.Length); i++) {
var sum = 0;
var bin = Convert.ToString(i, repeat);
for (var j = 0; j < bin.Length; j++) {
var pos = int.Parse(bin[j].ToString());
if (0 < pos) {
sum += source[j] * pos;
}
}
results.Add(sum);
}
Console.WriteLine(results.Union(source).Distinct().OrderBy(x = > x));
Upvotes: 0
Reputation: 415
Find all subset of your array using something like this then sum, you will get all the possible values if you don't need the duplicated one remove them.
int[] source = new int[] { 50,100,120,260,360 };
for (int i = 0; i < Math.Pow(2, source.Length); i++)
{
int[] combination = new int[source.Length];
for (int j = 0; j < source.Length; j++)
{
if ((i & (1 << (source.Length - j - 1))) != 0)
{
combination[j] = source[j];
}
}
Console.WriteLine("[{0}, {1}, {2}]", combination[0], combination[1], combination[2]);
}
Upvotes: 1