Cartesian Product + N x M Dynamic Array

前端 未结 3 1864
心在旅途
心在旅途 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: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> CartesianProduct(
        this IEnumerable> sequences) 
    { 
        IEnumerable> emptyProduct = new[] { Enumerable.Empty() }; 
        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
        
        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> CartesianProduct(
        this IEnumerable> sequences)
    {
        var accum = new List();
        var list = sequences.ToList();
        if (list.Count > 0)
            CartesianRecurse(accum, new Stack(), list, list.Count - 1);
        return accum;
    }
    
    static void CartesianRecurse(List accum, Stack stack,
                                    List> 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> CartesianProduct(
        this IEnumerable> sequences)
    {
        var accum = new List();
        var list = sequences.ToList();
        if (list.Count > 0)
        {
            var enumStack = new Stack>();
            var itemStack = new Stack();
            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;
    }
    

提交回复
热议问题