Czy istnieje sposób sprawdzenia, jak posortowana jest lista?

161

Czy istnieje sposób sprawdzenia, jak posortowana jest lista?

Chodzi mi o to, że nie chodzi o to, aby wiedzieć, czy lista jest posortowana, czy nie (boolean), ale coś w rodzaju współczynnika „sortowania”, czegoś w rodzaju współczynnika korelacji w statystyce.

Na przykład,

  • Jeśli pozycje na liście są w porządku rosnącym, to jej kurs wyniesie 1,0

  • Jeśli lista jest posortowana malejąco, jej współczynnik wyniesie -1,0

  • Jeśli lista jest prawie posortowana rosnąco, jej współczynnik wyniesie 0,9 lub jakąś wartość bliską 1.

  • Jeśli lista nie jest w ogóle posortowana (losowa), jej współczynnik byłby bliski 0

Piszę małą bibliotekę w Scali do ćwiczeń. Myślę, że przydałby się współczynnik sortowania, ale nie znajduję żadnych informacji o czymś takim. Może nie znam odpowiednich terminów na to pojęcie.

Josell
źródło
4
Czy zostanie to wykorzystane do określenia idealnego algorytmu do sortowania listy? Np. Dla wartości zbliżonych do 0, QuickSort byłby idealny, ale wartości na każdym końcu skali (prawie posortowane lub prawie odwrotnie posortowane), MergeSort byłoby znacznie szybsze, ponieważ w takich przypadkach QC przechodzi do O (N ^ 2).
Darrel Hoffman
8
+1 dla „stosunku sortowania”
0x499602D2
1
@Fuhrmanator Stochastyczna wersja algorytmu nie musi wykonywać sortowania, aby otrzymać probabilistyczne oszacowanie sortowania. Tylko jeśli chcesz uzyskać dokładną miarę, musisz wykonać sortowanie.
Timothy Shields
1
Sarkastyczny, ale zabawny pierwszy instynkt: Możesz posortować listę i zobaczyć, ile czasu to zajmie, a następnie porównać to z tym, ile czasu zajmuje posortowanie (posortowana) lista i jej odwrotność.
kqr

Odpowiedzi:

142

Możesz po prostu policzyć liczbę inwersji na liście.

Odwrócenie

Odwrócenie w ciągu elementów typu Tto para elementów sekwencji, które pojawiają się nie w porządku według pewnego uporządkowania <na zbiorze elementów T.

Z Wikipedii :

Formalnie niech A(1), A(2), ..., A(n)będzie ciągiem nliczb.
Jeśli i < ji A(i) > A(j), wtedy para (i,j)nazywany jest odwrócenie się A.

Liczba inwersji sekwencji jest jedną z powszechnych miar sortowania.
Formalnie liczba inwersji jest definiowana jako liczba inwersji, to znaczy

definicja

Aby wyjaśnić te definicje, rozważ przykładową sekwencję 9, 5, 7, 6. Ta sekwencja ma inwersje (0,1), (0,2), (0,3), (2,3) i numer inwersji 4 .

Jeśli chcesz uzyskać wartość między 0a 1, możesz podzielić liczbę inwersji przez N choose 2.

Aby faktycznie utworzyć algorytm obliczający ten wynik dla posortowania listy, masz dwa podejścia:

Podejście 1 (deterministyczne)

Zmodyfikuj swój ulubiony algorytm sortowania, aby śledzić, ile inwersji koryguje podczas działania. Chociaż jest to nietrywialne i ma różne implementacje w zależności od wybranego algorytmu sortowania, otrzymasz algorytm, który nie jest droższy (pod względem złożoności) niż algorytm sortowania, od którego zacząłeś.

Jeśli wybierzesz tę trasę, pamiętaj, że nie jest to tak proste, jak liczenie „zamiany”. Na przykład scalanie jest najgorszym przypadkiem O(N log N), ale jeśli zostanie uruchomione na liście posortowanej w porządku malejącym, poprawi wszystkie N choose 2inwersje. To jest O(N^2)odwrócenie poprawione w O(N log N)operacjach. Dlatego niektóre operacje muszą nieuchronnie korygować więcej niż jedną inwersję naraz. Musisz uważać na swoją implementację. Uwaga: możesz to zrobić ze O(N log N)złożonością, to po prostu trudne.

Powiązane: obliczanie liczby „inwersji” w permutacji

Podejście 2 (stochastyczne)

  • Losowe pary próbek (i,j), gdziei != j
  • Dla każdej pary określ, czy list[min(i,j)] < list[max(i,j)](0 czy 1)
  • Oblicz średnią z tych porównań, a następnie znormalizuj według N choose 2

Osobiście wybrałbym podejście stochastyczne, chyba że masz wymóg dokładności - choćby dlatego, że jest tak łatwy do wdrożenia.


