Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 排列中的交换数_C++_Algorithm_Permutation - Fatal编程技术网

C++ 排列中的交换数

C++ 排列中的交换数,c++,algorithm,permutation,C++,Algorithm,Permutation,是否有一种有效的算法(在大O表示法方面是有效的)来找到交换数,从而将置换p转换为恒等置换I?互换不需要在相邻的元素上,而是在任何元素上 例如: I = {0, 1, 2, 3, 4, 5}, number of swaps is 0 P = {0, 1, 5, 3, 4, 2}, number of swaps is 1 (2 and 5) P = {4, 1, 3, 5, 0, 2}, number of swaps is 3 (2 with 5, 3 with 5, 4 with 0) 一

是否有一种有效的算法(在大O表示法方面是有效的)来找到交换数,从而将置换p转换为恒等置换I?互换不需要在相邻的元素上,而是在任何元素上

例如:

I = {0, 1, 2, 3, 4, 5}, number of swaps is 0
P = {0, 1, 5, 3, 4, 2}, number of swaps is 1 (2 and 5)
P = {4, 1, 3, 5, 0, 2}, number of swaps is 3 (2 with 5, 3 with 5, 4 with 0)
一个想法是编写如下算法:

int count = 0;
for(int i = 0; i < n; ++ i) {
    for(; P[i] != i; ++ count) { // could be permuted multiple times
        std::swap(P[P[i]], P[i]);
        // look where the number at hand should be
    }
}
int count=0;
对于(int i=0;i
但我不太清楚这是否真的保证终止,或者它是否找到了正确的互换数量。它适用于上述示例。我尝试在5和12个数字上生成所有排列,它总是在这些数字上终止

这个问题出现在数值线性代数中。一些矩阵分解使用旋转,它有效地将具有最大值的行交换给下一个要操作的行,以避免被小数字除法并提高数值稳定性。某些分解,如LU分解,可在以后用于计算矩阵行列式,但如果排列数为奇数,则分解行列式的符号与原始矩阵的符号相反

编辑:我同意这个问题类似于。但我认为这个问题更为根本。将一个置换转换为另一个置换可以转化为这个问题,方法是将O(n)中的目标置换反转,在O(n)中组合置换,然后找到从那里到标识的交换数。通过将身份显式表示为另一个置换来解决这个问题似乎是次优的。另外,直到昨天,另一个问题有四个答案,其中只有一个(由| \/| ad)看起来有用,但对方法的描述似乎含糊不清。现在,用户lizusek在这里回答了我的问题。我不同意以重复的方式结束这个问题


EDIT2:正如用户rcgldr在评论中指出的,建议的算法实际上似乎是相当优化的,请参见我对的回答。

我认为关键是要根据

这表示任何置换都是不相交循环的产物

关键事实是:

  • 在两个不相交的循环中交换元素会产生一个更长的循环
  • 在同一个周期中交换元素会减少一个周期
  • 所需的置换数为n-c,其中c是分解中的循环数
  • 您的算法总是在同一个周期内交换元素,因此将正确计算所需交换的数量

    如果需要,也可以通过计算循环分解并返回n减去找到的循环数,在O(n)中执行此操作

    计算循环分解可以在O(n)中完成,方法是从第一个节点开始,然后进行置换,直到再次到达起点。标记所有已访问的节点,然后在下一个未访问的节点重新开始。

    我相信以下是正确的:

    如果
    S(x[0],…,x[n-1])
    是将
    x
    转换为
    {0,1,…,n-1}
    所需的最小掉期数量,则:

  • 如果
    x[n-1]==n-1
    ,则
    S(x)==S(x[0],…,x[n-2])
    (即,切断最后一个元素)
  • 如果
    x[-1]!=n-1
    ,然后
    S(x)==S(x[0],…,x[n-1],…,x[i],…x[n-2])+1
    ,其中
    x[i]==n-1
  • S({})=0
  • 这就提出了一种计算
    S(x)
    的简单算法,该算法在
    O(n)
    时间内运行:

    int num_swaps(int[] x, int n) {
      if (n == 0) {
        return 0;
      } else if (x[n - 1] == n  - 1) {
        return num_swaps(x, n - 1);
      } else {
        int* i = std::find(x, x + n, n - 1);
        std::swap(*i, x[n - 1])
        return num_swaps(x, n - 1) + 1;
      }
    }
    

    @我没有这方面的书。它几乎没有什么关系,因为分解可以简单地返回它所做的排列数量,而不需要计算。我从理论的角度对这种算法感兴趣。如果您对学习数值方法感兴趣,我建议您购买最新版的数值公式()。您可以计算乘法因子的循环长度,这将告诉您需要多少次交换才能将其转换为标识。请注意,每次交换都会在其适当位置放置至少一个元素,所以对于n个数,效率是O(n)。我不知道如何才能获得更高的效率。可能重复的,我们可以重新打开这个,这样我可以粘贴一个完整的C++实现,在我被迫提出的那一刻?第一篇文章中的示例代码将比这个递归方法快,尽管两者都是O(n)。这是O(n)还是更像O(n log n)和最坏情况O(n^2/2),因为
    std::find()
    ?可能复杂性取决于交换的数量和交换的方式。@如果你是对的,这种方法可能是O(n^2)。使用哈希映射作为查找的反向索引而不是
    std::find
    可能会得到O(n)。