4

Im having some issues with trying to update a nested for loop to use recursion instead. Is it possible to access the a,b and c variables from the earlier for loops when using recursion? Below is a simple example of what im trying to convert into a recursive call.

for(int a= 0; a < 10; a++)
{
    for(int b = 0; b < 20; b++)
    {
        for(int c = 0; c < 10; c++)
        {
            int[] indexes = new int[3]{a,b,c}
            collection.add(indexes);
        }
    }
}

EDIT: The solution needs to be able to be adjusted at runtime, such that a user can select how many levels are required.

Hans Rudel
  • 3,433
  • 5
  • 39
  • 62

6 Answers6

9

Here's a recursive solution (using a functional programming style):

public static IEnumerable<IEnumerable<int>> GetCombinations(IEnumerable<int> limits)
{
    if (limits.Any() == false)
    {
        // Base case.
        yield return Enumerable.Empty<int>();
    }
    else
    {
        int first = limits.First();
        IEnumerable<int> remaining = limits.Skip(1);
        IEnumerable<IEnumerable<int>> tails = GetCombinations(remaining);

        for (int i = 0; i < first; ++i)
            foreach (IEnumerable<int> tail in tails)
                yield return Yield(i).Concat(tail);
    }
}

// Per http://stackoverflow.com/q/1577822
public static IEnumerable<T> Yield<T>(T item)
{
    yield return item;
}

Sample use:

var sequences = GetCombinations(new [] { 5, 3, 2, 4 /* ... */ });
foreach (var sequence in sequences)
    Console.WriteLine(string.Join(", ", sequence));

/* Output:
0, 0, 0, 0
0, 0, 0, 1
0, 0, 0, 2
0, 0, 0, 3
0, 0, 1, 0
0, 0, 1, 1
0, 0, 1, 2
0, 0, 1, 3
0, 1, 0, 0
0, 1, 0, 1
0, 1, 0, 2
... */

For OP's specific scenario (adding arrays to collection):

var sequences = GetCombinations(new [] { 10, 20, 10 });
collection.AddRange(sequences.Select(s => s.ToArray()));
Douglas
  • 53,759
  • 13
  • 140
  • 188
4

Ok, try with this

static void AddToCollectionRecursive(
    List<int[]> collection,
    params int[] counts)
{
    AddTo(collection, new List<int>(), counts, counts.Length - 1);
}

static void AddTo(
    List<int[]> collection,
    IEnumerable<int> value,
    IEnumerable<int> counts,
    int left)
{
    for (var i = 0; i < counts.First(); i++)
    {
        var list = value.ToList();

        list.Add(i);

        if (left == 0)
        {
            collection.Add(list.ToArray());
        }
        else
        {
            AddTo(collection, list, counts.Skip(1), left - 1);
        }
    }
}

Usage is like this AddToCollectionRecursive(collection, 10, 20, 10);.

Alessandro D'Andria
  • 8,663
  • 2
  • 36
  • 32
  • I believe this is what i was looking for. Slightly different, dare i say it, better than what i came up with. Thanks. – Hans Rudel Sep 20 '13 at 11:29
2

something like this will work:

public void CreateIndexes(int a, int b, int c, Collection collection)
{
    if(c == 10) {b++; c = 0;}
    if(b == 20) {a++; b = 0;}
    if(a == 10) return;

    int[] indexes = new int[3]{a,b,c}
    collection.add(indexes);
    c++;

    CreateIndexes(a, b, c, collection);
}
gesus
  • 471
  • 1
  • 10
  • 24
1

Off the top of my head, i.e. not tested, something like this might work:

    List<int[]> collection = new List<int[]>();
    private void AddValues(int a, int b, int c)
    {

        collection.Add(new[] { a, b, c });

        if (c < 10)
        {
            c++;
            AddValues(a, b, c);
        }

        if (b < 20)
        {
            b++;
            c = 0;
            AddValues(a, b, c);   
        }

        if (a < 10)
        {
            a++;
            b = 0;
            c = 0;
            AddValues(a, b, c);
        }
    }

Start it by calling:

AddValues(0, 0, 0);
DaveDev
  • 41,155
  • 72
  • 223
  • 385
  • Need to reset `b` and `c` in `a` branch and `c` in `b` branch. – Dialecticus Sep 20 '13 at 10:32
  • Also, this code is not exactly good to the stack. In our case recursion should be ideally there levels deep, but with this code it will be 9+19+9=37 levels deep. – Dialecticus Sep 20 '13 at 10:35
  • This requires that i know there are 3 inputs though. If the number of inputs were changed to 4 at runtime, then it wouldnt produce the correct results. – Hans Rudel Sep 20 '13 at 10:37
  • 1
    That wasn't in your original spec. You sound like the sales guys I work with. Instead of taking 3 parameters, take a list, then foreach item in that list increment the current, reset the previous values, do some magic, call the method again with the current values and then hope for best. – DaveDev Sep 20 '13 at 10:42
  • DaveDev, sorry to inform you but just ran a quick test and your code produces doubles. Made the test with a, b and c < 1 which gives 41 items in the collection. Leaving the resets out of the respective branch still produces 8 items instead of 4. Don't have a solution for it at the moment. – Koen Sep 20 '13 at 10:48
  • @DaveDev Sorry for sounding like ur sales college :(. As for ur advice, yeah i think i have managed to get it sorted based on ur advice. Thanks for ur help. – Hans Rudel Sep 20 '13 at 11:26
1

Well, i think that if u resolve this problem using recursion, it will consume more memory and other resources!

But there is my suggestion:

private void FunctionName(int a, int b, int c, List<int[]> list)
{
    if (a<10)
    { 
       if (b<20)
       {
           if (c<10)
           {
               list.Add(new[] { a, b, c });
               c++;
               FunctionName(a,b,c,list);
            }
            else
            {
                 c=0;
                 b++;
                 FunctionName(a,b,c,list);
            }
       }
       else
       {
          b=0;
          a++;
          FunctionName(a,b,c,list);
       }
    }
 }

You call like this : FunctionName(0,0,0,list).

Hope it works! ^^

Cold
  • 787
  • 8
  • 23
0

This solution takes an Action for the work to be done at the leafs:

void ForEachCombo(int from, int to, int nrLevels, Action<int[]> action)
{
    int[] d = new int[nrLevels];
    InnerFor(from, to, 0);
    void InnerFor(int from, int to, int level)
    {
        if (level == nrLevels)
            action(d);
        else
            for (d[level] = from; d[level] <= to - nrLevels + level + 1; d[level]++)
                InnerFor(d[level] + 1, to, level + 1);
    }
}

Use like this:

ForEachCombo(0, 9, 3, (d) =>
{
    Console.WriteLine(string.Join(", ", d));
});

// Output
0, 1, 2
0, 1, 3
0, 1, 4
0, 1, 5
...
6, 7, 9
6, 8, 9
7, 8, 9
//

If you want to you can save a level of recursion by writing like this:

void ForEachCombo(int from, int to, int nrLevels, Action<int[]> action)
{
    int[] d = new int[nrLevels];
    InnerFor(from, to, 0);
    void InnerFor(int from, int to, int level)
    {
        if (level == nrLevels - 1)
            for (d[level] = from; d[level] <= to - nrLevels + level + 1; d[level]++)
                action(d);
        else
            for (d[level] = from; d[level] <= to - nrLevels + level + 1; d[level]++)
                InnerFor(d[level] + 1, to, level + 1);
    }
}
FreddyFlares
  • 473
  • 6
  • 17