Clear Filters
Clear Filters

How to sort a complex vector with tie breaker angles between [0, 2π)?

3 views (last 30 days)
By default the sort(A) function sort A by abs(A) when A is complex. If A has elements with equal magnitude, then use angle(A) in the interval (-π,π] to break ties. Therefore, we have the following sorting scheme [2,-1,i,-i] -> [-i,i,-1,2] since the first vector has the angles [0,pi,pi/2,-pi/2] respectively, due to the fact angles are defined in (-pi,pi]. I want to convert the tie break angle range to [0, 2pi), so that angles would be [0,pi,pi/2,3pi/2] so that we would have the sorting scheme of [2,-1,i,-i] -> [i,-1,-i,2].
Things I did:
  • I could not find a way to provide a custom comparator function to the sort() function like it can be done in other languages like Python or C++.
  • I tried the hectic way of redefining angle() function and adding new definition on top the MATLAB path. When I call the angle() function it calls my definition, but I guess since a compiled version of the built-in sort() function is called, new implementation is not used. I may be missing something here.
Of course, I can write my own mergesort or quicksort implementation with custom comparator function, but I keep thinking there must be a better way to do this.

Accepted Answer

Tuna Alikasifoglu
Tuna Alikasifoglu on 2 Nov 2022
Edited: Tuna Alikasifoglu on 2 Nov 2022
Based on the final solution @Jan proposed, I noticed a simple modification that can fix the problem. Since the angle() function produces angles between (-π,π], and since
angle(-1)
ans = 3.1416
which is π. By writing (-a), we basically shift the interval by π, which results in the interval (0,2π]. Hence, the real values end up with 2π angles, which are larger than every other angle. However, we want to have [0,2π) interval (or equivalently (-2π,0] interval). This can be achieved by -π shift, i.e., writing -1 = exp(-j pi). Even though they are mathematically equivalent, we ensure that the angle is -π as:
angle(exp(-1j * pi))
ans = -3.1416
Hence, with this slight modification to the latest proposal of @Jan, we can sort a complex vector with tie breaker angles between [0,2π) as follows:
a = [0, -1j, 1j, 1, -1, 1-1j, 1+1j, -1-1j, -1+1j, -4+3j, 3+4j];
b = [0, 1, 1j, -1, -1j, 1+1j, -1+1j, -1-1j, 1-1j, 3+4j, -4+3j];
[~, idx] = sort(exp(-1j * pi) * a);
sorted = a(idx)
sorted =
0.0000 + 0.0000i 1.0000 + 0.0000i 0.0000 + 1.0000i -1.0000 + 0.0000i 0.0000 - 1.0000i 1.0000 + 1.0000i -1.0000 + 1.0000i -1.0000 - 1.0000i 1.0000 - 1.0000i 3.0000 + 4.0000i -4.0000 + 3.0000i
all(sorted == b)
ans = logical
1
Thank you @Jan and @Voss for your contribution.
  1 Comment
Jan
Jan on 2 Nov 2022
exp(-1j * pi) is mathematically -1, but with the rounding error: -1 + 1.22e-16j . This can have side-effects for sorting large numbers.
Another idea:
aa = [abs(a); angle(a)];
[~, idx] = sortrows(aa.');
Now mod() and adding or subtracting pi might be useful.

Sign in to comment.

More Answers (2)

Voss
Voss on 1 Nov 2022
How about adding pi to the phase (angle) of each number, sorting, then subtracting pi again (i.e., negate, sort, negate)?
z = [2 -1 1i -1i];
sort(z) % original
ans =
0.0000 - 1.0000i 0.0000 + 1.0000i -1.0000 + 0.0000i 2.0000 + 0.0000i
-sort(-z) % new
ans =
0.0000 + 1.0000i -1.0000 + 0.0000i 0.0000 - 1.0000i 2.0000 + 0.0000i
  1 Comment
Tuna Alikasifoglu
Tuna Alikasifoglu on 1 Nov 2022
This is incorrect, since we expect [1, -1, i, -i] -> [1,i,-1,-i], however your code produces [i, -1, -i, 1].
z = [1 -1 1i -1i];
-sort(-z)
ans =
0.0000 + 1.0000i -1.0000 + 0.0000i 0.0000 - 1.0000i 1.0000 + 0.0000i

Sign in to comment.


Jan
Jan on 1 Nov 2022
Edited: Jan on 1 Nov 2022
Maybe it helps to sort the rotated values:
a = [2, -1, i, -i];
[~, idx] = sort(-a * 1i);
a(idx)
ans =
0.0000 + 1.0000i -1.0000 + 0.0000i 0.0000 - 1.0000i 2.0000 + 0.0000i
  3 Comments
Jan
Jan on 2 Nov 2022
"since we expect" - I do not think that I really know, what you expect. But I assume you can understand the idea of modifying the data by rotating them instead of inventing a new sorting function.
Unfortunately you have provided a not exhaustive set of inputs in a format, which cannot be used by copy&paste. This makes it tedious to modify the suggested method until it matchs your expectations. But maybe you can adjust this idea to your needs?
What about:
a = [1-1i, 1+1j, -1-1i, -1+1i];
[~, idx] = sort(-a);
a(idx)
ans =
1.0000 + 1.0000i -1.0000 + 1.0000i -1.0000 - 1.0000i 1.0000 - 1.0000i
Tuna Alikasifoglu
Tuna Alikasifoglu on 2 Nov 2022
Edited: Tuna Alikasifoglu on 2 Nov 2022
Maybe I could not express myself very clear, let me try again. MATLAB sorts complex vectors with following strategy: Sort A by abs(A) when A is real or complex. If A has elements with equal magnitude, then use angle(A) in the interval (-π,π] to break ties. I want to break ties with the interval [0,2π). I provided a simple example to showcase a basic end result. Then, you and @Voss provided solutions that generate the correct output for the given case. However, these solutions do not achieve what I described for all cases, so I provided counter examples such that your snippets fail to generate correct output. Finally, the final solution you provided is correct for the given case but fails again for 0 imaginary part (real) elements. However, this gave me an idea why these may fail, I will post the solution if it works. Meanwhile I provided an exhaustive input a with the expected sorted version of b, as you can see they do not match.
a = [0, -1j, 1j, 1, -1, 1-1j, 1+1j, -1-1j, -1+1j, -4+3j, 3+4j];
b = [0, 1, 1j, -1, -1j, 1+1j, -1+1j, -1-1j, 1-1j, 3+4j, -4+3j];
[~, idx] = sort(-a);
sorted = a(idx)
sorted =
0.0000 + 0.0000i 0.0000 + 1.0000i -1.0000 + 0.0000i 0.0000 - 1.0000i 1.0000 + 0.0000i 1.0000 + 1.0000i -1.0000 + 1.0000i -1.0000 - 1.0000i 1.0000 - 1.0000i 3.0000 + 4.0000i -4.0000 + 3.0000i
all(sorted == b)
ans = logical
0

Sign in to comment.

Products


Release

R2022b

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!