如何生成多个数组的所有排列/组合?

时间:2014-02-17 22:19:50

标签: arrays vb.net algorithm linq permutation

我的目标很简单,我正在尝试为数据库中的产品生成所有可能组合的列表。

所以例如;产品选项如下

  • 产品选项:颜色/值:红色,绿色,蓝色
  • 产品选项:尺寸/值:小,中,大,XL
  • 产品选择:风格/价值观:男士,女士

我希望能够自动生成所有3个组合的每个组合:

Small, Red, Mens
Small, Green, Mens
Small, Blue, Mens
etc

无论我是否将2,3,4或5个数组传入其中,我都需要该函数。

我做了很多研究并发现了以下文章但却未能实现我的目标。

我发现的文章如下:

5 个答案:

答案 0 :(得分:4)

在笛卡尔产品上调整Eric Lippert's blog的代码:

Private Function CartesianProduct(Of T)(ParamArray sequences As T()()) As T()()

    ' base case: 
    Dim result As IEnumerable(Of T()) = {New T() {}}
    For Each sequence As var In sequences
        Dim s = sequence
        ' don't close over the loop variable 
        ' recursive case: use SelectMany to build the new product out of the old one 
        result = From seq In result
                 From item In s
                 Select seq.Concat({item}).ToArray()
    Next
    Return result.ToArray()
End Function

用法:

Dim s1 As String() = New String() {"small", "med", "large", "XL"}
Dim s2 As String() = New String() {"red", "green", "blue"}
Dim s3 As String() = New String() {"Men", "Women"}

Dim ss As String()() = CartesianProduct(s1, s2, s3)

答案 1 :(得分:0)

一个在另一个内部的三个循环应该可以解决问题。

所以伪代码......

For each value in sex array
    For each value in size array
        For each value in colour array
          Output sex, size, colour values
        Next colour
    Next size
Next sex

更新了Pseudo

Sub ouputOptions(array1, array2, array3, array4, array5)
    For each value in array1
        For each value in array2
            If array3 Not Nothing Then
                For each value in array3
                    If array4 Not Nothing Then
                        For each value in array4
                            If array5 Not Nothing Then
                                For each value in array5
                                    output array1, array2, array3, array4, array5 values
                                next array5
                            Else
                                Output array1, array2, array3, array4 values
                            End if
                        Next array4
                    Else
                        Output array1, array2, array3 values
                    End if
                next array3
            Else
                Output array1, array2 values
            End if
        Next array2
    Next array1
End Sub

您需要将数组3到5指定为Optional

答案 2 :(得分:0)

你可以通过一点递归来实现这一点。

以下结果返回一个字符串数组数组。

