Antoine
Antoine

Reputation: 946

Converting permutation to inversion representation

Permutation P = [a1, a2, ... , aN] of the first N natural numbers can be represented by a list of inversions I = [i1, i2, ... , iN], where iK tells us how many numbers that are greater than K can be found before K in the permutation P.

Example: if P = [3, 1, 4, 2], then I = [1, 2, 0, 0] (3 is placed 1, 3 and 4 are placed before 2, whereas 3 and 4 are not preceded by any bigger number).

There is an obvious algorithm that converts a permutation from standard to inversion form and runs in O(N^2) (we just follow the definition and count). The same holds for the inverse conversion (which is slightly less straight forward).

Is there an algorithm that has lower time complexity?

Upvotes: 1

Views: 283

Answers (2)

Antoine
Antoine

Reputation: 946

Meanwhile, I have found a simple solution, regarding pseudo-code, which works in O(n log n), also.

initialize AVL-tree T
initialize array I of length n
for i = 1, 2, ... , n:
    I[P[i]] = T.countGreaterThan(P[i])
    T.add(P[i])

Upvotes: 0

kfx
kfx

Reputation: 8537

There is a simple iterative dynamic-programming algorithm to solve this: for all i from 1 to n (length of the permutation) take the number i and look how many elements in P left of i have already been seen. Since we process i in increasing order, we know that the elements not seen are the elements bigger than i - and so we count and write down the number of those elements. The trick is to introduce an external list than keeps track of which elements in P have already been seen.

For the start, lets look at how to do it in O(n^2) way. For example, if P=[4, 3, 2, 1], then the algorithm would executed as follows:

  1. Create a structure tree initialized to zeros. It holds "1" in position j if element with j-th position in P has already been seen by the iterative algorithm.

  2. Take 1, determine that pos==3. Write "1" down in the tree[pos]. Calculate num_seen=sum(tree[0:3]) which is equal to 0. Write down pos - num_seen + 1 at I[0]. After this: tree = [0, 0, 0, 1], I = [3, 0, 0, 0]

  3. Take 2, write "1" down in the tree[1] and 1 at I[1]. tree = [0, 1, 0, 1], I=[3,1,0,0].

  4. Take 3, write "1" down in the tree[2] and 0 at I[2]. tree = [0, 1, 1, 1], I=[3,1,0,0].

  5. Take 4, write "1" down in the tree[0] and 0 at I[3]. tree = [1, 1, 1, 1], I=[3,1,0,0].

The second trick is to use an efficient data structure to count the number of the seen elements in O(n log n) time instead of O(n^2) as above.

Here is Python code that uses a Fenwick tree to count the number of seen elements in a fast way:

def ft_sum(tree, a, b):
  if a == 0:
      s = 0;
      while  b >= 0:
          s += tree[b];
          b = (b & (b + 1)) - 1
      return s
  return ft_sum(tree, 0, b) - ft_sum(tree, 0, a - 1)

def ft_adjust(tree, k, v):
    while k < len(tree):
        tree[k] += v
        k |= k + 1

def calcI(P):
    n = len(P)
    tree = [0] * n
    I = [0] * n
    positions = [0] * n
    for i in xrange(n):
        positions[P[i]-1] = i
    tree = [0] * n
    for i in xrange(n):
        pos = positions[i]
        ft_adjust(tree, pos, 1)
        num_seen = ft_sum(tree, 0, pos)
        I[i] = pos - num_seen + 1
    return I

Upvotes: 1

Related Questions