Proper way to exit from recursive loop - c#

I am trying to write a program to identify the occurrences of 3 consecutive integers in a given array of N numbers and replace them with the middle value by deleting the other two.
For example Input->55 99 99 100 101 101 34 35 36 5 28 7 50 50 51 52 52 24 13 14 15 5 6 7 37 31 37 38 39 36 40
Output->55 100 35 5 28 7 51 24 14 6 37 31 38 36 40
To achieve this i wrote this method which accepts array as an input and it returns the modified array.
//input
int[] original = new int[] { 1, 3, 4, 5, 5, 6, 8} ;
List<int> lstoriginal = new List<int>(original);
List<int> modified = Test(lstoriginal);
//method
public static List<int> Test(List<int> arrayInput)
{
for (i = 0; i < arrayInput.Count; i++)
{
if (i + 2 < arrayInput.Count)
{
if (arrayInput[i + 2] == arrayInput[i + 1] + 1
&& arrayInput[i + 2] == arrayInput[i] + 2)
{
arrayInput.RemoveAt(i + 2);
arrayInput.RemoveAt(i);
List<int> temp = arrayInput;
Test(temp);
}
}
}
return arrayInput;
}
Follwoing are the execution steps/result which i analyzed-
1-Initially if the test input is 1, 3, 4, 5, 5, 6, 8
2-When i=1 and it finds that 3,4,5 is in sequence it removes 3 and 5 and list becomes 1,4,5,6,8
3-Next time when i=1 then it finds 4,5,6 and it removes 4 and 6 and the new list is 1,5,8
4-i am expecting to exit from loop when i + 2 < arrayInput.Count returns false and trying to retrun the modified array immediately here the return statement gets executed but instead of return the result it again calls the Test(temp); statement few more times and then get exit. Please suggest

You actually don't need recursion at all. You can perform the task significantly faster by just moving i after you're removed your sequence. Here's a function that is much simpler and does the exact same thing. I tested it on tens of thousands of randomly generated unordered sequences.
public static List<int> Test2(List<int> arrayInput)
{
for (int i = 0; i < arrayInput.Count - 2; i++)
{
if (arrayInput[i + 2] == arrayInput[i + 1] + 1
&& arrayInput[i + 2] == arrayInput[i] + 2)
{
arrayInput.RemoveAt(i + 2);
arrayInput.RemoveAt(i);
i = Math.Max(-1, i - 3); // -1 'cause i++ in loop will increment it
}
}
return arrayInput;
}
That said, to answer your specific question, the best way to exit a recursive loop like your original is to change the signature of your recursive function to return a bool indicating whether or not it actually made any changes. When the first one returns with no changes, they all can exist, so your call to Test can be wrapped in if (!Test(...)) { return; }.
Here's the complete test and test data comparing your original to my modified version:
public static void Main()
{
const int COUNT = 10000;
var r = new Random();
int matchCount = 0;
var stopwatch1 = new Stopwatch();
var stopwatch2 = new Stopwatch();
for (int j = 0; j < COUNT; j++)
{
var list = new List<int>(100) {1};
for(int k=1; k<100; k++)
{
switch(r.Next(5))
{
case 0:
case 1:
case 2:
list.Add(list[k - 1] + 1);
break;
case 3:
list.Add(list[k - 1] + r.Next(2));
break;
case 4:
list.Add(list[k - 1] - r.Next(5));
break;
}
}
stopwatch1.Start();
List<int> copy1 = Test1(new List<int>(list));
stopwatch1.Stop();
stopwatch2.Start();
List<int> copy2 = Test2(new List<int>(list));
stopwatch2.Stop();
string list1 = String.Join(",", copy1);
string list2 = String.Join(",", copy2);
if (list1 == list2)
{
if (copy1.Count == list.Count)
{
Console.WriteLine("No change:" + list1);
}
else
{
matchCount++;
}
}
else
{
Console.WriteLine("MISMATCH:");
Console.WriteLine(" Orig : " + String.Join(",", list));
Console.WriteLine(" Test1 : " + list1);
Console.WriteLine(" Test2 : " + list2);
}
}
Console.WriteLine("Matches: " + matchCount);
Console.WriteLine("Elapsed 1: {0:#,##0} ms", stopwatch1.ElapsedMilliseconds);
Console.WriteLine("Elapsed 2: {0:#,##0} ms", stopwatch2.ElapsedMilliseconds);
}
public static List<int> Test1(List<int> arrayInput)
{
for (int i = 0; i < arrayInput.Count; i++)
{
if (i + 2 < arrayInput.Count)
{
if (arrayInput[i + 2] == arrayInput[i + 1] + 1
&& arrayInput[i + 2] == arrayInput[i] + 2)
{
arrayInput.RemoveAt(i + 2);
arrayInput.RemoveAt(i);
List<int> temp = arrayInput;
Test1(temp);
}
}
else
{ // modified part: return the array
return arrayInput;
}
}
return arrayInput;
}
//method
public static List<int> Test2(List<int> arrayInput)
{
for (int i = 0; i < arrayInput.Count - 2; i++)
{
if (arrayInput[i + 2] == arrayInput[i + 1] + 1
&& arrayInput[i + 2] == arrayInput[i] + 2)
{
arrayInput.RemoveAt(i + 2);
arrayInput.RemoveAt(i);
i = Math.Max(-1, i - 3); // -1 'cause i++ in loop will increment it
}
}
return arrayInput;
}

