Есть ли способ определить, как отсортирован список?
Есть ли способ определить, как отсортирован список?
Я имею в виду, это не значит знать, отсортирован ли список или нет (логический), но что-то вроде отношения "сортировки", что-то вроде коэффициента корреляции в статистике.
Например,
-
Если элементы списка находятся в порядке возрастания, то его скорость будет 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()