Cartesian Product + N x M Dynamic Array

前端 未结 3 1857
心在旅途
心在旅途 2020-12-16 07:29

I have looked hours for a solution without any success. Hopefully someone can help me out.

I have a dynamic array of N items on M origin zip codes.

For insta

相关标签:
3条回答
  • 2020-12-16 07:39

    What you're looking to do is generate combinations of each item in the array.

    Here's an example hard-coded for N == 3:

            var array1 = new[] { 1101, 5410, 60621 };
            var array2 = new[] { 1101, 60621 };
            var array3 = new[] { 60621 };
    
            foreach (var a in array1)
            {
                foreach (var b in array2)
                {
                    foreach (var c in array3)
                    {
                        Console.WriteLine("{0},{1},{2}", a, b, c);
                    }
                }
            }
    

    See if you can adapt that for N cases.

    0 讨论(0)
  • 2020-12-16 07:40

    You can use linq for that:

    var item1 = new[] { 11001, 54010, 60621 };
    var item2 = new[] {  11001, 60621 };
    var item3 = new [] { 60621 };
    IEnumerable<int[]> cartesian = 
        from i1 in item1
        from i2 in item2
        from i3 in item3
        select new[] { i1, i2, i3 };
    
    0 讨论(0)
  • 2020-12-16 07:44

    It sounds like you want this function from Eric Lippert's blog post written in response to Generating all Possible Combinations:

    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}));                
    }
    

    That would let you write code like this:

    int[][] items = { 
                        new[] { 11001, 54010, 60621 },
                        new[] { 11001, 60621 },
                        new[] { 60621 }
                    };
    var routes = CartesianProduct(items);
    foreach (var route in routes)
        Console.WriteLine(string.Join(", ", route));
    

    And get output like this:

    11001, 11001, 60621
    11001, 60621, 60621
    54010, 11001, 60621
    54010, 60621, 60621
    60621, 11001, 60621
    60621, 60621, 60621
    

    EDIT: Here's the VB.NET version (in VS2010)

    Imports System.Runtime.CompilerServices
    
    Module Module1
        <Extension()>
        Private Function CartesianProduct(Of T)(
                ByVal sequences As IEnumerable(Of IEnumerable(Of T))) _
                As IEnumerable(Of IEnumerable(Of T))
            Dim emptyProduct As IEnumerable(Of IEnumerable(Of T)) =
                New IEnumerable(Of T)() {Enumerable.Empty(Of T)()}
            Return sequences.Aggregate(
                emptyProduct,
                Function(accumulator, sequence)
                    Return (From accseq In accumulator
                            From item In sequence
                            Select accseq.Concat(New T() {item}))
                End Function)
        End Function
    
        Sub Main(ByVal args As String())
            Dim items = New Integer()() {New Integer() {11001, 54010, 60621},
                                         New Integer() {11001, 60621},
                                         New Integer() {60621}}
            Dim routes = items.CartesianProduct()
            Dim route As IEnumerable(Of Integer)
            For Each route In routes
                Console.WriteLine(String.Join(", ", route))
            Next
        End Sub
    End Module
    

    Of course, if you don't want any LINQ whatsoever, here's a completely LINQ-free recursive implementation:

    public static IEnumerable<IEnumerable<T>> CartesianProduct<T>(
        this IEnumerable<IEnumerable<T>> sequences)
    {
        var accum = new List<T[]>();
        var list = sequences.ToList();
        if (list.Count > 0)
            CartesianRecurse(accum, new Stack<T>(), list, list.Count - 1);
        return accum;
    }
    
    static void CartesianRecurse<T>(List<T[]> accum, Stack<T> stack,
                                    List<IEnumerable<T>> list, int index)
    {
        foreach (T item in list[index])
        {
            stack.Push(item);
            if (index == 0)
                accum.Add(stack.ToArray());
            else
                CartesianRecurse(accum, stack, list, index - 1);
            stack.Pop();
        }
    }
    

    It doesn't return the items in the same order as the first function, but is otherwise functionally identical. If you don't like LINQ or recursion, here's a single LINQ-less method that does the same thing as the recursive version:

    public static IEnumerable<IEnumerable<T>> CartesianProduct<T>(
        this IEnumerable<IEnumerable<T>> sequences)
    {
        var accum = new List<T[]>();
        var list = sequences.ToList();
        if (list.Count > 0)
        {
            var enumStack = new Stack<IEnumerator<T>>();
            var itemStack = new Stack<T>();
            int index = list.Count - 1;
            var enumerator = list[index].GetEnumerator();
            while (true)
                if (enumerator.MoveNext())
                {
                    itemStack.Push(enumerator.Current);
                    if (index == 0)
                    {
                        accum.Add(itemStack.ToArray());
                        itemStack.Pop();
                    }
                    else
                    {
                        enumStack.Push(enumerator);
                        enumerator = list[--index].GetEnumerator();
                    }
                }
                else
                {
                    if (++index == list.Count)
                        break;
                    itemStack.Pop();
                    enumerator = enumStack.Pop();
                }
        }
        return accum;
    }
    
    0 讨论(0)
提交回复
热议问题