Please define "cannot exit". Do you mean the for keeps looping indefinitely? I don't see that happening from this code.
What it looks like to me:
This function will:
Step through the input, int by int. Checks to see if this int and the next 2 are sequential. Then it removes this one and the one after next, then feeds the result back into this same function. It then ignores any value this may have given us and continues on its merry way.
You have an input of 8,9,10
It starts to step through: i = 0 and all that.
so it finds that 8,9,10 are sequential, it then removes 8 and 9 and feeds that result into this same function.
So we start over again:
You have an input of 9
It starts to step through: i = 0 again.
it steps through and finds that there are not at least 3 values in the list, and returns the original.
We then completely ignore that result and continue the original loop above. Now i = 1, but there's only 1 thing in the arrayInput anymore, so it should end.
From what you're doing, I see no reason to make a recursive call. You're not doing anything with the result and even if you were, it would only help you if you had a collection like 8,9,10,10,11. Then the first call would trim it down to 9,10,11 and the recursive call would trim it down to 10

Related

Difficulty understanding logic of the implemented algorithm - C#

I am finding it hard to understand the logic of the following algorithm that outputs 8,8. I would appreciate if you could provide some insight.
using System;
namespace Console_Example
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(g(4) + g(5) + "," + g(6));
Console.ReadKey();
}
static int g(int k)
{
if ((k == 1) || (k == 2))
return 1;
else
return g(k - 1) + g(k - 2);
}
}
}
Just a recursive function. You have to follow all steps.
if k = 1:
return 1;
if k = 2:
return 1;
if k = 3:
return (g(2) + g(1)) result is: 1 + 1 = 2
if k = 4:
return (g(3) + g(2)) result is: (1 + 1) + 1 = 3
if k = 5:
return (g(4) + g(3)) result is: 5 + 3 = 5
if k = 6:
return (g(5) + g(4)) result is: 5 + 3 = 8
This looks very similar to the Fibonacci sequence (wikipedia), where each number is the sum of the two previous numbers.
Because it is a recursive function, look at the return g(k - 1) + g(k - 2); where it calls itself, you must follow all the recursion until it ends.
g(1): 1 -- Because the return 1;
g(2): 1 -- Because the return 1;
g(3): g(2) + g(1) = 1 + 1 = 2 -- Here starts the recursion
g(4): g(3) + g(2) = 2 + 1 = 3
g(5): g(4) + g(3) = 3 + 2 = 5
g(6): g(5) + g(4) = 5 + 3 = 8
So g(4) + g(5) +"," +g(6) would be:
3 + 5 , 8 = 8, 8
Pretty much the sum of the F4 F5 resulting in the F6 value.
A better implementation of the Fibonacci sequence would be
static int Fibonacci(int k)
{
if(k < 0)
throw new ArgumentException("Fibonnaci works with numbers >= 0");
if (k == 0 || k == 1)
return k;
else
return Fibonacci(k - 1) + Fibonacci(k - 2);
}

