2

I have multiple sets of arrays that contain additional arrays that have values attached that I use for figuring out math. In order to find the best combination of these things, I need to mix and match from these arrays. I've seen "solutions" similar to this around, but they're usually 1 array deep with no real combinations/possibilities. So to give an example.

I have sets A, B, and C. Set A contains Aa, Ab, Ac, and Ad. Aa contains a set of values. Extrapolate that out for the others. Aa can only be compared with Ba and Ca. How do I go about writing a program to find all combinations(i.e. Aa, Ab, Cc, Bd compared with Ba, Cb, Ac, Bd and etc) so I can compare the math on each combination to find the best one? Note: this is just an example, I don't need it for specifically 3 sets of 4 sets of 4, it needs to be able to expand.

Now I know I didn't use very meaningful names for my variables, but I would appreciate if any code given does have meaningful names in it(I'd really rather not follow around variables of x and c around in code).

Austin Salonen
  • 49,173
  • 15
  • 109
  • 139
Wildcolt
  • 23
  • 3
  • possible duplicate of [Generating all Possible Combinations](http://stackoverflow.com/questions/3093622/generating-all-possible-combinations) – Eric Lippert Oct 09 '13 at 15:16

2 Answers2

5

The accepted answer appears to be correct but is a very strange way to do a Cartesian product in C#. If you have a given number of sequences you can take their Cartesian product idiomatically like this:

    var aList = new[] { "a1", "a2", "a3" };
    var bList = new[] { "b1", "b2", "b3" };
    var cList = new[] { "c1", "c2", "c3" };
    var product = from a in aList
                  from b in bList
                  from c in cList
                  select new[] { a, b, c };

    foreach (var p in product)
        Console.WriteLine(string.Join(",", p));

If you have arbitrarily many sequences that you need to take their Cartesian product then you can do it like this:

static class Extensions
{
  public static IEnumerable<IEnumerable<T>> CartesianProduct<T>(
    this IEnumerable<IEnumerable<T>> sequences) 
  { 
    IEnumerable<IEnumerable<T>> emptyProduct = new[] { Enumerable.Empty<T>() }; 
    return sequences.Aggregate( 
      emptyProduct, 
      (accumulator, sequence) => 
        from accseq in accumulator 
        from item in sequence 
        select accseq.Concat(new[] {item})); 
  }
}

And then:

    var aList = new[] { "a1", "a2", "a3" };
    var bList = new[] { "b1", "b2", "b3" };
    var cList = new[] { "c1", "c2", "c3" };
    var lists = new[] { aList, bList, cList };
    var product = lists.CartesianProduct();
    foreach (var p in product)
        Console.WriteLine(string.Join(",", p));

See

http://ericlippert.com/2010/06/28/computing-a-cartesian-product-with-linq/

and my answer to

Generating all Possible Combinations

for more discussion of this problem.

Community
  • 1
  • 1
Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • Thanks for the alternate answer. Is there any real problem with using the other listed answer though? Other than it doesn't work with a random amount of input? – Wildcolt Oct 10 '13 at 00:50
  • Oh, wow, I had no idea you could do joins that way. Coming from a SQL background it almost seems "wrong" to do it without a join operator, but if it works... – ekolis Oct 10 '13 at 01:32
-1

Assuming you are using a version of C# which supports LINQ:

static void Main(string[] args)
    {
        // declare some lists
        var aList = new string[] { "a1", "a2", "a3" };
        var bList = new string[] { "b1", "b2", "b3" };
        var cList = new string[] { "c1", "c2", "c3" };

        // do the equivalent of a SQL CROSS JOIN
        var permutations = aList
            .Join(bList, a => "", b => "", (a, b) => new string[] { a, b })
            .Join(cList, ab => "", c => "", (ab, c) => new string[] { ab[0], ab[1], c });

        // print the results
        Console.WriteLine("Permutations:");
        foreach (var p in permutations)
            Console.WriteLine(string.Join(", ", p));
    }

The Join calls with the lambda expressions pointing the strings to empty strings causes the Join function to treat the strings as equal, emulating a SQL CROSS JOIN.

ekolis
  • 6,270
  • 12
  • 50
  • 101
  • Oh, sorry, I didn't notice you wanted multiple arrays deep. How exactly would that work? You said each subarray should only generate permutations with its "siblings"? How would these permutations be then permuted with each other? – ekolis Oct 09 '13 at 01:35
  • Well to give a very specific example of the list(I'll still keep it simple)...Set A contains Aa, Ab and Set B contains Ba, Bb. Aa contains the numbers 4,0,1,1. Ab: 10,1,0,1. Ba: 15,0,0,0. Bb: 20,0,0,8. The best set we'll say for this is going to be only involving the first and last digits, namely a multiplier based on another set of numbers I have. For one specific set of numbers, this comes out to the best being Ba and Bb, while for another set would be Aa and Bb. So I need a selection of suba and subb to be picked for each combination. Only one suba, one subb, etc. Is that clear? – Wildcolt Oct 09 '13 at 01:47
  • Just ran your code. Very nice. Thank you. I can adapt this to what I need. – Wildcolt Oct 09 '13 at 02:13
  • 1
    This appears to be correct but this is a *bizarre* way to do a Cartesian product in LINQ. See my answer for how to do so idiomatically. – Eric Lippert Oct 09 '13 at 15:15
  • If you hate query expressions, I would prefer `SelectMany` over `Join`, since there is no key to join on. E.g., `var permutations = aList.SelectMany(b=>bList,(a,b)=>new{a,b}).SelectMany(c=>cList,(ab,c)=>new{ab.a,ab.b,c});`. However, I think Eric's answer is easier to read. – Brian Oct 09 '13 at 17:17
  • @Brian: Right, this answer attempts to build SelectMany out of Join by matching on an empty key! Like I said, this is very strange; *logically* one builds Join out of a combination of SelectMany and Where, though of course that is inefficient in practice. The idea of building SelectMany out of Join is a novel one to me. – Eric Lippert Oct 09 '13 at 18:38
  • 1
    I note also that this answer does not address the principal difficulty of the original question: the number of arrays is not fixed. Neither SelectMany nor Join addresses that; as I note in my answer, you've got to write a general-purpose operator that can take arbitrarily many sequences. – Eric Lippert Oct 09 '13 at 18:40