Parity of a Permutation

14

1

Background

The parity of a permutation, as defined by wikipedia, is as follows:

The sign or signature of a permutation σ is denoted sgn(σ) and defined as +1 if σ is even and −1 if σ is odd.

The sign of a permutation can be explicitly expressed as

sgn(σ) = (−1)^N(σ)

where N(σ) is the number of inversions in σ.

Alternatively, the sign of a permutation σ can be defined from its decomposition into the product of transpositions as

sgn(σ) = (−1)^m

where m is the number of transpositions in the decomposition.

For those of you who are not fond of Greek alphabet soup in their math, I'll try and simplify the definition a bit with an example (also stolen from wikipedia).

Example

Consider the input array {1, 2, 3, 4, 5}, and a permutation of it, let's say, {3, 4, 5, 2, 1}. In order to get from the original array to its permutation, you must swap indices 0 and 2, 1 and 3, then 2 and 4. Although this is not a unique solution, the parity is well-defined so this works for all cases.

Since it requires 3 swaps, we label this permutation with an odd parity. As you might expect, a permutation that requires an even amount of swaps is said to have an even parity.

Challenge

Your challenge is to write a program in as few bytes as possible to determine the parity of a permutation. Your program or function must:

  • Accept as arguments, two input arrays (or strings) representing a set before and after a permutation.
  • Return or print the character e for even or o for odd, given the permutation.
  • Should assume that all indices in the arrays or strings have unique values.

Test Cases

Assuming you declared a function named f:

f([10], [10]) == "e"
f([10, 30, 20], [30, 20, 10]) == "e"
f([10, 30, 20, 40], [30, 20, 40, 10]) == "o"

This is , the shortest program in bytes wins!

Patrick Roberts

Posted 2016-03-20T02:40:07.123

Reputation: 2 475

4People won't like the strict output format. How about truthy for even and falsy for odd? (or the other way around) – CalculatorFeline – 2016-03-20T02:47:44.653

I was actually hoping to retain the output format I specified unless anyone else really is bothered by it. Edit hold on, I'll compromise. – Patrick Roberts – 2016-03-20T02:49:30.480

@CatsAreFluffy is that better? – Patrick Roberts – 2016-03-20T02:51:50.450

Well, I guess we'll see! – CalculatorFeline – 2016-03-20T02:53:04.810

Good night! Here are some suggestions for when you come back to this (but please do check yourself): [10], [10] -> e (zero transpositions). [10 30 20], [30 20 10] -> e (two transpositions). [10 30 20 40], [30 20 40 10] -> o (three transpositions) – Luis Mendo – 2016-03-20T05:11:22.173

The J language has a builtin C.!.2 for the sign of a permutation, giving a 15 character solution. It's not disallowed in the rules, but it's not really much fun either. – algorithmshark – 2016-03-20T19:28:37.653

@DonMuesli I added the test cases you suggested. – Patrick Roberts – 2016-03-22T08:13:42.910

Answers

5

Jelly, 13 12 bytes

żṗ2</€⁺Sị“oe

Try it online!

How it works

żṗ2</€⁺Sị“oe  Main link. Arguments: A, B (lists)

ż             Zip A with B. Yields an array of pairs [x, σ(x)].
 ṗ2           Generate all pairs [[x, σ(x)], [y, σ(y)]].
   </€        Reduce each pair by </€.
              This maps [[x, σ(x)], [y, σ(y)]] to [x < y, σ(x) < σ(y)].
      ⁺       Repeat the previous link, i.e., execute </€ once more.
              This maps [x < y, σ(x) < σ(y)] to ((x < y) < (σ(x) < σ(y))), which is
              true if and only if x > y and σ(x) < σ(y).
       S      Sum. This counts the number of inversions.
        ị“oe  Retrieve the letter at the corresponding index.
              Indexing is 1-based and modular, so an odd sum retrieves the first
              letter, an even sum the second.

Dennis

Posted 2016-03-20T02:40:07.123

Reputation: 196 637

1That is impressively small. Kudos! – Patrick Roberts – 2016-03-20T15:23:59.923

6

MATL, 17 16 bytes

1 byte removed thanks to a suggestion by Dennis

2$St!<Rz2\'oe'w)

This works in current version (15.0.0) of the language.

Try it online!

Explanation

This uses the definition of parity in terms of inversions. An inversion is a pair of elements in the second array that are in the "wrong" order compared with the first array. Since the first array need not be sorted, we first sort it and the same rearrangement needed for that sorting is applied to the second array. Then an inversion corresponds to a pair of elements that is not increasing in the second array.

Note also that the two input arrays can be swapped and the result is the same. So it's not important which array is considered to be the "original" and which the "permuted".

2$S     % implicitly take two row vectors. Sort second and apply the indices
        % of that sorting to the first
t!      % duplicate. Transpose into column vector
<       % true for elements of the column vector that exceed those of the 
        % row vector. Gives a 2D array with all pairs of comparisons