Public class Permuter

    Public Function Permute(ParamArray toPermute As String()()) As String()()

        Return DoPermute(Nothing, toPermute)

    End Function

    ''' <summary>
    ''' Permute the first two arrays,then pass that, and the remainder recursively
    ''' </summary>
    Private Function DoPermute(working As String()(), toPermute As String()()) As String()()

        Dim nextWorking As String()()

        If working Is Nothing Then

            'Make a new working list
            nextWorking = (From a In toPermute(0)
                       Select {a}).ToArray

        Else

            'Combine from the next working list
            nextWorking = (From a In working, b In toPermute(0)
                          Select a.Concat({b}).ToArray).ToArray

        End If

        If toPermute.Length > 1 Then

            'Go Around again

            Dim nextPermute = toPermute.Skip(1).ToArray

            Return DoPermute(nextWorking, nextPermute)

        Else

            'We're done
            Return nextWorking

        End If

    End Function

End Class

将公共方法称为:

Dim permuter = New Permuter
Dim permutations = permuter.Permute({"a", "b", "c"}, {"1", "2", "3"}, {"x", "y", "z"})

更新:接受@DStanley的Eric Lippert博客参考,以下是that post上提到的累加器方法的转换:

Public Function CartesianProduct(Of T)(ParamArray sequences As T()()) As IEnumerable(Of IEnumerable(Of T))

    Dim emptyProduct As IEnumerable(Of IEnumerable(Of T)) = {Enumerable.Empty(Of T)()}

    Return sequences.Aggregate(
            emptyProduct,
            Function(accumulator, sequence) _
                From accseq In accumulator, item In sequence
                Select accseq.Concat({item})
        )

End Function

请注意,这会返回延迟查询,而不是扩展的数组集。

答案 3 :(得分:0)

递归有时是错误的方法。

如果你不想使用递归(害怕StackOverflow异常?),你可以这样做:

List<List<string>> Combine(List<List<string>> lists)
{
    List<List<string>> result = new List<List<string>>();
    var arrayIndexes = new int[lists.Count]; 
    result.Add(GetCurrentItem(lists, arrayIndexes));
    while (!AllIndexesAreLast(lists, arrayIndexes))
    {
        for (int i = arrayIndexes.Length - 1; i >= 0; i--)
        {
            arrayIndexes[i] = (arrayIndexes[i] + 1) % lists[i].Count;
            if (arrayIndexes[i] != 0)
            {
                break;
            }
        }
        result.Add(GetCurrentItem(lists, arrayIndexes));
    } 

    return result;
}

List<string> GetCurrentItem(List<List<string>> lists, int[] arrayIndexes)
{
    var item = new List<string>();
    for (int i = 0; i < lists.Count; i++)
    {
        item.Add(lists[i][arrayIndexes[i]]);
    }
    return item;
}

bool AllIndexesAreLast(List<List<string>> lists, int[] arrayIndexes)
{
    for (int i = 0; i < arrayIndexes.Length; i++)
    {
        if (lists[i].Count - 1 != arrayIndexes[i])
        {
            return false;
        }
    }
    return true;
}

你可以像这样使用它:

var shirts = new List<List<string>>()
{
    new List<string>() {"colour", "red", "blue", "green", "yellow"},
    new List<string>() {"cloth", "cotton", "poly", "silk"},
    new List<string>() {"type", "full", "half"}
};
var result = Combine(shirts);

答案 4 :(得分:0)

(我认为)我需要完全相同的东西,但我无法在答案中找到我需要的东西(主要是因为它们是我不知道的语言,我猜)。 / p>

我带来了这个(功能本身):

Public Function nChooseK(Of T)(ByVal Values As List(Of T), ByVal k As Integer, Optional ByRef Result As List(Of List(Of T)) = Nothing, Optional ByRef CurCombination As List(Of T) = Nothing, Optional ByVal Offset As Integer = 0) As List(Of List(Of T))
    Dim n = Values.Count
    If CurCombination Is Nothing Then CurCombination = New List(Of T)
    If Result Is Nothing Then Result = New List(Of List(Of T))

    If k <= 0 Then
        Result.Add(CurCombination.ToArray.ToList)
        Return Result
    Else
        For i = Offset To n - k
            CurCombination.Add(Values(i))
            nChooseK(Values, k - 1, Result, CurCombination, i + 1)
            CurCombination.RemoveAt(CurCombination.Count - 1)
        Next

        Return Result
    End If

End Function

所有人需要做的就是将它放在一个模块中(或者在我猜的调用它的子/函数的上方/下方)并用任何变量和数字调用它

如何称呼它:

nChooseK(List, kInteger)

小例子:

Dim NumbersCombinations As List(Of List(Of Integer)) = nChooseK(lstNumbers, k)

与整数和字符串一起使用以及将结果打印到屏幕的完整示例:

        Dim Numbers() As Integer = {1, 2, 3, 4, 5}
    Dim lstNumbers = New List(Of Integer)
    Dim k = 3
    lstNumbers.AddRange(Numbers)

    Dim NumbersCombinations As List(Of List(Of Integer)) = nChooseK(lstNumbers, k)

    Dim sbCombinations1 As New StringBuilder
    For i = 0 To NumbersCombinations.Count - 1
        sbCombinations1.AppendLine()
        For j = 0 To NumbersCombinations(i).Count - 1
            sbCombinations1.Append(NumbersCombinations(i)(j) & " ")
        Next
        sbCombinations1.Length = sbCombinations1.Length - 1
    Next
    MsgBox(sbCombinations1.ToString)



    Dim lstNoumera = New List(Of String)
    lstNoumera.AddRange({"ena", "dio", "tria", "tessera", "pente"})

    Dim Combinations As List(Of List(Of String)) = nChooseK(lstNoumera, k)

    Dim sbCombinations2 As New StringBuilder
    For i = 0 To Combinations.Count - 1
        sbCombinations2.AppendLine()
        For j = 0 To Combinations(i).Count - 1
            sbCombinations2.Append(Combinations(i)(j) & " ")
        Next
        sbCombinations2.Length = sbCombinations2.Length - 1
    Next
    MsgBox(sbCombinations2.ToString)