【问题标题】:Generate a list of permutations paired with their number of inversions生成与其反转次数配对的排列列表
【发布时间】:2014-12-13 11:39:28
【问题描述】:

我正在寻找一种生成集合的所有排列的算法。为方便起见,该集合始终为[0, 1..n]。有很多方法可以做到这一点,而且并不是特别难。

我还需要每个排列的反转次数。 执行此操作的最快(就时间复杂度而言)算法是什么?

我希望有一种方法可以生成那些产生反转次数的排列,作为副作用而不增加复杂性。

算法应该生成列表,而不是数组,但如果它在速度方面有足够大的差异,我会接受基于数组的。

加分(...没有分...)如果它是功能性的并且是用纯语言实现的。

【问题讨论】:

标签: algorithm permutation


【解决方案1】:

Steinhaus–Johnson–Trotter algorithm 允许在排列生成期间轻松保持反转计数。维基摘录:

Thus, from the single permutation on one element,
1
one may place the number 2 in each possible position in descending
order to form a list of two permutations on two elements,
1 2
2 1
Then, one may place the number 3 in each of three different positions
for these three permutations, in descending order for the first 
permutation 1 2, and then in ascending order for the permutation 2 1:
1 2 3
1 3 2
3 1 2
3 2 1
2 3 1
2 1 3

在递归的每一步,我们都会在较小的数字列表中插入最大的数字。很明显,这个插入增加了 M 个新的反转,其中 M 是插入位置(从右数)。例如,如果我们有 3 1 2 列表(2 个反转),并且将插入 4

3 1 2 4  //position 0, 2 + 0 = 2 inversions
3 1 4 2  //position 1, 2 + 1 = 3 inversions
3 4 1 2  //position 2, 2 + 2 = 4 inversions
4 3 1 2  //position 3, 2 + 3 = 5 inversions

伪代码:

function Generate(List, Count)
   N = List.Length
   if N = N_Max then
      Output(List, 'InvCount = ': Count)
   else
      for Position = 0 to N do
         Generate(List.Insert(N, N - Position), Count + Position)

附:递归方法在这里不是强制性的,但我怀疑它对于功能性的人来说是很自然的

P.P.S如果您担心插入到列表中,请考虑 Even's speedup section,它仅使用相邻元素的交换,并且每次交换都会增加或减少反转计数 1。

【讨论】:

  • 我有点担心插入列表,但也许这不会成为问题。当我有时间更好地查看它时,我会接受答案。
【解决方案2】:

这是一个完成该任务的算法,它被摊销 O(1) 置换,并生成一个链表元组数组,这些链表可以合理地共享尽可能多的内存。

我将在未经测试的 Python 中实现除链表位之外的所有内容。虽然 Python 对于真正的实现来说是一种糟糕的语言。

def permutations (sorted_list):
    answer = []
    def add_permutations(reversed_sublist, tail_node, inversions):
        if (0 == len(sorted_sublist)):
            answer.append((tail_node, inversions))
        else:
            for idx, val in enumerate(reversed_sublist):
                add_permutations(
                    filter(lambda x: x != val),
                    ListNode(val, tail_node,
                    inversions + idx
                )

    add_permutations(reversed(sorted_list), EmptyListNode(), 0)
    return answer

您可能想知道我对所有这些复制的摊销O(1) 的声明。那是因为如果留下m 元素,我们会做O(m) 工作,然后将其摊销到m! 元素上。因此,更高级别节点的摊销成本是每次底部调用的收敛成本,我们需要每个排列一个。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多