【问题标题】:Python, permutation to permuation-index functionPython,排列到排列索引函数
【发布时间】:2020-05-07 11:06:09
【问题描述】:

我有一个列表的一些排列:

>>> import itertools
>>> perms = list(itertools.permutations([0,1,2,3]))
>>> perms
[(0, 1, 2, 3), (0, 1, 3, 2), (0, 2, 1, 3), (0, 2, 3, 1), (0, 3, 1, 2), (0, 3, 2, 1), (1, 0, 2, 3), (1, 0, 3, 2), (1, 2, 0, 3), (1, 2, 3, 0), (1, 3, 0, 2), (1, 3, 2, 0), (2, 0, 1, 3), (2, 0, 3, 1), (2, 1, 0, 3), (2, 1, 3, 0), (2, 3, 0, 1), (2, 3, 1, 0), (3, 0, 1, 2), (3, 0, 2, 1), (3, 1, 0, 2), (3, 1, 2, 0), (3, 2, 0, 1), (3, 2, 1, 0)]
>>> len(perms)
24

我可以使用什么函数(不访问列表perm)来获取任意排列的索引,例如(0, 2, 3, 1) -> 3?

(您可以假设置换的元素始终是从零开始的升序整数列表。)

提示:可能涉及阶乘数系统。 https://en.wikipedia.org/wiki/Factorial_number_system

【问题讨论】:

  • 您要求的是ranking 一个排列。您正在寻找的有很多实现......只需向下滚动到 Python 部分。

标签: python permutation itertools factorial


【解决方案1】:

我突然想到了以下内容,但没有彻底测试。

from math import factorial
elements = list(range(4))
permutation = (3, 2, 1, 0)
index = 0
nf = factorial(len(elements))

for n in permutation:
    nf //= len(elements)
    index += elements.index(n) * nf
    elements.remove(n)

print(index)

编辑:nf /= len(elements)替换为nf //= len(elements)

【讨论】:

  • 您可能想使用nf //= len(elements) 来保留int
【解决方案2】:

我想这是一个挑战,所以这是我的(递归)答案:

import math
import itertools

def get_index(l):
    # In a real function, there should be more tests to validate that the input is valid, e.g. len(l)>0
    # Terminal case
    if len(l)==1:
        return 0

    # Number of possible permutations starting with l[0]
    span = math.factorial(len(l)-1)

    # Slightly modifying l[1:] to use the function recursively
    new_l = [ val if val < l[0] else val-1 for val in l[1:] ]

    # Actual solution
    return get_index(new_l) + span*l[0]


get_index((0,1,2,3))
# 0
get_index((0,2,3,1))
# 3
get_index((3,2,1,0))
# 23
get_index((4,2,0,1,5,3))
# 529
list(itertools.permutations((0,1,2,3,4,5))).index((4,2,0,1,5,3))
# 529

【讨论】:

    【解决方案3】:

    您需要编写自己的函数。像这样的东西会起作用

    import math
    
    def perm_loc(P):
    
        N = len(P)
        assert set(P) == set(range(N))
    
        def rec(perm):
            nums = set(perm)
            if not perm:
                return 0
            else:
                sub_res = rec(perm[1:])                  # Result for tail of permutation
                sub_size = math.factorial(len(nums) - 1) # How many tail permutations exist
                sub_index = sorted(nums).index(perm[0])  # Location of first element in permutaiotn
                                                         # in the sorted list of number
                return sub_index * sub_size + sub_res
    
        return rec(P)
    

    完成所有工作的函数是rec,perm_loc 只是作为它的包装器。请注意,此算法基于itertools.permutation 碰巧使用的置换算法的性质。

    以下代码测试上述功能。首先是您的样本,然后是 range(7) 的所有排列:

    print perm_loc([0,2,3,1]) # Print the result from the example
    
    import itertools
    
    def test(N):
        correct = 0
        perms = list(itertools.permutations(range(N)))
        for (i, p) in enumerate(perms):
            pl = perm_loc(p)
            if i == pl:
                correct += 1
            else:
                print ":: Incorrect", p, perms.index(p), perm_loc(N, p)
        print ":: Found %d correct results" % correct
    
    test(7) # Test on all permutations of range(7)
    

    【讨论】:

      【解决方案4】:
      from math import factorial
      
      def perm_to_permidx(perm):
          # Extract info
          n = len(perm)
          elements = range(n)
          # "Gone"s will be the elements of the given perm
          gones = []
          # According to each number in perm, we add the repsective offsets
          offset = 0
          for i, num in enumerate(perm[:-1], start=1):
              idx = num - sum(num > gone for gone in gones)
              offset += idx * factorial(n - i)
              gones.append(num)
          return offset
      
      the_perm = (0, 2, 3, 1)
      print(perm_to_permidx(the_perm))
      # 3
      

      解释:给定范围内的所有排列都可以视为一组排列。因此,例如,对于0, 1, 2, 3 的排列,我们首先“固定” 0 并置换余数,然后修复 1 并置换余数,依此类推。一旦我们确定了一个数字,剩下的就是排列;所以我们再次从剩余的数字中一次固定一个数字并排列其余的数字。这种情况一直持续到我们只剩下一个数字。每个级别的修复都有一个对应的(n-i)! 排列。

      因此,此代码会找到每个排列级别的“偏移量”。当我们按顺序固定perm 的数量时,offset 对应于给定排列的开始位置。对于(0, 2, 3, 1) 的给定示例,我们首先查看给定perm 中的第一个数字0,并将偏移量计算为0。然后进入gones 列表(我们将看到它的用法)。然后,在下一个排列级别,我们将 2 视为固定数。为了计算这个偏移量,我们需要这个 2 在其余三个数字中的“顺序”。这就是gones 发挥作用的地方;如果一个已经固定并考虑过的数字(在本例中为 0)小于当前的固定器,我们减 1 以找到新的顺序。然后计算偏移量并累加。对于下一个数字 3,新顺序是 3 - (1 + 1) = 1,因为之前的固定器 0 和 2 都在 3 的“左侧”。

      这一直持续到给定perm 的最后一个数字,因为不需要查看它;无论如何都会确定的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-03-27
        • 2017-04-08
        • 1970-01-01
        • 2020-10-25
        • 2023-03-30
        • 2016-01-16
        相关资源
        最近更新 更多