Efficiently determine the parity of a permutation

北战南征 提交于 2019-12-01 14:38:24

问题


I have an int[] array of length N containing the values 0, 1, 2, .... (N-1), i.e. it represents a permutation of integer indexes.

What's the most efficient way to determine if the permutation has odd or even parity?

(I'm particularly keen to avoid allocating objects for temporary working space if possible....)


回答1:


I think you can do this in O(n) time and O(n) space by simply computing the cycle decomposition.

You can compute the cycle decomposition in O(n) by simply starting with the first element and following the path until you return to the start. This gives you the first cycle. Mark each node as visited as you follow the path.

Then repeat for the next unvisited node until all nodes are marked as visited.

The parity of a cycle of length k is (k-1)%2, so you can simply add up the parities of all the cycles you have discovered to find the parity of the overall permutation.

Saving space

One way of marking the nodes as visited would be to add N to each value in the array when it is visited. You would then be able to do a final tidying O(n) pass to turn all the numbers back to the original values.




回答2:


Consider this approach...

From the permutation, get the inverse permutation, by swapping the rows and sorting according to the top row order. This is O(nlogn)

Then, simulate performing the inverse permutation and count the swaps, for O(n). This should give the parity of the permutation, according to this

An even permutation can be obtained as the composition of an even number and only an even number of exchanges (called transpositions) of two elements, while an odd permutation be obtained by (only) an odd number of transpositions.

from Wikipedia.

Here's some code I had lying around, which performs an inverse permutation, I just modified it a bit to count swaps, you can just remove all mention of a, p contains the inverse permutation.

size_t
permute_inverse (std::vector<int> &a, std::vector<size_t> &p) {
    size_t cnt = 0
    for (size_t i = 0; i < a.size(); ++i) {
        while (i != p[i]) {
            ++cnt;
            std::swap (a[i], a[p[i]]);
            std::swap (p[i], p[p[i]]);
        }
    }
    return cnt;
}



回答3:


You want the parity of the number of inversions. You can do this in O(n * log n) time using merge sort, but either you lose the initial array, or you need extra memory on the order of O(n).

A simple algorithm that uses O(n) extra space and is O(n * log n):

inv = 0
mergesort A into a copy B
for i from 1 to length(A):
    binary search for position j of A[i] in B
    remove B[j] from B
    inv = inv + (j - 1)

That said, I don't think it's possible to do it in sublinear memory. See also:

  • https://cs.stackexchange.com/questions/3200/counting-inversion-pairs
  • https://mathoverflow.net/questions/72669/finding-the-parity-of-a-permutation-in-little-space



回答4:


I selected the answer by Peter de Rivaz as the correct answer as this was the algorithmic approach I ended up using.

However I used a couple of extra optimisations so I thought I would share them:

  • Examine the size of data first
  • If it is greater than 64, use a java.util.BitSet to store the visited elements
  • If it is less than or equal to 64, use a long with bitwise operations to store the visited elements. This makes it O(1) space for many applications that only use small permutations.
  • Actually return the swap count rather than the parity. This gives you the parity if you need it, but is potentially useful for other purposes, and is no more expensive to compute.

Code below:

public int swapCount() {
    if (length()<=64) {
        return swapCountSmall();
    } else {
        return swapCountLong();
    }
}

private int swapCountLong() {
    int n=length();
    int swaps=0;
    BitSet seen=new BitSet(n);
    for (int i=0; i<n; i++) {
        if (seen.get(i)) continue;
        seen.set(i);
        for(int j=data[i]; !seen.get(j); j=data[j]) {
            seen.set(j);
            swaps++;
        }       
    }
    return swaps;
}

private int swapCountSmall() {
    int n=length();
    int swaps=0;
    long seen=0;
    for (int i=0; i<n; i++) {
        long mask=(1L<<i);
        if ((seen&mask)!=0) continue;
        seen|=mask;
        for(int j=data[i]; (seen&(1L<<j))==0; j=data[j]) {
            seen|=(1L<<j);
            swaps++;
        }       
    }
    return swaps;
}


来源:https://stackoverflow.com/questions/20702782/efficiently-determine-the-parity-of-a-permutation

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!