Jeśli naprawdę potrzebujesz wartości ( z') między -1(posortowane malejąco) do 1(posortowane rosnąco), możesz po prostu zmapować wartość powyżej ( z), która znajduje się pomiędzy 0(posortowano rosnąco) i 1(posortowano malejąco), na ten zakres przy użyciu tej formuły :

z' = -2 * z + 1
Timothy Shields
źródło
2
To dla mnie trochę fascynujące, że sortowanie listy to (zazwyczaj) O (n * logn), a naiwna / oczywista metoda obliczania inwersji to O (n ^ 2). Zastanawiam się, czy istnieją lepsze algorytmy obliczania liczby inwersji?
Mark Bessey
5
Jest kilka interesujących podejść do tego pytania SO: stackoverflow.com/questions/6523712/ ... Zasadniczo sprowadzają się one do sortowania tablicy w celu ustalenia, ile jest inwersji.
Mark Bessey
4
Naiwnie myślałem, że możesz po prostu policzyć sąsiadujące pary, które są niesprawne. Ale to będzie poważnie zaniżone: 1 2 3 1 2 3 ma tylko jedną sąsiednią inwersję, ale jest ona odwrócona o 50% przez bardziej poprawną miarę.
Barmar
2
@Barmar Myślę, że lista 1 2 3 1 2 3 kwalifikuje się jako sorta sorta ;-)
scunliffe
2
@TimothyShields, cóż, nie, nie jest. Ale nie będę się nad tym rozwodzić. To tylko sugestia, aby dodać nieformalną definicję, która byłaby bardziej przystępna dla mniej skłonnych symbolicznie.
Chris Calo
24

Tradycyjną miarą sortowania listy (lub innej struktury sekwencyjnej) jest liczba inwersji.

Liczba inwersji to liczba par (a, b). Indeks a <b ORAZ b <<a. W tym celu <<reprezentuje dowolną relację porządkową wybraną dla określonego rodzaju.

W pełni posortowana lista nie zawiera inwersji, a całkowicie odwrócona lista ma maksymalną liczbę inwersji.

Marcin
źródło
5
Technicznie 5 4 3 2 1jest w pełni posortowane, ponieważ kolejność nie jest określona, ​​ale jestem pedantyczny :-)
paxdiablo
7
@paxdiablo To zależy od definicji <.
Marcin
@paxdiablo, cóż, posortowanie można mierzyć odległością od liczby inwersji do najbliższej wartości 0 lub n choose 2 .
huon
17

Możesz użyć rzeczywistej korelacji.

Załóżmy, że każdemu elementowi na posortowanej liście przypisujesz liczbę całkowitą, zaczynając od zera. Zwróć uwagę, że wykres wskaźnika pozycji elementów w funkcji rangi będzie wyglądał jak kropki w linii prostej (korelacja 1,0 między pozycją a pozycją).

Możesz obliczyć korelację na tych danych. W przypadku sortowania odwrotnego otrzymasz -1 i tak dalej.

Kaz
źródło
1
Przepraszam, ale to pozostawia zbyt wiele niewyjaśnionych, na przykład sposobu przypisywania liczb całkowitych.
Marcin
2
Aby przypisać liczby całkowite, potrzebujesz posortowanej listy; wtedy jest to tylko wyliczenie elementów.
Kaz,
1
Dokładnie to, co chciałem zasugerować. Określ korelację między pozycją obiektu na oryginalnej liście a jego pozycją na posortowanej liście. Zła wiadomość jest taka, że ​​procedury korelacji prawdopodobnie działają w O (n ^ 2); dobrą wiadomością jest to, że prawdopodobnie są one gotowe do użycia w Twoim środowisku.
Peter Webb
2
Tak, tylko Spearman's rho en.wikipedia.org/wiki/…
Lucas
Jestem ciekawy ... czy to podejście jest równoważne ze skalowaniem liczby inwersji?
Clayton Stanley,
4

Były świetne odpowiedzi i chciałbym dodać matematyczny aspekt dla kompletności:

  • Posortowaną listę można zmierzyć, mierząc, w jakim stopniu jest ona skorelowana z posortowaną listą. Aby to zrobić, możesz użyć korelacji rang (najbardziej znanej jest korelacja Spearmana ), która jest dokładnie taka sama jak zwykła korelacja, ale używa rangi elementów na liście zamiast wartości analogowych jej elementów.

  • Istnieje wiele rozszerzeń, takich jak współczynnik korelacji (+1 dla dokładnego sortowania, -1 dla dokładnej inwersji)

  • Pozwala to mieć właściwości statystyczne dla tej miary, takie jak permutacyjne centralne twierdzenie graniczne, które pozwala poznać rozkład tej miary dla list losowych.

meduz
źródło
3

Oprócz liczby inwersji, w przypadku list numerycznych można sobie wyobrazić średnią kwadratową odległość od posortowanego stanu:

#! ruby
d = -> a { a.zip( a.sort ).map { |u, v| ( u - v ) ** 2 }.reduce( :+ ) ** 0.5 }

