Алгоритм оценки монотонности массива (т. е. оценки "сортированности" массива)
EDIT : Вау, много отличных ответов. Да, я использую это как функцию пригодности для оценки качества сорта, выполняемого генетическим алгоритмом. Таким образом, оценка стоимости важна (т. е. она должна быть быстрой, предпочтительно O(n)
.)
Как часть приложения ИИ, с которым я играю, я хотел бы иметь возможность оценить потенциальный массив целых чисел на основе его монотонности, или "сортированности". На данный момент я использую эвристику, которая вычисляет самую длинную сортировку выполнить, а затем разделить его на длину массива:
public double monotonicity(int[] array) {
if (array.length == 0) return 1d;
int longestRun = longestSortedRun(array);
return (double) longestRun / (double) array.length;
}
public int longestSortedRun(int[] array) {
if (array.length == 0) return 0;
int longestRun = 1;
int currentRun = 1;
for (int i = 1; i < array.length; i++) {
if (array[i] >= array[i - 1]) {
currentRun++;
} else {
currentRun = 1;
}
if (currentRun > longestRun) longestRun = currentRun;
}
return longestRun;
}
Это хорошее начало, но оно не учитывает возможность того, что могут быть "сгустки" отсортированных под-последовательностей. Например:
{ 4, 5, 6, 0, 1, 2, 3, 7, 8, 9}
Этот массив разбит на три отсортированных под-последовательности. Мой алгоритм оценит его как сортированный только на 40%, но интуитивно он должен получить более высокий балл. Существует ли стандартный алгоритм для такого рода вещей?
11 ответов:
Я ожидаю, что выбор функции для использования очень сильно зависит от того, для чего вы собираетесь ее использовать. Исходя из вашего вопроса, я бы предположил, что вы используете генетическую систему для создания программы сортировки, и это должно быть функцией ранжирования. Если это так, то скорость выполнения имеет решающее значение. Исходя из этого, я уверен, что ваш алгоритм с самой длинной сортировкой подпоследовательностей будет работать довольно хорошо. Это звучит так, как будто это должно определять фитнес довольно хорошо.
Это кажется хорошим кандидатом для
Вот простой алгоритм ruby, который суммирует квадраты расстояний. Это кажется хорошей мерой сортированности-результат становится меньше каждый раз, когда два элемента из порядка меняются местами.ЛевенштейнDamerau-Levenstein distance-количество свопов, необходимых для сортировки массива. Это должно быть пропорционально тому, как далеко каждый элемент находится от того места, где он должен быть в отсортированном массиве.ap = a.sort sum = 0 a.each_index{|i| j = ap.index(a[i])-i sum += (j*j) } dist = sum/(a.size*a.size)
Что-то вроде этого? http://en.wikipedia.org/wiki/Rank_correlation
Вот один, который я только что придумал.
Для каждой пары соседних значений вычислите числовую разницу между ними. Если второе больше или равно первому, добавьте его к сумме
sorted
, в противном случае добавьте к суммеunsorted
. Когда закончите, возьмите соотношение двух.
Вычислите длину всех отсортированных под-последовательностей, затем возведите их в квадрат и добавьте. Если вы хотите откалибровать, сколько enphasis вы ставите на наибольший, используйте мощность, отличную от 2.
Я не уверен, что лучший способ нормализовать это по длине, может быть, разделить его на длину в квадрате?
То, что вы, вероятно, ищете, - это Кендалл Тау. Это взаимно однозначная функция расстояния сортировки пузырьков между двумя массивами. Чтобы проверить, является ли массив "почти отсортированным", вычислите его Kendall Tau против отсортированного массива.
Я бы предложил посмотреть на Блиновую Задачу и расстояние обращения перестановок. Эти алгоритмы часто используются для определения расстояния между двумя перестановками (тождеством и перестановочной строкой). Эта мера расстояния должна учитывать больше сгустков значений порядка, а также развороты (монотонно убывающие вместо возрастающих подпоследовательностей). Существуют также приближения , которые являются полиномиальными по времени[PDF].
На самом деле все зависит от что означает число и имеет ли эта функция расстояния смысл в вашем контексте.
У меня та же проблема (оценка монотонности), и я предлагаю вам попробовать самую длинную возрастающую подпоследовательность. Самый эффективный алгоритм, запущенный в
O(n log n)
, не так уж плох.Беря пример из вопроса, самая длинная возрастающая последовательность
{4, 5, 6, 0, 1, 2, 3, 7, 8, 9}
равна{0, 1, 2, 3, 7, 8, 9}
(Длина 7). Может быть, это лучше (70%), чем ваш алгоритм с самой длинной сортировкой.
Это сильно зависит от того, для чего вы собираетесь использовать меру, но один простой способ сделать это-ввести массив в стандартный алгоритм сортировки и измерить, сколько операций (свопов и/или сравнений) необходимо выполнить для сортировки массива.
Некоторые эксперименты с модификатором Ratcliff & Obershelp
>>> from difflib import SequenceMatcher as sm >>> a = [ 4, 5, 6, 0, 1, 2, 3, 7, 8, 9 ] >>> c = [ 0, 1, 9, 2, 8, 3, 6, 4, 7, 5 ] >>> b = [ 4, 5, 6, 0, 1, 2, 3, 7, 8, 9 ] >>> b.sort() >>> s = sm(None, a, b) >>> s.ratio() 0.69999999999999996 >>> s2 = sm(None, c, b) >>> s2.ratio() 0.29999999999999999
Таким образом, он делает то, что ему нужно. Хотя не очень уверен, как это доказать.