How to dynamically add indexes values of an array in C#?

I have an array where the first two smallest values have to be added, and consequently the result has to be added to next smallest and so on until it reaches the end of the array to give a final total.
However, how can I dynamically modify the method/function so if the values changes and I have 6 vehicles and 6 specs values in the array, the return of the method/function total is not restricted to just 4 indexes.
The array values are unsorted, so in order to add the first smallest, it has to be sorted. Once that's done it adds the values of the new array.
Here's what I've tried:
public static int vehicles = 4;
public static int[] specs = new int[] { 40, 8, 16, 6 };
public static int time(int vehicles, int[] specs)
{
int newValue = 0;
for (int i = 1; i < vehicles; i++)
{
newValue = specs[i];
int j = i;
while (j > 0 && specs[j - 1] > newValue)
{
specs[j] = specs[j - 1];
j--;
}
specs[j] = newValue;
}
// How can I dynamically change this below:
int result1 = specs[0] + specs[1];
int result2 = result1 + specs[2];
int result3 = result2 + specs[3];
int total = result1 + result2 + result3;
return total; // Returns 114
}
Here's the idea of how it works:
4, [40, 8, 16, 6] = 14 --> [40, 14, 16] = 30 --> [40, 30] = 70 ==>> 14 + 30 + 70 = 114
6, [62, 14, 2, 6, 28, 41 ] = 8 --> [62, 14, 8, 28, 41 ] --> 22 [62, 22, 28, 41 ] --> 50
[62, 50, 41 ] --> 91 [62, 91 ] --> 153 ==> 8 + 22 + 50 + 91 + 153 = 324
First off, if you are not restricted to arrays for some weird reason use List<int> and your life will be easier.
List<int> integers = { 14, 6, 12, 8 };
integers.Sort();
integers.Reverse();
while( integers.Count > 1 )
{
int i = integers[integers.Count - 1];
int j = integers[integers.Count - 2];
integers[integers.Count - 2] = i + j;
integers.RemoveAt(integers.Count - 1);
}
var result = integers[0];
P.S.: This can be easily modified to operate on the array version, you can't RemoveAt() from an array but can separately maintain a lastValidIndex.
I would go with the simplest version of a one line solution using LINQ:
Array.Sort(specs);
int total = specs.Select((n, i) => specs.Take(i + 1).Sum()).Sum() - (specs.Length > 1 ? specs[0] : 0);
I would use Linq.
Enumerable.Range(2, specs.Length - 1)
.Select(i => specs
.Take(i)
.Sum())
.Sum();
Explanation:
We take a range starting from 2 ending with specs.Length.
We sum the first i values of specs where i is the current value in the range.
After we have all those sums, we sum them up as well.
To learn more about linq, start here.
This code only works if the values have been sorted already.
If you want to sort the values using linq, you should use this:
IEnumerable<int> sorted = specs.OrderBy(x => x);
Enumerable.Range(2, sorted.Count() - 1)
.Select(i => sorted
.Take(i)
.Sum())
.Sum();
The OrderBy function needs to know how to get the value it should use to compare the array values. Because the array values are the values we want to compare we can just select them using x => x. This lamba takes the value and returns it again.
See comments in code for explanation.
using System;
using System.Linq;
class Program
{
static void Main()
{
//var inputs = new [] { 40, 8, 16, 6 }; // total = 114
var inputs = new[] { 62, 14, 2, 6, 28, 41 }; // total = 324
var total = 0;
var query = inputs.AsEnumerable();
while (query.Count() > 1)
{
// sort the numbers
var sorted = query.OrderBy(x => x).ToList();
// get sum of the first two smallest numbers
var sumTwoSmallest = sorted.Take(2).Sum();
// count total
total += sumTwoSmallest;
// remove the first two smallest numbers
query = sorted.Skip(2);
// add the sum of the two smallest numbers into the numbers
query = query.Append(sumTwoSmallest);
}
Console.WriteLine($"Total = {total}");
Console.WriteLine("Press any key...");
Console.ReadKey(true);
}
}
I benchmark my code and the result was bad when dealing with large dataset. I suspect it was because of the sorting in the loop. The sorting is needed because I need to find the 2 smallest numbers in each iteration. So I think I need a better way to solve this. I use a PriorityQueue (from visualstudiomagazine.com) because the elements are dequeued based on priority, smaller numbers have higher priority in this case.
long total = 0;
while (pq.Count() > 0)
{
// get two smallest numbers when the priority queue is not empty
int sum = (pq.Count() > 0 ? pq.Dequeue() : 0) + (pq.Count() > 0 ? pq.Dequeue() : 0);
total += sum;
// put the sum of two smallest numbers in the priority queue if the queue is not empty
if (pq.Count() > 0) pq.Enqueue(sum);
}
Here's some benchmark results of the new (priority queue) code and the old code in release build. Results are in milliseconds. I didn't test the 1 million data with the old code because it's too slow.
+---------+----------+-------------+
| Data | New | Old |
+---------+----------+-------------+
| 10000 | 3.9158 | 5125.9231 |
| 50000 | 16.8375 | 147219.4267 |
| 1000000 | 406.8693 | |
+---------+----------+-------------+
Full code:
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
class Program
{
static void Main()
{
const string fileName = #"numbers.txt";
using (var writer = new StreamWriter(fileName))
{
var random = new Random();
for (var i = 0; i < 10000; i++)
writer.WriteLine(random.Next(100));
writer.Close();
}
var sw = new Stopwatch();
var pq = new PriorityQueue<int>();
var numbers = File.ReadAllLines(fileName);
foreach (var number in numbers)
pq.Enqueue(Convert.ToInt32(number));
long total = 0;
sw.Start();
while (pq.Count() > 0)
{
// get two smallest numbers when the priority queue is not empty
int sum = (pq.Count() > 0 ? pq.Dequeue() : 0) + (pq.Count() > 0 ? pq.Dequeue() : 0);
total += sum;
// put the sum of two smallest numbers in the priority queue if the queue is not empty
if (pq.Count() > 0) pq.Enqueue(sum);
}
sw.Stop();
Console.WriteLine($"Total = {total}");
Console.WriteLine($"Time = {sw.Elapsed.TotalMilliseconds}");
total = 0;
var query = File.ReadAllLines(fileName).Select(x => Convert.ToInt32(x));
sw.Restart();
while (query.Count() > 0)
{
// sort the numbers
var sorted = query.OrderBy(x => x).ToList();
// get sum of the first two smallest numbers
var sumTwoSmallest = sorted.Take(2).Sum();
// count total
total += sumTwoSmallest;
// remove the first two smallest numbers
query = sorted.Skip(2);
// add the sum of the two smallest numbers into the numbers
if (query.Count() > 0)
query = query.Append(sumTwoSmallest);
}
sw.Stop();
Console.WriteLine($"Total = {total}");
Console.WriteLine($"Time = {sw.Elapsed.TotalMilliseconds}");
Console.WriteLine("Press any key...");
Console.ReadKey(true);
}
}
PriorityQueue code:
using System;
using System.Collections.Generic;
// From http://visualstudiomagazine.com/articles/2012/11/01/priority-queues-with-c.aspx
public class PriorityQueue<T> where T : IComparable<T>
{
private List<T> data;
public PriorityQueue()
{
this.data = new List<T>();
}
public void Enqueue(T item)
{
data.Add(item);
int ci = data.Count - 1; // child index; start at end
while (ci > 0)
{
int pi = (ci - 1) / 2; // parent index
if (data[ci].CompareTo(data[pi]) >= 0)
break; // child item is larger than (or equal) parent so we're done
T tmp = data[ci];
data[ci] = data[pi];
data[pi] = tmp;
ci = pi;
}
}
public T Dequeue()
{
// assumes pq is not empty; up to calling code
int li = data.Count - 1; // last index (before removal)
T frontItem = data[0]; // fetch the front
data[0] = data[li];
data.RemoveAt(li);
--li; // last index (after removal)
int pi = 0; // parent index. start at front of pq
while (true)
{
int ci = pi * 2 + 1; // left child index of parent
if (ci > li)
break; // no children so done
int rc = ci + 1; // right child
if (rc <= li && data[rc].CompareTo(data[ci]) < 0) // if there is a rc (ci + 1), and it is smaller than left child, use the rc instead
ci = rc;
if (data[pi].CompareTo(data[ci]) <= 0)
break; // parent is smaller than (or equal to) smallest child so done
T tmp = data[pi];
data[pi] = data[ci];
data[ci] = tmp; // swap parent and child
pi = ci;
}
return frontItem;
}
public T Peek()
{
T frontItem = data[0];
return frontItem;
}
public int Count()
{
return data.Count;
}
public override string ToString()
{
string s = "";
for (int i = 0; i < data.Count; ++i)
s += data[i].ToString() + " ";
s += "count = " + data.Count;
return s;
}
public bool IsConsistent()
{
// is the heap property true for all data?
if (data.Count == 0)
return true;
int li = data.Count - 1; // last index
for (int pi = 0; pi < data.Count; ++pi)
{ // each parent index
int lci = 2 * pi + 1; // left child index
int rci = 2 * pi + 2; // right child index
if (lci <= li && data[pi].CompareTo(data[lci]) > 0)
return false; // if lc exists and it's greater than parent then bad.
if (rci <= li && data[pi].CompareTo(data[rci]) > 0)
return false; // check the right child too.
}
return true; // passed all checks
}
// IsConsistent
}
// PriorityQueue
Reference:
https://visualstudiomagazine.com/articles/2012/11/01/priority-queues-with-c.aspx
https://en.wikipedia.org/wiki/Priority_queue
You can simply sort it using Array.Sort(), then get the sums in a new array which starts with the smallest value and add each next value to the most recent sum, the total will be the value of the last sum.
public static int time(int vehicles, int[] specs)
{
int i, total;
int[] sums = new int[vehicles];
Array.Sort(spec);
sums[0] = specs[0];
for (i = 1; i < vehicles; i++)
sums[i] = sums[i - 1] + spec[i];
total = sums[spec - 1];
}

