0

Is there any way to convert the following code snippet to use for loops instead of the heavy LINQ usage?

public static IEnumerable<IEnumerable<T>> Combinations<T>(this IEnumerable<T> elements, int k)
{
    return k == 0
       ? new[] {new T[0]}
       : elements.SelectMany((e, i) =>
             elements.Skip(i + 1).Combinations(k - 1).Select(c => (new[] {e}).Concat(c)));
}

The goal is to come up with all possible combinations of size k of a given list of elements.

Greg
  • 1
  • 1
  • 3
    Yes, there is a way. After all, the LINQ stuff resolves to loops. Are you asking "What is the way?" Have you given any thought to breaking down each of those LINQ method calls? – Jim Mischel May 07 '15 at 18:12
  • Thanks for the answer! Yes, if there is a way, I'd like know how, since I'm not very familiar with LINQ. I tried a couple of things, but was unable to get the same result... – Greg May 07 '15 at 18:23
  • 1
    This is question has been solved from an algorithmic standpoint [here](http://stackoverflow.com/questions/127704/algorithm-to-return-all-combinations-of-k-elements-from-n). Also note that removing the linq will not improve the run time of the combination algorithms, as they all tend to be VERY slow. – James C. Taylor IV May 07 '15 at 18:25
  • 1
    A quick Google search reveals http://dotnetgeek.tumblr.com/post/5205119053/c-combination-subset-extension-method, which looks like it might do what you want. – Jim Mischel May 07 '15 at 18:28
  • @JamesC.TaylorIV Well, the solution provided is extremely inefficient, as it does a ton of re-computation of the same values again and again. Re-writing it leaves quite a lot of room for improvement. Now, even a great implementation will scale poorly, but it could rather easily be a lot more effective than it currently is. – Servy May 07 '15 at 18:41
  • @Servy I agree with you, however the run time of the algorithm would be effectively the same. You can cut out some of the excess computational work by employing Chase's algorithm with using a dynamic approach to storing and looking up the previous combinations at the current level. This was suggested in the comments of the link I provided. – James C. Taylor IV May 07 '15 at 18:50
  • @JamesC.TaylorIV No, the asymptotic complexity wouldn't be staying the same. It'd be going from !(n^2) to !n. Both are terrible, but the latter is a *lot* better than the former. The `Skip` that he's doing involves taking O(n) time to find the start of the sequence, when it could easily be done in O(1) time. Given that that operation is done on the order of n! times, that makes quite a big difference. – Servy May 07 '15 at 19:03
  • I am not sure if it is straightforward, but I was looking for a direct replication of the above code without linq. Thank you @James C. Taylor IV ! – Greg May 07 '15 at 19:05

1 Answers1

0

Here is my first take on this

    public static IEnumerable<IEnumerable<int>> Combs(List<int> e, int size)
    {
            for (int i=0; i< e.Count; i ++)
            {                
                if (size  == 1)                    
                    yield return new[] {e[i]};

                foreach ( var next in Combs(e.GetRange(i + 1, e.Count - (i + 1)), size  - 1) )
                    yield return new[] { e[i] }.Concat(next);
            }      
    }

However, I'd like to simplify the foreach statement, and possibly the concat argument, if possible...

Greg
  • 1
  • 1