Алгоритм оценки монотонности массива (т. е. оценки "сортированности" массива)


редактировать: Вау, много замечательных ответов. Да, я использую это как функцию пригодности для оценки качества сорта, выполняемого генетическим алгоритмом. Таким образом, стоимость оценки важна (т. е. она должна быть быстрой, предпочтительно O(n).)


в рамках приложения AI, с которым я играю, я хотел бы иметь возможность оценивать массив кандидатов целых чисел на основе его монотонности, он же его "сортированность". На данный момент я использую эвристику, которая вычисляет самый длинный отсортированный прогон, а затем делит его на длину массива:

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 ответов


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


Это кажется хорошим кандидатом для Левенштейна Дамерау–Левенштейна distance-количество свопов, необходимых для сортировки массива. Это должно быть пропорционально тому, как далеко каждый элемент находится от того места, где он должен быть в отсортированном массиве.

вот простой алгоритм ruby, который суммирует квадраты расстояний. Это кажется хорошей мерой сортированности-результат становится меньше каждый раз, когда два элемента не в порядке обменивать.

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 total, в противном случае добавьте к unsorted общий. Когда закончите, возьмите соотношение двух.


вычислите длины всех отсортированных подпоследовательностей, затем квадрат их и добавьте их. Если вы хотите откалибровать, сколько enphasis вы ставите на самый большой, используйте мощность, отличную от 2.

Я не уверен, что лучший способ нормализовать это по длине, может быть, разделить его на длину в квадрате?


то, что вы, вероятно, ищете, это Тау Кендалла. Это функция "один к одному" расстояния сортировки пузырьков между двумя массивами. Чтобы проверить, "почти отсортирован" ли массив, вычислите его Kendall Tau против отсортированного массива.


Я бы предложил посмотреть на Проблема Блин и расстояние разворота перестановок. Эти алгоритмы часто используются для поиска расстояния между двумя перестановками (идентификатором и перестановочной строкой). Эта мера расстояния должна учитывать больше сгустков значений порядка, а также развороты (монотонно убывающие вместо возрастающих подпоследовательностей). Есть также приближения, которые являются полиномиальным временем[PDF].

Это на самом деле все зависит от того, что означает число, и если эта функция расстояния имеет смысл в вашем контексте.


у меня такая же проблема (оценка монотонности), и я предлагаю вам попробовать Самая Длинная Возрастающая Подпоследовательность. Наиболее эффективный алгоритм работает в O(n log n) не так уж и плохо.

взяв пример из вопроса, самая длинная возрастающая последовательность {4, 5, 6, 0, 1, 2, 3, 7, 8, 9} is {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

Так что делает то, что ему нужно. Хотя не знаю, как это доказать.


Как насчет подсчета количества шагов с увеличением значения против количества общих шагов. Это O(n).