What will be the output of this and how?

The output of this code is
0 1 2 3
But I am not getting the factorial part. I mean 1!=1 (i.e. i factorial equals to 1), so it does not satisfy the condition, so type for input 2 and 3, but they get printed as output?
static void Main(string[] args)
{
int i = 0;
int b = 8, a = 32;
for (i = 0; i <= 10; i++)
{
if ((a / b * 2)== 2)
{
Console.WriteLine( i + " ");
continue;
}
else if (i!=4)
Console.Write(i + " ");
else
break;
}
Console.ReadLine();
}
OK, let's see:
int b = 8, a = 32;
...
a / b * 2 == 32 / 8 * 2 == 4 * 2 == 8
That's why if ((a / b * 2) == 2) will never succeed, and so we can drop this if and simplify the loop into
for (i = 0; i <= 10; i++)
if (i != 4) // i != means "i doesn't equal", not "i factorial equals"
Console.Write(i + " "); // print 0, 1, 2, 3
else
break; // break on 4
Here we can clearly see that the routine will be printing out i up to 4 So you have
0 1 2 3
Side note: in order to avoid such errors, format out your code and let the compiler help you:
i!=4 // Bad, it can be read in different ways (not equal or factorial)
i != 4 // i is not equal to 4
i! = 4 // assign 4 to i factorial: compile time error
i! == 4 // i factorial equals to 4: compile time error - C# doesn't have factorials

