Есть ли способ определить, как отсортирован список?

Есть ли способ определить, как отсортирован список?

Я имею в виду, это не значит знать, отсортирован ли список или нет (логический), но что-то вроде отношения "сортировки", что-то вроде коэффициента корреляции в статистике.

Например,

  • Если элементы списка находятся в порядке возрастания, то его скорость будет 1,0

  • Если список отсортирован по убыванию, его скорость будет равна -1.0

  • Если список почти отсортирован по возрастанию, его скорость будет 0,9 или некоторое значение, близкое к 1.

  • Если список не отсортирован вообще (случайный), его скорость будет близка к 0

Я пишу небольшую библиотеку в Scala для практики. Я думаю, что скорость сортировки была бы полезна, но я не нахожу никакой информации о чем-то подобном. Возможно, я не знаю подходящих терминов для этой концепции.

Ответ 1

Вы можете просто подсчитать количество инверсий в списке.

Инверсия

Инверсия в последовательности элементов типа T представляет собой пару элементов последовательности, которые выглядят не по порядку в соответствии с некоторым порядком < на множестве T.

От Wikipedia:

Формально пусть A(1), A(2), ..., A(n) является последовательностью чисел n.
Если i < j и A(i) > A(j), то пара (i,j) называется инверсией of A.

Число инверсии последовательности является одной общей мерой его сортировки.
Формально число инверсии определяется как количество инверсий, т.е.

definition

Чтобы сделать эти определения более ясными, рассмотрим пример последовательности 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

Традиционной мерой сортировки списка (или другой последовательной структуры) является количество инверсий.

Число инверсий - это количество пар (a, b) st-индекса < b И b << a. Для этих целей << представляет любое отношение упорядочения, которое вы выбираете для своего конкретного вида.

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

Ответ 3

Вы можете использовать фактическую корреляцию.

Предположим, что для каждого элемента в отсортированном списке вы назначаете целочисленный ранг, начиная с нуля. Обратите внимание, что график индекса позиции элементов по сравнению с ранга будет выглядеть как точки в прямой (соотношение 1,0 между позицией и рангом).

Вы можете вычислить корреляцию по этим данным. Для обратного сортировки вы получите -1 и т.д.

Ответ 4

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

  • Вы можете измерить, как отсортирован список, измеряя, насколько он коррелирован с отсортированным списком. Для этого вы можете использовать корреляцию рангов (наиболее известная из них Spearman's), которая точно такая же, как и обычная корреляция, но она использует ранг элементов в списке вместо аналоговых значений его элементов.

  • Существует множество расширений, таких как коэффициент корреляции (+1 для точного сортирования, -1 для точной инверсии)

  • Это позволяет вам иметь статистические свойства для этой меры, такие как перестановочная центральная предельная теорема, которая позволяет вам узнать распределение этой меры для случайных списков.

Ответ 5

Помимо числа инверсии, для числовых списков можно представить среднее квадратное расстояние от отсортированного состояния:

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

Ответ 6

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

Ответ 7

Я бы посчитал сравнения и разделил их на общее количество сравнений. Вот простой пример 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

Ответ 8

Если вы возьмете свой список, вычислите ранги значений в этом списке и вызовите список рангов 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).

Ответ 9

Как насчет чего-то подобного?

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