【问题标题】:Counting Inversions between 2 arrays计算 2 个数组之间的反转
【发布时间】:2016-06-26 20:00:19
【问题描述】:

我正在寻找一种算法来解决这个问题:

给定 2 个数组,A 和 B,它们都是 [1..n] 的排列,这两个数组之间的反转次数是多少?

这里的反转是当一对元素 (i,j) 成立时:

if A.indexOf(i) > A.indexOf(j) && B.indexOf(i) < B.indexOf(j)

if A.indexOf(i) < A.indexOf(j) && B.indexOf(i) > B.indexOf(j)

我知道当您可以假设第一个数组已排序时,有多种方法可以做到这一点,例如在执行 MergeSort 时计算反转,但我有 2 个未排序的数组。

例子:

A = [6,3,4,1,2,5] and B = [3,5,2,6,1,4]
No. of inversions = 9
6 has a lower index than 3 in A, but a higher index than 3 in B. This is an inversion.

我希望使用分而治之的方法以 O(n log n) 的时间复杂度实现这一目标。

【问题讨论】:

  • 为什么您删除了关于使用 OpenCL 实施呼吸优先搜索的其他问题?
  • 我联系了我的老师,感谢他,我已经得到了解决方案,所以我觉得这里不再需要这个问题了。
  • 最好为未来的访客介绍您自己的答案,而不是删除一个好问题。

标签: algorithm mergesort divide-and-conquer


【解决方案1】:

为了做到这一点,我们可以做一个简单的替换(取自您的示例): 6->1、3->2、4->3、1->4、2->5、5->6。 因此,第一个列表变为 [1,2,3,4,5,6],第二个变为 [2,6,5,1,4,3]。然后,我们可以运行一个简单的 O(n log n) 算法来计算第二个列表中的反转次数,从而给出答案。

这种转换可以使用额外的索引列表在 O(n) 操作中完成。 ind[i] 将是第一个列表中给定数字的索引,因此,如果 A[1]=6,则 ind[6]=1。

构造ind数组的代码:

    var A = new List<int> {6, 3, 4, 1, 2, 5};
    foreach (var num in A) Console.Write(num + " ");
    Console.WriteLine("");

    var indexes = new int[7];
    for (var i = 0; i < 6; ++i)
        indexes[A[i]] = i + 1;

    for (var i = 1; i < 7; ++i) Console.Write(indexes[i] + " ");
    Console.WriteLine("");

然后,给定这个数组(在本例中为 [4,5,2,3,6,1]),我们可以使用 resultArray[i] = ind[B[i]];

构造替换数组的代码:

    var B = new List<int> {3, 5, 2, 6, 1, 4};

    var resultList = new List<int>();
    for (var i = 0; i < 6; ++i)
        resultList.Add(indexes[B[i]]);

    for(var i = 0; i < 6; ++i)
        Console.Write(resultList[i] + " ");

然后我们可以运行算法来计算最后一个数组中的排列,这就是答案。 (我可以给出 O(n log n) 中计算排列的代码,但我认为这不是问题所在)。

【讨论】:

  • 为什么回答中提到的上述方法与问题中提到的 kendall tau 方法的反转次数不同? Consider this example where A = 1, 4, 2, 3, 0 and B = 3, 4, 0, 2, 1. I get inversion as 7 if I follow steps mentioned in the answer whereas I get inversions as 3 with kendall tau method
  • @h8red 你能回答上面评论中提出的问题吗
【解决方案2】:

这对你有用吗(虽然 O(N^2) 时间复杂度):

        int[] A = ...;
        int[] B = ..;

        int count = 0;

        for (int i = 0; i < A.Length-1; i++) {
            for (int j = i+1; j < A.Length; j++) {
                if ((A[i] > A[j] && B[i] < B[j]) || (A[i] < A[j] && B[i] > B[j])) {
                    count++;
                }
            }
        }

【讨论】:

  • 感谢您的评论,但恐怕 O(n^2) 不够快。我的目标是使用分而治之 O(n log n)。
  • 这里也有解释 O(n Log n) => geeksforgeeks.org/counting-inversions ;尝试摆弄 B 作为比较指标而不是索引
  • 这到底是什么意思?我是否必须尝试操作数组以使 1 成为排序数组 1..n?
  • 您只需要比较,不是针对索引 (A[i],A[j]) 和 (i,j),而是针对不同的目标:(A[i],A[j] ) 和 (B[i],B[j])
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-09-25
  • 2013-04-06
  • 1970-01-01
  • 1970-01-01
  • 2016-12-13
  • 2021-03-11
相关资源
最近更新 更多