Get range in multiplication of 5

I have a number. For instance, my number is 19 . Then I want to populate my drop down with range in multiplication of 5. So my dropdownlist will consist of items of:
1-5
6-10
11-15
16-19
I tried modulus and division, however, I can't seems to get the range. Is there a fixed method?
Sample code
List<string> range = new List<string>();
int number = 19;
int numOfOccur = (19/5);
for (int i = 1; i < numOfOccur ; i++)
{
range.Add(i + " - " + (i * 5))
}
Sometime I think that old school code, without fancy linq is a bit more clear
int maximum = 19;
int multiple = 5;
int init = 1;
while (init + multiple <= maximum )
{
string addToDDL = init.ToString() + "-" + (init + multiple - 1).ToString();
Console.WriteLine(addToDDL);
init += multiple;
}
if(init <= maximum)
{
string last = init.ToString() + "-" + maximum.ToString();
Console.WriteLine(last);
}
Linq solution (modern techs allow us to put it consize):
int number = 19;
int div = 5;
List<string> range = Enumerable
.Range(0, number / div + (number % div == 0 ? 0 : 1))
.Select(i => $"{i * div + 1} - {Math.Min((i + 1) * div, number)}")
.ToList();
Test
Console.Write(string.Join(Environment.NewLine, range));
Returns
1 - 5
6 - 10
11 - 15
16 - 19
When using modulo arithmetics, do not forget about remainders: you have an error in int numOfOccur = (19/5); line. It should be
int numOfOccur = 19 / 5 + (19 % 5 == 0 ? 0 : 1);
for the last incomplete 16 - 19 range to be proceeded.
Add this package to your project : https://www.nuget.org/packages/System.Interactive/
Then you can do this:
IEnumerable<IList<int>> buffers2 = Enumerable.Range(1, 19).Buffer(5);
IList<int>[] result2 = buffers2.ToArray();
// { { 1, 2, 3, 4, 5 }, { 6, 7, 8, 9, 10 }, ...
Don't forget to add System.Interactive namespace to your using block.

