Чи є спосіб оцінити, наскільки сортований список?


161

Чи є спосіб оцінити, наскільки сортований список?

Я маю на увазі, що мова йде не про те, чи списку відсортовано чи ні (булева), а щось на зразок співвідношення "своєрідності", щось на зразок коефіцієнта кореляції у статистиці.

Наприклад,

  • Якщо елементи списку є у порядку зростання, то його показник складе 1,0

  • Якщо список буде відсортований у порядку зменшення, його швидкість складе -1,0

  • Якщо список майже відсортований за зростанням, його швидкість складе 0,9 або деяке значення, близьке до 1.

  • Якщо список взагалі не відсортований (випадковий), його швидкість буде близькою до 0

Я пишу невелику бібліотеку в Скалі для практики. Я думаю, що коефіцієнт сортування буде корисним, але я не знаходжу інформації про щось подібне. Можливо, я не знаю адекватних термінів для концепції.



4
Чи буде це використано для визначення ідеального алгоритму для сортування списку? Наприклад, для значень, близьких до 0, QuickSort був би ідеальним, але значення на будь-якому кінці шкали (майже відсортовані або майже відсортовані), MergeSort буде набагато швидшим, оскільки QC переходить до O (N ^ 2) у цих випадках.
Даррель Гофман

8
+1 за "співвідношення сортів"
0x499602D2

1
@Fuhrmanator Стохастична версія алгоритму не повинна виконувати сортування, щоб дійти до ймовірнісної оцінки сортування. Тільки якщо ви хочете отримати точний захід, вам потрібно виконати сортування.
Тімоті Шилдс

1
Саркастичний, але кумедний перший інстинкт: Ви можете вставити сортування списку і побачити, скільки часу займає, а потім порівняти його з тим, скільки часу потрібно для сортування (тепер відсортованого) списку та реверсу його.
kqr

Відповіді:


142

Ви можете просто порахувати кількість перетворень у списку.

Інверсія

Інверсія в послідовність елементів типу T- це пара елементів послідовності, які виходять з ладу згідно з деяким упорядкуванням <на множині Ts.

З Вікіпедії :

Формально, нехай це A(1), A(2), ..., A(n)буде послідовність nчисел.
Якщо i < jі A(i) > A(j), то пара (i,j)називається інверсією з A.

Інверсії номер послідовності одна загальна міра його або порівняння .
Формально число інверсії визначається як кількість інверсій, тобто

визначення

Щоб зробити ці визначення більш зрозумілими, розглянемо приклад послідовності 9, 5, 7, 6. Ця послідовність має інверсії (0,1), (0,2), (0,3), (2,3) та номер інверсії 4 .

Якщо потрібно значення між 0і 1, ви можете розділити число інверсії на N choose 2.

Для того, щоб створити алгоритм для обчислення цього бала для сортування списку, у вас є два підходи:

Підхід 1 (детермінований)

Змініть свій улюблений алгоритм сортування, щоб відстежувати, скільки інверсій він виправляє під час його запуску. Хоча це нетривіально і має різні реалізації в залежності від вибраного алгоритму сортування, ви отримаєте алгоритм, який не є дорожчим (за складністю), ніж алгоритм сортування, з якого ви почали.

Якщо ви їдете цим маршрутом, пам’ятайте, що це не так просто, як підрахунок «свопів». Наприклад, Mergesort - це найгірший випадок O(N log N), але якщо він ведеться за списком, відсортованим у порядку зменшення, він виправить усі N choose 2інверсії. Це O(N^2)виправлення, виправлені в O(N log N)операціях. Тому деякі операції неминуче повинні виправляти більше однієї інверсії одночасно. Ви повинні бути обережними у своїй реалізації. Зауважте: ви можете зробити це O(N log N)складно, це просто хитро.

Пов'язане: обчислення кількості "інверсій" у перестановці

Підхід 2 (стохастичний)

  • Випадково вибіркові пари (i,j), деi != j
  • Для кожної пари визначте, чи list[min(i,j)] < list[max(i,j)](0 або 1)
  • Обчисліть середнє значення цих порівнянь, а потім нормалізуйте N choose 2

Я особисто пішов би зі стохастичним підходом, якщо у вас немає вимоги точності - хоча б тому, що це так просто здійснити.


Якщо ви дійсно хочете - це значення ( z') між -1(відсортовано за спаданням) до 1(відсортовано за зростанням), ви можете просто зіставити значення вище ( z), яке знаходиться між 0(сортування за зростанням) та 1(відсортоване за спаданням), до цього діапазону, використовуючи цю формулу :

z' = -2 * z + 1

2
Мені начебто захоплює те, що сортування списку є (як правило) O (n * logn), а наївним / очевидним методом обчислення обертів є O (n ^ 2). Цікаво, чи є кращі алгоритми для обчислення кількості інверсій?
Марк Бессі

5
Є кілька цікавих підходів у цьому запитанні ТА : stackoverflow.com/questions/6523712/… В основному вони складають для сортування масиву, щоб зрозуміти, скільки інверсій існує.
Марк Бессі

4
Я наївно думав, що ви можете просто порахувати сусідні пари, які вийшли з ладу. Але це буде суттєво недооцінене: 1 2 3 1 2 3 має лише одну сусідню інверсію, але це на 50% перевернуто при більш правильній мірі.
Бармар

2
@Barmar Я думаю, що список 1 2 3 1 2 3 буде кваліфікований як сортований ;-)
scunliffe

