Reputation: 663
Here's what I mean. Suppose S = {1, 4}
and N = 5
. The ordered arrangements of elements in the set S
would be like
{1}, {4}, {1,1}, {1,4}, {4,1}, {4,4}, {1,1,1}, ....
and the ones that sum up to N
are
{1,4}, {4, 1}, {1,1,1,1,1}
I want an algorithm that finds those efficiently.
My "brute force" way would be like
static IEnumerable<IEnumerable<int>> OrderedArrangements(IEnumerable<int> nums, int k)
{
var singles = nums.Select(i => new int[] {i} );
var cumulative = singles;
for(int j = 2; j <= k; ++j)
{
var last = cumulative.Where(list => list.Count() == (j - 1));
var next = from x in singles
from y in last
select x.Concat(y);
cumulative = cumulative.Concat(next);
}
return cumulative;
}
and then
int sumToN = OrderedArrangements(new int[] {1, 4}, N)
.Where(x => x.Sum() == N);
but I'm wondering if there's an obvious and more efficient way to do this.
Upvotes: 1
Views: 98
Reputation: 591
Just in case the above answer isn't clear enough, you could try straight forward recursion e.g.
...
/ \
(1) (4)
/ \ / \
(1)(4) (1)(4)
static void f(int sum, int n, String str, int[] arr){
if (n == sum){
Console.WriteLine(str);
return;
}
if (n > sum) return;
for (int i = 0; i < arr.Length; i++){
f(sum, n + arr[i], str + arr[i].ToString(), arr);
}
}
static void Main(string[] args){
int[] arr = { 1, 4 };
f(5, 0, "", arr);
}
Where sum
is N
in your question, n
is initialized to 0
, str
is initialized to ""
and arr
is S
in your question.
output:
11111
14
41
Upvotes: 1
Reputation: 117027
This works for me:
static IEnumerable<IEnumerable<int>> OrderedArrangements(IEnumerable<int> nums, int k)
{
return
k <= 0
? new [] { Enumerable.Empty<int>() }
: nums
.SelectMany(
n => OrderedArrangements(nums, k - n),
(n, ns) => new [] { n }.Concat(ns))
.Where(ns => ns.Sum() == k);
}
The result of OrderedArrangements(new [] { 1, 4 }, 5)
is:
I ran this performance testing code:
Func<Func<IEnumerable<IEnumerable<int>>>, double> measure = f =>
{
var sw = Stopwatch.StartNew();
var result = f();
sw.Stop();
return sw.Elapsed.TotalMilliseconds;
};
var n = 200;
var a = 0.0;
var b = 0.0;
for (var i = 0; i < n; i++)
{
a += measure(() => OrderedArrangements_A(new [] { 1, 4, 9, 13 }, 50000));
b += measure(() => OrderedArrangements_B(new [] { 1, 4, 9, 13 }, 50000));
}
OrderedArrangements_A
is the OP's code and OrderedArrangements_B
is mine.
I got an average of 15.6ms for "A" and 0.004ms for "B". My code run about 3,895 times faster for this test.
Upvotes: 0