Pick a varying number of item combinations from a List

Assume I have a list of integers of any length, for an example I have the list of 1,3,5 and 7.
I would like an algorithm to pick a combination of X elements from the list.
For example, X = 1 would return:
1
3
5
7
x = 2 would return:
1 + 1
1 + 3
1 + 5
1 + 7
3 + 3
3 + 5
3 + 7
5 + 5
5 + 7
7 + 7
var listOfInts = new List<int> { 1, 3, 5, 7 };
var combinedInts = new List<int>();
// x = 1 solution
// This is only picking one item from the list.
for (int i = 0; i < listOfInts.Count(); i++)
{
combinedInts.Add(listOfInts[i]);
}
// x = 2 solution
// This is how to pick two. I wrap it around another for loop.
for (int i = 0; i < listOfInts.Count(); i++)
{
for (int j = i; j < listOfInts.Count(); j++)
{
combinedInts.Add(listOfInts[i] + listOfInts[j]);
}
}
// x = 3 solution
// If I go up another level I have to wrap it around another for loop. This solution won't scale.
for (int i = 0; i < listOfInts.Count(); i++)
{
for (int j = i; j < listOfInts.Count(); j++)
{
for (int k = j; k < listOfInts.Count(); k++)
{
combinedInts.Add(listOfInts[i] + listOfInts[j] + listOfInts[k]);
}
}
}
This solution doesn't scale as I have to continually wrap around another for loop for each number of element I'm picking. For example X = 7 would need 7-nested for loops. Is there a better way to write this method that doesn't involve nesting for loops?
You can use the following to get combinations of the sequences:
public static class LinqHelper
{
public static IEnumerable<IEnumerable<T>> Combinations<T>(this IEnumerable<T> elements, int? k = null)
{
if (!k.HasValue)
k = elements.Count();
return k == 0 ? new[] { new T[0] } :
elements.SelectMany((e, i) => elements.Skip(i).Combinations(k - 1).Select(c => (new[] { e }).Concat(c)));
}
}
var list = new List<int> { 1, 3, 5, 7 };
int x = 2; //Change to 3, 4, 5, etc
var result = list.Combinations(x);
Yields:
1 1
1 3
1 5
1 7
3 3
3 5
3 7
5 7
7 7
To get the sum of each one, you'd aggregate the result:
var result = list.Combinations(x).Select(g => g.Aggregate((left, right) => left + right));
Which produces:
2
4
6
8
6
8
10
10
12
14
There is also a purely iterative way to do this. It requires a great deal more thought and complexity, but can be made very efficient. The basic idea is to simulate the same nested loops, but track the iterations of each nested loop as an array of loop counters, which are iterated forward in the same manner as the original nested loop code. Here is a fully working example:
var listOfInts = new List<int> { 1, 3, 5, 7 };
var combinedInts = new List<int>();
var numInts = listOfInts.Count;
var numElements = 5; // number of "nested loops", or ints selected in each combination
var loopCounters = new int[numElements]; // make one loop counter for each "nested loop"
var lastCounter = numElements - 1; // iterate the right-most counter by default
// maintain current sum in a variable for efficiency, since most of the time
// it is changing only by the value of one loop counter change.
var tempSum = listOfInts[0] * numElements;
// we are finished when the left/outer-most counter has looped past number of ints
while (loopCounters[0] < numInts) {
// you can use this to verify the output is iterating correctly:
// Console.WriteLine(string.Join(",", loopCounters.Select(x => listOfInts[x])) + ": " + loopCounters.Select(x => listOfInts[x]).Sum() + "; " + tempSum);
combinedInts.Add(tempSum);
tempSum -= listOfInts[loopCounters[lastCounter]];
loopCounters[lastCounter]++;
if (loopCounters[lastCounter] < numInts) tempSum += listOfInts[loopCounters[lastCounter]];
// if last element reached in inner-most counter, increment previous counter(s).
while (lastCounter > 0 && loopCounters[lastCounter] == numInts) {
lastCounter--;
tempSum -= listOfInts[loopCounters[lastCounter]];
loopCounters[lastCounter]++;
if (loopCounters[lastCounter] < numInts) tempSum += listOfInts[loopCounters[lastCounter]];
}
// if a previous counter was advanced, reset all future counters to same
// starting number to start iteration forward again.
while (lastCounter < numElements - 1) {
lastCounter++;
if (loopCounters[lastCounter] < numInts) tempSum -= listOfInts[loopCounters[lastCounter]];
loopCounters[lastCounter] = loopCounters[lastCounter - 1];
if (loopCounters[lastCounter] < numInts) tempSum += listOfInts[loopCounters[lastCounter]];
}
}
At the end of the iteration, combinedInts should contains a list of all sum combinations, similar to the original code or the other recursive solutions. If you are working with small sets, and small combinations, then this level of efficiency is unnecessary and you should prefer a recursive solution which is easier to reason about correctness. I present this as an alternative way to think about the problem. Cheers!
This works for me:
Func<IEnumerable<int>, int, IEnumerable<IEnumerable<int>>> generate = null;
generate = (xs, n) =>
(xs == null || !xs.Any())
? Enumerable.Empty<IEnumerable<int>>()
: n == 1
? xs.Select(x => new [] { x })
: xs.SelectMany(x => generate(xs, n - 1).Select(ys => ys.Concat(new [] { x })));
int[] array = { 1, 3, 5, 7, };
var results =
generate(array, 3)
.Select(xs => String.Join("+", xs));
With this call I get:
1+1+1, 3+1+1, 5+1+1, 7+1+1, 1+3+1, 3+3+1, 5+3+1, 7+3+1, 1+5+1, 3+5+1, 5+5+1, 7+5+1, 1+7+1, 3+7+1, 5+7+1, 7+7+1, 1+1+3, 3+1+3, 5+1+3, 7+1+3, 1+3+3, 3+3+3, 5+3+3, 7+3+3, 1+5+3, 3+5+3, 5+5+3, 7+5+3, 1+7+3, 3+7+3, 5+7+3, 7+7+3, 1+1+5, 3+1+5, 5+1+5, 7+1+5, 1+3+5, 3+3+5, 5+3+5, 7+3+5, 1+5+5, 3+5+5, 5+5+5, 7+5+5, 1+7+5, 3+7+5, 5+7+5, 7+7+5, 1+1+7, 3+1+7, 5+1+7, 7+1+7, 1+3+7, 3+3+7, 5+3+7, 7+3+7, 1+5+7, 3+5+7, 5+5+7, 7+5+7, 1+7+7, 3+7+7, 5+7+7,7+7+7

Categories