Вычисление числа "инверсий" в перестановке

Пусть A - массив размера N. мы называем пару индексов (i,j) "обратными", если i < j и A[i] > A[j]

Мне нужно найти алгоритм, который получает массив размером N (с уникальными номерами) и возвращает количество инверсий во время O(n*log(n)).

Ответ 1

Вы можете использовать алгоритм merge sort.

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

Предположим массив [leftIndex] > массив [rightIndex]. Это означает, что все элементы в левой части, следующие за элементом с индексом leftIndex, также больше, чем текущий в правой части (потому что левая сторона сортируется по возрастанию). Таким образом, текущий элемент в правой части генерирует инверсии numberOfElementsInTheLeftSide - leftIndex + 1, поэтому добавьте это в свой глобальный счет инверсии.

Как только алгоритм завершит выполнение, вы получите свой ответ, а сортировка слияния - O (n log n) в худшем случае.

Ответ 2

В 2010 году опубликована статья, опубликованная в SIAM в Cham и Patrascu под названием "Подсчет инверсий" , "Одиночный ортогональный диапазон подсчета" и "Связанные проблемы" , который дает алгоритм, принимающий O (n sqrt (log (n))). В настоящее время это самый известный алгоритм и улучшает давний алгоритм O (n log (n)/log (log (n))). Из реферата:

Дадим алгоритм O (n sqrt (lg n)) - времени для подсчета количества инверсий в перестановке на n элементов. Эта улучшает давний предыдущий связанная с O (n lg n/lg lg n), что следует из структуры данных Dietz [WADS'89] и отвечает на вопрос Андерссон и Петерсон [SODA'95]. В виде Известно, что результат Дица является оптимальным для связанной проблемы динамического ранга, наш результат демонстрирует значительную улучшение настройки в автономном режиме.

Наша новая техника довольно проста: мы выполнить "вертикальное разбиение" trie (сродни ван-эдд-боас-деревьям), и использовать идеи из внешней памяти. Тем не менее, техника находит множество приложений: например, получим

  • в d-измерениях, алгоритм ответить на n ортогональный диапазон подсчет запросов во времени O (n lg d-2 + 1/d n);
  • улучшенный время строительства для онлайн-данных структуры для ортогонального диапазона подсчет;
  • улучшенное время обновления для задачи частичных сумм;
  • быстрее Алгоритмы Word RAM для поиска максимальная глубина в расположении выровненных по оси прямоугольников, а для проблема выбора склона.

В качестве бонуса, мы также даем простой (1 + ε) -приближения для подсчет инверсий, который выполняется в линейное время, улучшение предыдущего O (n lg lg n), связанного с Андерссоном и Петерсон.

Ответ 3

Я думаю, что это самый удивительный способ сделать это (и только потому, что мне нравится структура данных) - использовать двоичное индексированное дерево . Имейте в виду, если все, что вам нужно, это решение, слияние будет работать так же хорошо (я просто думаю, что это понятие полностью скалывает!). Основная идея заключается в следующем: Создайте структуру данных, которая обновляет значения в O (log n) и отвечает на запрос "Сколько чисел меньше x уже произошло в массиве до сих пор?" Учитывая это, вы можете легко ответить, сколько из них больше x, что способствует инверсии с x как второе число в паре. Например, рассмотрим список {3, 4, 1, 2}.

При обработке 3 пока нет других чисел, поэтому инверсии с 3 в правой части = 0 При обработке 4 число чисел меньше 4 до сих пор = 1, поэтому число больших чисел (и, следовательно, инверсий) = 0 Теперь при обработке 1 число чисел меньше 1 = 0, это число больше чисел = 2, что способствует двум инверсиям (3,1) и (4,1). Такая же логика применяется к 2, которая находит 1 число меньше, чем оно, и, следовательно, 2 больше, чем оно.

Теперь единственный вопрос - понять, как эти обновления и запросы происходят в журнале n. Упомянутый выше URL является одним из лучших учебных пособий, которые я прочитал по этому вопросу.

Ответ 4

Это оригинальные алгоритмы MERGE и MERGE-SORT из Кормена, Лейзера, Ривеста, Штейна Введение в алгоритмы:

MERGE(A,p,q,r)
 1  n1 = q - p + 1
 2  n2 = r - q
 3  let L[1..n1 + 1] and R[1..n2 + 1] be new arrays
 4  for i = 1 to n1
 5      L[i] = A[p + i - 1]
 6  for j = 1 to n2
 7      R[j] = A[q + j]
 8  L[n1 + 1] = infinity
 9  R[n2 + 1] = infinity
10  i = 1
11  j = 1
12  for k = p to r
13      if L[i] <= R[j] 
14          A[k] = L[i]
15          i = i + 1
16      else A[k] = R[j]
17          j = j + 1

и

MERGE-SORT(A,p,r)
 1 if p < r
 2     q = floor((p + r)/2)
 3     MERGE-SORT(A,p,q)
 4     MERGE-SORT(A,q + 1,r)
 5     MERGE(A,p,q,r)

в строке 8 и 9 в бесконечности MERGE - так называемая сигнальная карта, который имеет такое значение, что все элементы массива меньше, чем это. Чтобы получить число инверсий, можно ввести глобальный счетчик, пусть ninv инициализируется до нуля до вызова MERGE-SORT и чем модифицировать алгоритм MERGE, добавив одну строку в инструкции else после строки 16, что-то вроде

ninv += n1 - i

чем после завершения MERGE-SORT ninv будет удерживать количество инверсий