Re: sorting a list and counting interchanges



[RickMuller]
> I have to sort a list, but in addition to the sorting, I need to
> compute a phase factor that is +1 if there is an even number of
> interchanges in the sort, and -1 if there is an odd number of
> interchanges.

So you want the parity ("sign") of the associated permutation.

> I could write a bubble sort, count the number of interchanges, and
> compute the factor, but I have a feeling that there some
> decorate-sort-undecorate solution buried in this problem somewhere.
> However, I can't see it. Can anyone else help me with this?
>
> I was thinking of something along the lines of zipping the list with a
> range() of the same length, sorting that, and then counting the number
> of times the second list has an item smaller than its previous item. In
> other words
>
> a = [1,10,2,7]
> b = zip(a,range(len(a)))
> b.sort()
> a_sorted = [i for i,j in b]
> order = [j for i,j in b]

Good so far -- keep that much. Pass `order` to the function below.
It computes the sign in linear time.

> phase = 0
> for i in range(len(order)-1):
> if order[i] > order[i+1]: phase += 1

That part isn't correct. For example, it computes phase == 1 for both

[1, 0, 2]
and
[1, 2, 0]

but those obviously have different parities (since they're one
transposition apart).

> phase = 2*(phase%2)-1
>
> However, I can't prove that this works,

See above for why <wink>.

> and there's *got* to be a more elegant way.

Why? There's no efficient way to solve this via counting
transpositions directly, although there is an efficient way by
computing the cycle structure. The following is an elegant way to do
the latter, but "elegance" requires buying into that this is necessary
if you want an efficient solution.

def permsign(p):
"""Return sign of permutation p.

p must be a permutation of the list range(len(p)).
The return value is 1 if p is even, or -1 if p is odd.

>>> for p in ([0,1,2], [0,2,1], [1,0,2],
... [1,2,0], [2,0,1], [2,1,0]):
... print p, permsign(p)
[0, 1, 2] 1
[0, 2, 1] -1
[1, 0, 2] -1
[1, 2, 0] 1
[2, 0, 1] 1
[2, 1, 0] -1
"""

n = len(p)
rangen = range(n)
if sorted(p) != rangen:
raise ValueError("p of wrong form")

# Decompose into disjoint cycles. We only need to
# count the number of cycles to determine the sign.
num_cycles = 0
seen = set()
for i in rangen:
if i in seen:
continue
num_cycles += 1
j = i
while True:
assert j not in seen
seen.add(j)
j = p[j]
if j == i:
break

return (-1)**(n - num_cycles)
.



Relevant Pages

  • Re: Sorting contacts in Windows Mail according to LAST name
    ... You can only sort on one column at a time. ... Try sorting the First Name, ... >I still don't see a sort option for last name of the 4 cycles. ... >>> How do go about sorting contacts in Windows Mail according to LAST ...
    (microsoft.public.windows.vista.mail)
  • Re: A Fast sorting algorithm for almost sorted data
    ... far my compressor has potential but is nowhere near ready. ... It does however make heavy use of sorting. ... which I am currently calling Run sort. ... entire selected run can be added to the sorted output array. ...
    (comp.compression)
  • Re: Solution for sorting an array alpha-numerically
    ... strings up into groups and sorting the groups seperately, ... > so that numeric and alphabetic data sort as seperate groups. ... To the same project as the web page, add the class AlphaNumCompare() ...
    (microsoft.public.dotnet.general)
  • Re: how fast can I sort on mainframe (using DFSORT)?
    ... Since I joined the team as the performance lead a couple years ago, ... Frank now defers these types of questions to me. ... I have been out of the sorting business for a while, ... Writing to sort work files should not be the problem, ...
    (bit.listserv.ibm-main)
  • Re: except tasks from sorting
    ... position out of any sort key. ... But we will sorting subsequently. ... sort key is a Text ... hint to filtering the tasks before ...
    (microsoft.public.project)