R       % keep only upper triangular part of that array
z       % number of nonzero elements. This is the number of inversions
2\      % parity of that number: gives 0 or 1
'oe'w   % push string 'eo' below the top of the stack
)       % apply index to produce 'e' or 'o'. An index 1 refers to the first
        % element, whereas 0 refers to the last. Implicitly display 

Luis Mendo

Posted 2016-03-20T02:40:07.123

Reputation: 87 464

1This is a really clever solution! – Alex A. – 2016-03-20T04:46:03.513

@AlexA. Thanks! I've edited the answer to clarify what the pre-ordering part does: we sort one array and then the same rearrangement needed for that sorting is applied to the other array. – Luis Mendo – 2016-03-20T05:00:31.790

1You should add modular indexing to MATL. That would save 3 bytes here. – Dennis – 2016-03-20T15:16:38.770

@Dennis Yes, I've often thought about that... but currently it uses a format where negative values have a different meaning. I chose that in order to have indices of the form x(1:end-2) etc without explicitly indicating the size of x. Not sure if it was a good choice, but I guess it's too late to change now :-) Maybe I'll find a compatible way to add modular indexing – Luis Mendo – 2016-03-20T16:13:42.943

...and indices exceeding current length are used to assign new values. But index 0 does have the meaning of "last entry", so I can save a byte (remove the increment). Thanks for the idea! – Luis Mendo – 2016-03-20T16:22:29.813

As of version 15.1.0, MATL includes modular indexing with scalar indices – Luis Mendo – 2016-03-26T18:02:34.577

As of version 17.0.0, modular indexing can be applied to non-scalar indices too – Luis Mendo – 2016-06-12T20:47:34.790

5

Haskell, 58 bytes

k%l|m<-zip k l=cycle"eo"!!sum[1|(a,b)<-m,(c,d)<-m,a<c,b>d]

Usage:

*Main> [8,3,5]%[5,3,8]
'o'

Same method as my Python answer. proud haskeller saved a byte with cycle.

xnor

Posted 2016-03-20T02:40:07.123

Reputation: 115 687

1You can write cycle"eo"!!... Instead of "eo"!!mod(...)2, saving one byte. – proud haskeller – 2016-03-20T11:16:48.663

5

Octave, 56 52 bytes

It seems nobody is using this approach so far: Basically I'm just using the determinants of the corresponding permutation matrices. The expression det(eye(nnz(a))(a,:)) returns the determinant of the permutation matrix defined by the vector a. Then it is just a matter of extracting the right character from the string, depending on the result.

p=@(v)eye(nnz(v))(v,:);@(a,b)'ole'(det(p(a)*p(b))+2)

flawr

Posted 2016-03-20T02:40:07.123

Reputation: 40 560

2Good idea to use the determinants. Ole! – Luis Mendo – 2016-03-20T16:15:21.983

4

Python 2, 68 bytes

lambda*M:"eo"[sum(a<b<M>A>B for a,A in zip(*M)for b,B in zip(*M))%2]

Usage:

>>> f=lambda*M:"eo"[sum(a<b<M>A>B for a,A in zip(*M)for b,B in zip(*M))%2]
>>> f([8,3,5],[5,3,8])
'o'

Counts the number of inversion pairs of two zipped lists, i,e. values (a,A) and (b,B) from each list at the same index with a<b and A>B. These comparisons are combines as a<b<M>A>B, using the property that the list M is greater than any number. The sum is then taken modulo 2 and turned into e or o.

xnor

Posted 2016-03-20T02:40:07.123

Reputation: 115 687

3

JavaScript (ES6), 73 bytes

(a,b)=>"eo"[r=0,g=a=>a.map((e,i)=>a.slice(i).map(d=>r^=d<e)),g(a),g(b),r]

Since we're only interested in the parity, any duplicate transpositions simply cancel out. Conveniently JavaScript's array subscripts are not multidimensional.

Neil

Posted 2016-03-20T02:40:07.123

Reputation: 95 035

1Interesting place for a comma.. didn't know you could do that. Don't forget about currying for -1 byte – Patrick Roberts – 2016-03-20T12:13:02.103

2

Mathematica, 77 bytes

If[Mod[Plus@@Length/@(Join[{0},#]&)/@PermutationCycles[#][[1]],2]==0,"e","o"]&

I agree!

CalculatorFeline

Posted 2016-03-20T02:40:07.123

Reputation: 2 608

Handy function, unfortunately long name! – Patrick Roberts – 2016-03-20T03:01:30.373

Annoying, right? I hate Cycles. It bumps up the size of PermutationCycles's name, and even PermutationCycles is stupid, returning a Cycles object! ` – CalculatorFeline – 2016-03-20T03:05:35.000

2

Mathematica, 31 bytes

If[Tr[Signature/@{##}]==0,o,e]&

Signature[list] gives the signature of the permutation needed to place the elements of list in canonical order

We can reorder one list to the other, by first reordering one list to any order (in this case the canonical order) and reorder this list to the final list. The sign of the overall permutation is even, iff the signs of the two sub-permutations are equal.

murphy

Posted 2016-03-20T02:40:07.123

Reputation: 635