【问题标题】:Fix this permutation generator function that generates empty strings修复这个生成空字符串的排列生成器函数
【发布时间】:2015-03-23 22:11:50
【问题描述】:

我在@Artur Udod 上采用了置换生成器解决方案回答这个问题:

Print all the possible combinations of "X" amount of characters with "X" string length (Brute Force)

我已经修改了代码以返回字符串而不是 Char 的集合,并且还能够指定是否允许在生成的排列上重复字符:

Public Shared Function PermuteCharacters(ByVal charSet As IEnumerable(Of Char),
                                         ByVal length As Integer,
                                         ByVal isRepetitionAllowed As Boolean) As IEnumerable(Of String)

    If length = 1 Then
        Return charSet.Select(Function(c As Char)
                                  Return New String(New Char() {c})
                              End Function)

    Else
        Return PermuteCharacters(charSet, length - 1, isRepetitionAllowed).
               SelectMany(Function(x As String) charSet,
                          Function(str As String, c As Char)

                              Select Case isRepetitionAllowed

                                  Case True
                                      Return str & c

                                  Case Else
                                      ' Firstly I need to check if string is empty because 
                                      ' String.Contains() will throw an exception on empty strings.
                                      ' This need to be fixed to avoid empty strings.
                                      If String.IsNullOrEmpty(str) Then
                                          Return Nothing
                                      End If

                                      If Not str.Contains(c) Then
                                          Return str & c
                                      Else
                                          Return Nothing
                                      End If

                              End Select

                          End Function)

    End If

End Function

示例用法:

Dim permutations As IEnumerable(Of String) =
    PermuteCharacters("123456789", 2, isRepetitionAllowed:=False)

问题在于,当我将重复设置为 False 时,该函数将创建、解析并返回空字符串,这会导致较大的集合或排列长度导致性能下降。

我知道我可以使用IEnumerable.Distinct() 方法来删​​除除一个之外的所有空字符串,但这会再次迭代整个大集合,从而导致代码本身出现更多负面性能。

我如何才能有效且正确地设计函数,同时考虑性能、创建排列集合所需的总体执行时间?

重要的是,我不认为 LINQ 的使用对性能有很大的不利影响,我将继续使用 LINQ,因为它允许开发精简的代码而不是一个普通的 Loop 所需的数千行来翻译这样的 LINQ 查询。

PS:我的次要目标是在函数上实现 Iterator 关键字以进一步提高其性能,如果有人可以说明这个问题的解决方案同时实现 Iterator 功能将非常棒(并且完美)。

【问题讨论】:

  • 不能在最后加上.Where(Function(s) Not String.IsNullOrEmpty(s))吗?
  • @Bjørn-Roger Kringsjå 您建议的修改不会避免在我的Select Case 中“不需要”创建和解析空字符串,如果我附加一个WHERE 子句,我还需要添加比较以返回字符串(取决于 isRepetitionAllowed.Where...( If(isRepetitionAllowed, Return Not String.IsNullOrEmpty(str), return str=str) ) ),而且,¿a WHERE clausule 不会在“最后”再次迭代集合?。无论如何,谢谢,但WHERE 无论如何都不是一个可以利用代码性能的解决方案。
  • 我删除了我的答案。我通常不会那样做。你需要改进你的问题。添加示例输入/输出。祝你好运!
  • @Plutonix 这几乎就是第一条评论所建议的。他说他对这个想法不满意。 @ElektroStudios 如果您想生成一串字符的所有组合,我会采用更传统的方法。它不仅会更快,而且您不会遇到现在遇到的问题。最多是你现在的代码量的两倍。
  • 在这个问题中引起我注意的部分是 ...一个通用循环所需的数千行...。我不相信你真的是这个意思!一个获取所有可能组合的简单算法可能需要大约 20 行代码。我确实用 C 写了一个,它是 25 行(第一次尝试)。我的建议与@Taekahn 相同。因为您关心性能,所以编写自己的算法。这将比 linq 方法快得多。

标签: .net vb.net algorithm combinations permutation


【解决方案1】:

我认为你不应该从 linq 开始,因为它看起来不像你已经掌握了它。也许尝试一个更简单的结构:

Private Shared Iterator Function BuildCombination(distinctChars As IEnumerable(Of Char), usedChars As Stack(Of Char), length As Integer, everyCharIsDistinct As Boolean) As IEnumerable(Of String)
' we give the method everything it needs to work
    Dim availableChars As IEnumerable(Of Char) = distinctChars
    ' what chars are available
    If everyCharIsDistinct Then
        availableChars = availableChars.Where(Function(c As Char) Not usedChars.Contains(c))
    End If
    ' if the string to return is of length 1, every available char can be returned directly
    If length = 1 Then
        For Each availableChar As Char In availableChars
            Yield New String(New Char()() = { availableChar })
        Next
    Else
        ' else we add each available char to the used list and we recurse to concat it with every possible substring
        For Each availableChar As Char In availableChars
            usedChars.Push(availableChar)
            For Each possibleSubstring As String In Program.BuildCombination(distinctChars, usedChars, length - 1, everyCharIsDistinct)
                Yield New String(New Char()() = { availableChar }) + possibleSubstring 
            Next
            usedChars.Pop()
        Next
    End If
    Return
End Function

使用这个包装器调用它,我们在其中设置列表并检查合理的参数:

Private Shared Sub FindCombinations(possibleChars As String, length As Integer, everyCharIsDistinct As Boolean)
    If possibleChars.Length = 0 Then
        Throw New InvalidOperationException()
    End If
    If everyCharIsDistinct AndAlso possibleChars.Length < length Then
        Throw New InvalidOperationException()
    End If
    Dim distinctChars As IEnumerable(Of Char) = possibleChars.Distinct(Of Char)()
    Dim listOfUsedChars As Stack(Of Char) = New Stack(Of Char)()
    For Each s As String In Program.BuildCombination(distinctChars, listOfUsedChars, length, everyCharIsDistinct).ToList(Of String)()
        Console.WriteLine(s)
    Next
End Sub

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-09-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-12
    • 1970-01-01
    相关资源
    最近更新 更多