a = 8, 7, 3, 4, 10, 9, 6, 2, 5, 1
d.( a ) #=> 15.556
d.( a.sort ) #=> 0.0
d.( a.sort.reverse ) # => 18.166 is the worrst case
Boris Stitnicky
źródło
Myślę, że to kwadrat standardowej funkcji korelacji, patrz en.wikipedia.org/wiki/Correlation_ratio . Dotyczy to w równym stopniu list nienumerycznych; dwie porównywane wartości to pozycja obiektu na dwóch listach.
Peter Webb
Jestem prostakiem. Nie wiem nawet, jaki jest współczynnik korelacji. Kiedy czytam ten artykuł w Wikipedii, na samej górze, jestem proszony o wyjaśnienie, czym jest „rozproszenie statystyczne”, następnie „odchylenie standardowe”, następnie „odchylenie”, a następnie „współczynnik korelacji międzyklasowej”. Dowiedziałem się tego wszystkiego kilka razy, a kilka razy znowu o tym zapomniałem. W tej mojej pragmatycznej odpowiedzi po prostu mierzę odległość między dwoma wektorami za pomocą twierdzenia Pitagorasa, które pamiętam ze szkoły podstawowej, to wszystko.
Boris Stitnicky
1

Nie jestem pewien "najlepszej" metody, ale prostą metodą byłoby porównanie każdego elementu z kolejnym, zwiększając licznik, jeśli element2> element 1 (lub cokolwiek chcesz przetestować), a następnie podzielenie przez całkowitą liczbę elementów. Powinien dać ci procent.

user2369405
źródło
1

Policzyłbym porównania i podzieliłbym je na całkowitą liczbę porównań. Oto prosty przykład w języku Python .

my_list = [1,4,5,6,9,-1,5,3,55,11,12,13,14]

right_comparison_count = 0

for i in range(len(my_list)-1):
    if my_list[i] < my_list[i+1]: # Assume you want to it ascending order
        right_comparison_count += 1

if right_comparison_count == 0:
    result = -1
else:
    result = float(right_comparison_count) / float((len(my_list) - 1))

print result
Ibrahim
źródło
0

A może coś takiego?

#!/usr/bin/python3

def sign(x, y):
   if x < y:
      return 1
   elif x > y:
      return -1
   else:
      return 0

def mean(list_):
   return float(sum(list_)) / float(len(list_))

def main():
   list_ = [ 1, 2, 3, 4, 6, 5, 7, 8 ]
   signs = []
   # this zip is pairing up element 0, 1, then 1, 2, then 2, 3, etc...
   for elem1, elem2 in zip(list_[:-1], list_[1:]):
      signs.append(sign(elem1, elem2))

   # This should print 1 for a sorted list, -1 for a list that is in reverse order
   # and 0 for a run of the same numbers, like all 4's
   print(mean(signs))

main()
dstromberg
źródło
2
Dotyczy to tylko sąsiednich inwersji. Jeśli spojrzysz na inne odpowiedzi, zobaczysz, że to jest niewystarczające.
Konrad Rudolph
1
@KonradRudolph: Myślę, że ta odpowiedź jest odpowiedzią na zadane pytanie. Fakt, że inne odpowiedzi są bardziej wyczerpujące, nie oznacza, że ​​ta jest niewystarczająca; zależy to od wymagań PO.
LarsH,
0

Jeśli weźmiesz swoją listę, obliczysz rangi wartości na tej liście i wywołaj listę rang Yoraz inną listę, Xktóra zawiera liczby całkowite od 1do length(Y), możesz uzyskać dokładnie taką miarę sortowania, której szukasz, obliczając współczynnik korelacji , rmiędzy dwiema listami.

r = \frac{\sum ^n _{i=1}(X_i - \bar{X})(Y_i - \bar{Y})}{\sqrt{\sum ^n _{i=1}(X_i - \bar{X})^2} \sqrt{\sum ^n _{i=1}(Y_i - \bar{Y})^2}} 

W przypadku w pełni posortowanej listy,, w przypadku listy posortowanej r = 1.0odwrotnie r=-1.0, i rwaha się między tymi limitami dla różnych stopni sortowania .

Możliwym problemem związanym z tym podejściem, w zależności od aplikacji, jest to, że obliczenie rangi każdego elementu na liście jest równoważne jego sortowaniu, więc jest to operacja O (n log n).

Szymon
źródło
Ale to nie zignoruje kształtu krzywej. Jeśli jego tablica jest posortowana, ale, powiedzmy, zawiera wartości rosnące wykładniczo, korelacja będzie mała tam, gdzie chce, aby wynosiła 1,0.
Lee Daniel Crocker
@LeeDanielCrocker: Tak, to dobra uwaga. Poprawiłem swoją odpowiedź, aby rozwiązać ten problem, biorąc rangi wartości.
Simon,