2

Я новичок в Excel VBA и учусь, изменяя / изменяя существующий код. Я попробовал некоторый код, который берет строку и дает мне следующую перестановку. Мои данные находятся в ячейке A1 и состоят из чисел, разделенных запятыми. Он рассматривает разделители как часть данных. Если я попытаюсь переставить двойные цифры (10 и т.д.), Они будут восприниматься как 1 и 0.

Function nextPerm(s As String)
' inspired by http://stackoverflow.com/questions/352203/generating-permutations-lazily
' this produces the "next" permutation
' it allows one to step through all possible iterations without having to have them
' all in memory at the same time
    Dim L As Integer, ii As Integer, jj As Integer
    Dim c() As Byte, temp As Byte

    L = Len(s)

    If StrComp(s, "**done**") = 0 Or StrComp(s, "") = 0 Then
        nextPerm = ""
        Exit Function
    End If

' convert to byte array... more compact to manipulate
    ReDim c(1 To L)
    For ii = 1 To L
        c(ii) = Asc(Mid(s, ii, 1))
    Next ii

' find the largest "tail":
    For ii = L - 1 To 1 Step -1
        If c(ii) < c(ii + 1) Then Exit For
    Next ii

' if we complete the loop without break, ii will be zero
    If ii = 0 Then
        nextPerm = "**done**"
        Exit Function
    End If

' find the smallest value in the tail that is larger than c(ii)
' take advantage of the fact that tail is sorted in reverse order
    For jj = L To ii + 1 Step -1
        If c(jj) > c(ii) Then
            ' swap elements
            temp = c(jj)
            c(jj) = c(ii)
            c(ii) = temp
            Exit For
        End If
    Next jj

' now reverse the characters from ii+1 to the end:
    nextPerm = ""
    For jj = 1 To ii
        nextPerm = nextPerm & Chr(c(jj))
    Next jj
    For jj = L To ii + 1 Step -1
        nextPerm = nextPerm & Chr(c(jj))
    Next jj
End Function

Что мне нужно изменить, чтобы сделать эту работу?

2 ответа2

0

Вот версия для списков, разделенных запятыми:

Function nextPerm2(s As String)
' inspired by http://stackoverflow.com/questions/352203/generating-permutations-lazily
' this produces the "next" permutation
' it allows one to step through all possible iterations without having to have them
' all in memory at the same time
    Dim L As Integer, ii As Integer, jj As Integer
    Dim c() As Variant, temp As Variant

    L = Len(s)

    If StrComp(s, "**done**") = 0 Or StrComp(s, "") = 0 Then
        nextPerm2 = ""
        Exit Function
    End If

' convert to byte array... more compact to manipulate
    arr = Split(s, ",")

    ReDim c(1 To UBound(arr) + 1)
    For ii = 1 To UBound(arr) + 1
        c(ii) = arr(ii - 1)
    Next ii
    L = UBound(arr) + 1
' find the largest "tail":
    For ii = L - 1 To 1 Step -1
        If c(ii) < c(ii + 1) Then Exit For
    Next ii

' if we complete the loop without break, ii will be zero
    If ii = 0 Then
        nextPerm2 = "**done**"
        Exit Function
    End If

' find the smallest value in the tail that is larger than c(ii)
' take advantage of the fact that tail is sorted in reverse order
    For jj = L To ii + 1 Step -1
        If c(jj) > c(ii) Then
            ' swap elements
            temp = c(jj)
            c(jj) = c(ii)
            c(ii) = temp
            Exit For
        End If
    Next jj

' now reverse the characters from ii+1 to the end:
    nextPerm2 = ""
    For jj = 1 To ii
        nextPerm2 = nextPerm2 & c(jj) & ","
    Next jj
    For jj = L To ii + 1 Step -1
        nextPerm2 = nextPerm2 & c(jj) & ","
    Next jj

    If Right(nextPerm2, 1) = "," Then nextPerm2 = Left(nextPerm2, Len(nextPerm2) - 1)
End Function

Разбор использует Split() и есть другие изменения.

Не полностью протестирован!

0

Я не изменил алгоритм в начальных сообщениях:

но я изменил код VBA на более описательные имена переменных и разрешил разделители в качестве параметров в исходной строке:


Option Explicit

Public Sub ShowPerm()

    With Sheet1
        .Range("B1") = nextPerm2(.Range("A1"))
        .Range("B2") = nextPerm2(.Range("A2"), " ")
        .Range("B3") = nextPerm2(.Range("A3"), " ")
        .Range("B4") = nextPerm2(.Range("A4"))
    End With

    'if A1 = "3,2,5,4,1"    Then B1 = "3,4,1,2,5"
    'if A2 = "3 222 5 4 1"  Then B2 = "3 4 1 222 5"
    'if A3 = "1"            Then B3 = "**done**"
    'if A4 = "2"            Then B4 = "**done**"

End Sub

Public Function nextPerm2(ByVal strIni As String, _
                          Optional ByVal delim As String = ",") As String

'inspired by http://stackoverflow.com/questions/352203/generating-permutations-lazily
'this produces the "next" permutation it allows one to step through all possible
'iterations without having to have them all in memory at the same time

    Dim arr As Variant, arrSz As Long, i As Long, j As Long, tmp As Byte

    If strIni = "**done**" Or Len(strIni) = 0 Then Exit Function

    arr = Split(strIni, delim)      'convert to array

    arrSz = UBound(arr)

    For i = 0 To arrSz
        arr(i) = Trim(arr(i))       'clean-up white-spaces from each item
    Next i
    For i = arrSz - 1 To 0 Step -1  'find the largest "tail"
        If arr(i) < arr(i + 1) Then Exit For
    Next i
    If i = 0 Or i = -1 Then         'if loop complete, i is 0; if i = -1, arrSz = 0
        nextPerm2 = "**done**"
        Exit Function
    End If

    'find the smallest value in the tail that is larger than arr(i)
    'take advantage of the fact that tail is sorted in reverse order
    For j = arrSz To i + 1 Step -1
        If arr(j) > arr(i) Then     'swap elements
            tmp = arr(j)
            arr(j) = arr(i)
            arr(i) = tmp
            Exit For
        End If
    Next j

    'now reverse the characters from i + 1 to the end
    nextPerm2 = vbNullString
    For j = 0 To i
        nextPerm2 = nextPerm2 & arr(j) & delim
    Next j
    For j = arrSz To i + 1 Step -1
        nextPerm2 = nextPerm2 & arr(j) & delim
    Next j

    nextPerm2 = Left(nextPerm2, Len(nextPerm2) - 1) 'remove last delim

End Function

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .