【问题标题】:Number of reshuffles used to sort用于排序的重新洗牌次数
【发布时间】:2021-12-18 02:50:06
【问题描述】:

我最近发现了一个有趣的问题,看起来是这样的:

有一个枯燥的排序算法,它从数组中取出第一个数字,它找到一个比第一个元素低1的元素(或者当没有更低的元素时它会取最高元素一),并将其放在前面。 将索引为 x(从 0 开始计数)的元素放在前面的成本等于它的索引。 它继续这个过程,直到数组被排序。 任务是计算排序所有 n! 的成本!从 1 到 n 的数字排列。答案可能很大,所以答案应该以 m 为模(输入中给出了 n 和 m)

示例:

Input (n,m): 3 40
Answer: 15

There are permutations of numbers from 1 to 3. The costs of sorting them are:
(1,2,3)->0
(1,3,2)->5
(2,1,3)->1
(2,3,1)->2
(3,1,2)->4
(3,2,1)->3
  sum = 15

我的程序生成所有可能的数组并一一排序。它的复杂度是 O(n!*n^2),太高了。我被我所有的想法和这种蛮力解决方案所困。

我还发现了一些有趣的事情:

  1. 当我按这些成本的数量对排列排序的成本进行分组时,我得到了一个奇怪的数字: n=7 https://cdn.discordapp.com/attachments/503942261051228160/905511062546292807/unknown.png (每行有 2 个数字 x * y,在它们之前有 y 个空格。x 排序成本,y 排列成本(1-n)的排序成本等于 y)。分数是所有行 x*y 的总和。如果需要,我可以为其他人提供这个数字。
  2. 可能与斐波那契数有关,只是一种感觉。
  3. 从图 1 中可以看出,有一个段,其中所有 y 都相同。它从 x=n^2-4n+3 开始。

问题:我怎样才能更有效地解决这个问题?

【问题讨论】:

  • "将索引为 x 的元素(从 0 开始计数)放在前面的成本等于它的索引。" - 它是原始数组中的索引(与当前迭代中的索引相反),对吧?
  • 是当前迭代中的索引。 4231,这里下一步的成本是2,那么我们有3421,下一步也是2,而不是1。

标签: algorithm sorting sequence factorial


【解决方案1】:

排序算法有两个阶段:首先对排列进行排序 进入身份的某种旋转,然后将其旋转到身份。 我们分别计算了这些阶段的成本。

第一阶段最多包含 n-2 步。在 n−1−j 个移动之后, 排列由 n−j 个值 x, x+1, x+2, ... mod n 后跟 a 剩余 j 值的排列,假设我们从 随机排列,同样可能以任何特定顺序排列。 我们必须移动 x−1 mod n 的预期距离是 ((n−j)+(n−1))/2。 但是等一下,如果我们仍处于第一阶段,我们只会计算移动。 因此,我们需要对排列已经是 回转。有 n!/j!其中,它们最后都有 x−1,所以 每个的折扣是 n-1。

第二阶段平均包括 (n−1)/2 次从结束的移动 排列到开头,每个花费 n-1。平均费用 总而言之n!因此排列为 (n-1)²/2。

我将在下面留下 Python 的模算术/强度缩减 作为练习。

from itertools import permutations
from math import factorial


# Returns the total cost of sorting all n-element permutations.
def fast_total_sort_cost(n):
    cost = 0
    for j in range(n - 1, 0, -1):
        cost += factorial(n) * ((n - j) + (n - 1)) // 2
        cost -= (factorial(n) // factorial(j)) * (n - 1)
    return cost + factorial(n) * (n - 1) ** 2 // 2


# Reference implementation and test.
def reference_total_sort_cost(n):
    return sum(sort_cost(perm) for perm in permutations(range(n)))


def sort_cost(perm):
    cost = 0
    perm = list(perm)
    while not is_sorted(perm):
        i = perm.index((perm[0] - 1) % len(perm))
        cost += i
        perm.insert(0, perm.pop(i))
    return cost


def is_sorted(perm):
    return all(perm[i - 1] <= perm[i] for i in range(1, len(perm)))


if __name__ == "__main__":
    for m in range(1, 9):
        print(fast_total_sort_cost(m), reference_total_sort_cost(m))

【讨论】:

  • 这太棒了。转移到 mod n,考虑旋转;有很多微妙之处。
【解决方案2】:

从排序后的排列 123...n 中,您可以使用相反的规则构建一棵树,并获得所有排列。请参阅此树的 n=4。

现在,观察一下

如果节点==1234 那么成本(节点)= 0

如果节点!=1234 那么成本(节点)= blue_label(节点)+ 成本(父)

您需要制定反向规则来生成树。 也许使用一些记忆技术来避免每次重新计算成本。

【讨论】:

    猜你喜欢
    • 2012-02-23
    • 1970-01-01
    • 2021-04-23
    • 2018-03-09
    • 2012-01-08
    • 2014-02-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多