直接从算法中获取第 n 个唯一排列,无需生成前一个

问题描述 投票:0回答:1

能否通过算法获得初始值的第 n 个唯一的、视觉上唯一的排列(即使按字母顺序排序)?

如果我们有一个初始值,例如0101,唯一的排列将是0011、0110、1100、0101、1010、1001,通过算法,如果我输入数字3,我应该得到唯一的排列例如,直接 1100,无需计算上述排列。直接生成它很重要,因为如果我们有一个初始值 0101011010101010110110010101010101010 并且我们想要获得唯一的排列 82873648234,如果我们生成所有以前的排列,将花费太多时间。有什么方法吗?我在这个论坛上搜索了多个类似的问题,但我无法获得这个具体案例的答案。我们需要直接处理独特的排列,而不生成以前的排列。

我尝试了几种代码选项。例如,这段代码不仅生成一次唯一的排列,而且重复它们并且不起作用。

Private Sub Button5_Click(sender As Object, e As EventArgs) Handles Button5.Click
        Dim str As String = "0011"
        Dim target As Integer = 4 ' La permutación que quieres generar
        Dim arr() As Char = str.ToCharArray()
        Array.Sort(arr)
        str = New String(arr)

        For i = 0 To 5
            Dim result As String = FactoradicPermutation(str, i)
            MsgBox(result)
        Next

    End Sub
    Function FactoradicPermutation(ByVal str As String, ByVal index As Integer) As String
        Dim factoradic() As Integer = ToFactoradic(index, str.Length)
        Dim chars As List(Of Char) = New List(Of Char)(str)
        Dim result As String = ""

        For Each i As Integer In factoradic
            result += chars(i)
            chars.RemoveAt(i)
        Next

        Return result
    End Function

    Function ToFactoradic(ByVal num As Integer, ByVal length As Integer) As Integer()
        Dim factoradic(length - 1) As Integer

        For i As Integer = 1 To length
            factoradic(length - i) = num Mod i
            num \= i
        Next

        Return factoradic
    End Function
algorithm math permutation
1个回答
0
投票

这就是“只是数数”。我将用 Python 对其进行编码,并且我将使用您的更长的示例

perm = '0101011010101010110110010101010101010'

我们有一些符号集,并且我们有每个符号出现的次数。让我们得到频率分布。 (所有代码均采用 Python 编写。)

def perm_to_freq (perm):
    freq = {}
    for x in perm:
        freq[x] = freq.get(x, 0) + 1
    return freq

对于这个例子,我们得到

freq
{'0': 18, '1': 19}
分布。

我们有多少种排列?这就是多项式系数。在这种情况下,它将是

factorial(18+19)/factorial(18)/factorial(19)
。但我发现像这样交错乘法和除法更容易:

def multichoose (freq):
    answer = 1
    prev_count = 0
    for count in freq.values():
        # This will be 1, 2, ..., count
        for i in range(1, count+1):
            answer = answer * (i + prev_count) // i
        prev_count += count
    return answer

在我们的示例中,我们得到

17672631900
。这就是 18
0
和 19
1
的排列数。

现在,如果我们按顺序列出所有这些排列,在哪里可以找到“0101011010101010110110010101010101010”?为了回答这个问题,我们需要计算它之前有多少个排列。逻辑是,每次我们看到下一项时,我们都需要计算在它有一个更小的项之前有多少种排列。所以计数是这样开始的:

First term '0'.
    Nothing came before it.
    {'0': 17, '1': 19} left.
Second term '1'.
    multichoose({'0': 16, '1': 19}) = 4059928950 had a leading 0 came before it.
    {'0': 17, '1': 18} left.
Third term '0'.
    Nothing more came before it.
    {'0': 16, '1': 18} left.
Fourth term '1'.
    multichoose({'0': 15, '1': 18}) = 1855967520 more had a 0 before it.
    {'0': 16, '1': 17} left.

等等。然后我们只需添加

0 + 4059928950  + 0 + 1855967520 ...
即可获得我们的排名。

这是代码。

def rank_perm (perm):
    freq = perm_to_freq(perm)
    alphabet = sorted(freq.keys())

    answer = 0
    for x in perm:
        # Look at all that could come before x
        for y in alphabet:
            if x == y:
                # We reached x so stop counting.
                break
            elif 0 < freq[y]:
                # Count everything with a y here.
                freq[y] -= 1
                answer += multichoose(freq)
                freq[y] += 1
        freq[x] -= 1
    return answer

在我们的示例中,它给出了

5558234991
。这就是我们之前的排列数量。

现在要找到哪个排列具有哪个等级,我们只需反转该过程即可。

def unrank_perm (perm, rank):
    freq = perm_to_freq(perm)
    alphabet = sorted(freq.keys())

    answer = []
    for i in range(len(perm)):
        for x in alphabet:
            if 0 < freq[x]:
                # How many have x here?
                freq[x] -= 1
                count = multichoose(freq)
                if count < rank:
                    # Count them, then put x back in.
                    rank -= count
                    freq[x] += 1
                else:
                    answer.append(x)
                    break # Found this one.
    return ''.join(answer) # Turn it into a string.

您可以像这样找到前几个排列:

perm = '0101011010101010110110010101010101010'
for i in range(30):
    print(unrank_perm(perm, i))

如果我们按照已有的排列查找排列,我们应该得到原始的排列:

rank = rank_perm(perm)
print(perm == unrank_perm(perm, rank))
© www.soinside.com 2019 - 2024. All rights reserved.