2
@TimothyShields, ну, ні, це не так. Але я не буду терпіти справу. Просто пропозиція додати неформальне визначення, яке є більш доступним для менш символічно схильних.
Кріс Кало

24

Традиційним показником сортування списку (або іншої послідовної структури) є кількість інверсій.

Кількість інверсій - це кількість пар (a, b) st індексу a <b AND b <<a. Для цих цілей <<представляє будь-яке замовлення, яке ви обрали для вашого конкретного сорту.

Повністю відсортований список не має інверсій, а повністю перевернутий список має максимальну кількість інверсій.


5
Технічно 5 4 3 2 1повністю відсортовано, оскільки порядок не визначений, але я буду педантичним :-)
paxdiablo

7
@paxdiablo Це залежить від визначення <.
Марцін

@paxdiablo, добре можна було виміряти сортування за відстанню від кількості інверсій до найближчих 0 або n choose 2.
хун

17

Ви можете використовувати фактичну кореляцію.

Припустимо, що кожному елементу у відсортованому списку ви присвоюєте ціле число, починаючи з нуля. Зауважте, що графік індексу позиції елементів проти ранжу буде виглядати як крапки у прямій формі (співвідношення 1,0 між позицією та рангом).

Ви можете обчислити кореляцію цих даних. Для зворотного сортування ви отримаєте -1 тощо.


1
Вибачте, але це залишає занадто незрозуміле, як, наприклад, як призначити цілі числа.
Марцін

2
Вам потрібно відсортований список для призначення цілих чисел; то це лише перерахування предметів.
Каз

1
Саме те, що я збирався запропонувати. Визначте співвідношення між положенням об’єкта у вихідному списку та його положенням у відсортованому списку. Погана новина полягає в тому, що кореляційні процедури, ймовірно, працюють в O (n ^ 2); гарна новина - це те, що вони, ймовірно, позаштатні для вашого оточення.
Пітер Вебб

2
Так, просто rho en.wikipedia.org/wiki/…
Lucas

Мені цікаво ... чи такий підхід еквівалентний масштабуванню підрахунку кількості інверсій?
Клейтон Стенлі

4

Було чудових відповідей, і я хотів би додати математичний аспект для повноти:

  • Ви можете виміряти, наскільки сортований список, вимірявши, скільки він співвідноситься з відсортованим списком. Для цього ви можете використовувати кореляцію рангів (найвідоміший - Спірман ), яка точно така ж, як і звичайну кореляцію, але вона використовує ранг елементів у списку замість аналогічних значень елементів.

  • Існує багато розширень, як коефіцієнт кореляції (+1 для точного сортування, -1 для точної інверсії)

  • Це дозволяє мати статистичні властивості для цієї міри, як теоретична центральна гранична теорема, яка дозволяє знати розподіл цієї міри за випадковими списками.


3

Окрім підрахунку інверсії, для числових списків можна уявити середнє квадратне відстань від відсортованого стану:

#! 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

Я думаю, що це квадрат стандартної функції кореляції, див. En.wikipedia.org/wiki/Correlation_ratio . І стосується однаковою мірою до нечислових списків; два значення, які порівнюються, є позицією об'єкта у двох списках.
Пітер Вебб

Я простак. Я навіть не знаю, що таке коефіцієнт кореляції. Коли я читаю цю статтю у Вікіпедії, прямо вгорі, мене просять дізнатися, що таке "статистична дисперсія", потім "стандартне відхилення", потім "варіація", потім "коефіцієнт міжкласової кореляції". Я все це дізнався, кілька разів і кілька разів, знову забув. У цій моїй прагматичній відповіді я просто вимірюю відстань між двома векторами теоремою Піфагора, яку я пам’ятаю від початкової школи, ось і все.
Борис Стітнікі

1

Я не впевнений у "найкращому" методі, але простим було б порівняти кожен елемент з тим, який після нього, збільшивши лічильник, якщо element2> елемент 1 (або все, що ви хочете перевірити), а потім розділити на загальну кількість елементів. Це має дати вам відсоток.


1

Я б порахував порівняння і поділив його на загальну кількість порівнянь. Ось простий приклад 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

0

Як щодо чогось подібного?

#!/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()

2
Це враховує лише сусідні інверсії. Якщо ви подивитесь на інші відповіді, то побачите, що цього недостатньо.
Конрад Рудольф

1
@KonradRudolph: Я думаю, що ця відповідь задовольняє запитання, яке я задав. Те, що інші відповіді є більш вичерпними, не означає, що ця відповідь недостатня; це залежить від вимог ОП.
LarsH

0

Якщо ви берете свій список, обчислюєте ранги значень у цьому списку і називаєте список рангів Yта інший список, Xякий містить цілі числа від 1до length(Y), ви можете отримати саме той показник сортування, який ви шукаєте, обчисливши коефіцієнт кореляції , rміж двома списками.

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}} 

Для повністю відсортованого списку, r = 1.0для списку, відсортованого на зворотному рівні r=-1.0, та rрізниці між цими межами для різного ступеня сортування.

Можлива проблема цього підходу, залежно від програми, полягає в тому, що обчислення рангу кожного елемента в списку еквівалентно його сортування, тому це операція O (n log n).


Але це не буде ігнорувати форму кривої. Якщо його масив буде відсортований, але, скажімо, містить значення, що збільшуються експоненціально, кореляція буде невеликою там, де він хоче, щоб він був 1,0.
Лі Даніел Крокер

@LeeDanielCrocker: Так, це хороший момент. Я змінив свою відповідь, щоб вирішити цю проблему, прийнявши ранги значень.
Саймон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.