Terse Method to Swap Lowest for Highest?

I have built a solution to swap the lowest values with the highest values in a list.

With

SeedRandom[987]
test = RandomSample@*Join @@ Range @@@ {{6, 10}, {56, 60}, {1, 5}, {-5, -1}}

{-1, 2, 7, 8, 60, 57, 58, 10, 9, 4, -5, -3, 3, 59, 1, 5, -4, 6, -2, 56}


Then

swapPositions =
PermutationReplace[
Ordering@Ordering@test,
With[{len = Length@test},
Cycles@
Transpose@{Range @@ {1, Floor[len/2]}, Reverse@*Range @@ {Ceiling[len/2] + 1, len}}
]
];

Sort[test][[swapPositions]]

{56, 9, 4, 3, -5, -2, -3, 1, 2, 7, 60, 58, 8, -4, 10, 6, 59, 5, 57, -1}


The largest half of the numbers have had their positions swapped with lowest half of the numbers.

However, it feels too verbose and I think Sort might be expensive in this case. Is there a built-in function or more terse method to achieve this. Of course with no loss in speed. The actual case is for list of length 100000 and more.

2 Answers

How about:

Module[{tmp = test},
With[{ord=Ordering[tmp]},
tmp[[ord]] = Reverse @ tmp[[ord]]];
tmp
]


{56, 9, 4, 3, -5, -2, -3, 1, 2, 7, 60, 58, 8, -4, 10, 6, 59, 5, 57, -1}

• That is so obvious I want to cry. Thanks (+1). – Edmund Mar 22 at 21:15

This is equivalent to Carl's procedure, except that it uses one less scratch list:

With[{ord = Ordering[test]},
test[[PermutationProduct[Reverse[ord], InversePermutation[ord]]]]]
{56, 9, 4, 3, -5, -2, -3, 1, 2, 7, 60, 58, 8, -4, 10, 6, 59, 5, 57, -1}


Recall that list[[perm]] = list is equivalent to list = list[[InversePermutation[perm]]], where perm is a permutation list. (The situation is equivalent to list.pmat being the same as Transpose[pmat].list if pmat is a permutation matrix.) You can then use PermutationProduct[] to compose successive permutations.

(This was supposed to be a comment, but it got too long.)

• This solution doesn't copy the list so may be faster than Carl's. (+1). – Edmund Mar 23 at 3:38
• FWIW, I consistently get {56, -2, 6, -4, 5, 1, 59, 3, -3, -5, 4, 9, 10, 58, 57, 60, 8, 7, 2, -1} from this. – Christopher Lamb Mar 23 at 16:01
• @Rabbit, what version number of Mathematica is giving that result? – J. M. is away Mar 23 at 16:10
• 11.3.0.0 (5944644, 2018030701) Win 10. I did a trace, which might have had the needed info but I didn't catch it. Started w/ fresh kernel, & repeated, w/ same result. Baffled. – Christopher Lamb Mar 23 at 16:16
• At least on my machine, the questioner's original (very verbose) proposed solution is faster than any other proposal. In order of presentation: 0.0000114366 v. 0.0000357762 v. 0.0000164219 (AbsoluteTiming in seconds). – CElliott Apr 3 at 14:10