Таблица инверсий — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
м (убран wikitex)
Строка 1: Строка 1:
<wikitex>
+
Пусть <tex> P = (p_1,p_2,\dots,p_n)</tex> является [[Действие перестановки на набор из элементов, представление в виде циклов|перестановкой]] чисел <tex> 1, 2,\dots, n</tex>.
Пусть $ P = (p_1,p_2,\dots,p_n)$ является [[Действие перестановки на набор из элементов, представление в виде циклов|перестановкой]] чисел $ 1, 2,\dots, n$.
 
  
 
{{Определение
 
{{Определение
 
|definition =  
 
|definition =  
'''Инверсией''' в перестановке $P$ называется всякая пара индексов $i, j$ такая, что $1\leqslant i<j\leqslant n$ и $P[i]>P[j]$.
+
'''Инверсией''' в перестановке <tex>P</tex> называется всякая пара индексов <tex>i, j</tex> такая, что <tex>1\leqslant i<j\leqslant n</tex> и <tex>P[i]>P[j]</tex>.
 
}}
 
}}
  
 
{{Определение
 
{{Определение
 
|definition =  
 
|definition =  
'''Таблицей инверсий''' перестановки $ P $ называют такую последовательность $ T = (t_1,t_2,\dots,t_n)$, в которой $t_i$ равно числу элементов перестановки $ P $, стоящих в $ P $ левее числа $i$ и больших $i$.
+
'''Таблицей инверсий''' перестановки <tex> P </tex> называют такую последовательность <tex> T = (t_1,t_2,\dots,t_n)</tex>, в которой <tex>t_i</tex> равно числу элементов перестановки <tex> P </tex>, стоящих в <tex> P </tex> левее числа <tex>i</tex> и больших <tex>i</tex>.
 
}}
 
}}
  
Строка 21: Строка 20:
 
     if P[j] > P[i]
 
     if P[j] > P[i]
 
       T[P[i]] = T[P[i]] + 1
 
       T[P[i]] = T[P[i]] + 1
Сложность данного алгоритма {{---}} $O(n^2)$. Уменьшить время работы можно используя [[Дерево отрезков. Построение|дерево отрезков]].
+
Сложность данного алгоритма {{---}} <tex>O(n^2)</tex>. Уменьшить время работы можно используя [[Дерево отрезков. Построение|дерево отрезков]].
  
Получим из исходной перестановки обратную и запишем её в массив $M$. Также заведём массив $S$ длиной $n$, инициализированный нулями. После обработки $i$-го элемента будем вносить значение 1 в ячейку $S[M[i]]$. Обработку начинаем с последнего элемента и двигаемся к началу. Пусть, функция $Sum(i)$ возвращает значение суммы элементов массива $S$ от 1 до $i$. Тогда $i$-й элемент таблицы инверсий находится так:
+
Получим из исходной перестановки обратную и запишем её в массив <tex>M</tex>. Также заведём массив <tex>S</tex> длиной <tex>n</tex>, инициализированный нулями. После обработки <tex>i</tex>-го элемента будем вносить значение 1 в ячейку <tex>S[M[i]]</tex>. Обработку начинаем с последнего элемента и двигаемся к началу. Пусть, функция <tex>Sum(i)</tex> возвращает значение суммы элементов массива <tex>S</tex> от 1 до <tex>i</tex>. Тогда <tex>i</tex>-й элемент таблицы инверсий находится так:
  $T[i] = Sum(M[i])$
+
  <tex>T[i] = Sum(M[i])</tex>
  
Данная формула даёт правильный ответ, так как $S[M[i]]$ содержит единицу только в том случае, если мы уже обработали элемент, стоящий в $M[i]$-й позиции. Так как обработка идёт от больших чисел к меньшим, $Sum(M[i])$ выдаст нужное нам количество чисел, больших $i$ и стоящих в перестановке левее него.
+
Данная формула даёт правильный ответ, так как <tex>S[M[i]]</tex> содержит единицу только в том случае, если мы уже обработали элемент, стоящий в <tex>M[i]</tex>-й позиции. Так как обработка идёт от больших чисел к меньшим, <tex>Sum(M[i])</tex> выдаст нужное нам количество чисел, больших <tex>i</tex> и стоящих в перестановке левее него.
  
Функция $Sum$ реализуется с помощью [[Дерево отрезков. Построение|дерева отрезков]]. Каждое изменение массива и обращение к функции $Sum$ влечёт за собой $\log_2n$ операций. Таким образом получаем сложность алгоритма $O(n\log_2n)$
+
Функция <tex>Sum</tex> реализуется с помощью [[Дерево отрезков. Построение|дерева отрезков]]. Каждое изменение массива и обращение к функции <tex>Sum</tex> влечёт за собой <tex>\log_2n</tex> операций. Таким образом получаем сложность алгоритма <tex>O(n\log_2n)</tex>
  
Также существует достаточно простой алгоритм построения таблицы инверсий с использованием [[Сортировка слиянием|сортировки слиянием]]. Вначале выписываются следующие упорядоченные пары чисел: в $i$-ой паре на первом месте стоит $i$-й элемент перестановки, а на втором {{---}} 0. Далее применяется [[Сортировка слиянием|сортировка слиянием]], при чем при выписывании элемента из второй цепочки упорядоченных пар, число на второй позиции в данном элементе увеличивается на количество элементов, оставшихся в первой цепочке. В конце сортировки получим цепочку упорядоченных пар. Выписывая вторые элементы данных упорядоченных пар, получим искомую таблицу инверсий. Сложность данного алгоритма совпадает со сложностью [[Сортировка слиянием|сортировки слиянием]] и составляет $O(n\log_2n)$.
+
Также существует достаточно простой алгоритм построения таблицы инверсий с использованием [[Сортировка слиянием|сортировки слиянием]]. Вначале выписываются следующие упорядоченные пары чисел: в <tex>i</tex>-ой паре на первом месте стоит <tex>i</tex>-й элемент перестановки, а на втором {{---}} 0. Далее применяется [[Сортировка слиянием|сортировка слиянием]], при чем при выписывании элемента из второй цепочки упорядоченных пар, число на второй позиции в данном элементе увеличивается на количество элементов, оставшихся в первой цепочке. В конце сортировки получим цепочку упорядоченных пар. Выписывая вторые элементы данных упорядоченных пар, получим искомую таблицу инверсий. Сложность данного алгоритма совпадает со сложностью [[Сортировка слиянием|сортировки слиянием]] и составляет <tex>O(n\log_2n)</tex>.
  
 
= Алгоритм восстановления =
 
= Алгоритм восстановления =
  
Для восстановления таблицы перестановки из таблицы инверсий создаем таблицу, которую будем расширять, по мере добавления в неё чисел. Добавляем в эту таблицу число $i$ (где $i$ от $n$ до 1) на позицию $k+1$, где $k$ - число в таблице инверсий на $i$-том месте. Данный алгоритм довольно прост в реализации, но без использования дополнительных структур данных, имеет сложность $O(n^2)$, т. к. для вставки элемента в определённую позицию, требуется порядка $n$ перестановок элементов.
+
Для восстановления таблицы перестановки из таблицы инверсий создаем таблицу, которую будем расширять, по мере добавления в неё чисел. Добавляем в эту таблицу число <tex>i</tex> (где <tex>i</tex> от <tex>n</tex> до 1) на позицию <tex>k+1</tex>, где <tex>k</tex> - число в таблице инверсий на <tex>i</tex>-том месте. Данный алгоритм довольно прост в реализации, но без использования дополнительных структур данных, имеет сложность <tex>O(n^2)</tex>, т. к. для вставки элемента в определённую позицию, требуется порядка <tex>n</tex> перестановок элементов.
  
Приведём алгоритм восстановления с использованием [[Сортировка слиянием|сортировки слиянием]], имеющий сложность $O(n\log_2n)$.
+
Приведём алгоритм восстановления с использованием [[Сортировка слиянием|сортировки слиянием]], имеющий сложность <tex>O(n\log_2n)</tex>.
  
Пусть $\alpha$ и $\beta$ - цепочки упорядоченных пар целых неотрицательных чисел $[m_1, n_1]\dots[m_k, n_k]$. Рассмотрим двоичную операцию $\circ$, рекурсивно определенную на парах таких цепочек следующим образом:
+
Пусть <tex>\alpha</tex> и <tex>\beta</tex> - цепочки упорядоченных пар целых неотрицательных чисел <tex>[m_1, n_1]\dots[m_k, n_k]</tex>. Рассмотрим двоичную операцию <tex>\circ</tex>, рекурсивно определенную на парах таких цепочек следующим образом:
 
  $([m, n]\alpha)\circ([m', n']\beta)=\left\{\begin{aligned}[]
 
  $([m, n]\alpha)\circ([m', n']\beta)=\left\{\begin{aligned}[]
 
  [m,n](\alpha \circ ([m'-m, n']\beta)), m \le m',\\
 
  [m,n](\alpha \circ ([m'-m, n']\beta)), m \le m',\\
Строка 45: Строка 44:
 
  \right.$
 
  \right.$
  
Сопоставим каждому элементу таблицы инверсий его номер. Получится множество упорядоченных пар чисел $[m_1, n_1]\dots[m_k, n_k]$, где $m_i$ {{---}} сам элемент, а $n_i$ {{---}} его номер. Разобьём данные элементы на пары и произведём с ними операцию $\circ$. Получим некоторое количество цепочек упорядоченных пар. Также разбиваем их на пары и производим операцию $\circ$. Так действуем, пока не останется одна цепочка. Выписывая вторые элементы данных упорядоченных пар в том порядке, в каком они представлены в цепочке, получим первоначальную перестановку.
+
Сопоставим каждому элементу таблицы инверсий его номер. Получится множество упорядоченных пар чисел <tex>[m_1, n_1]\dots[m_k, n_k]</tex>, где <tex>m_i</tex> {{---}} сам элемент, а <tex>n_i</tex> {{---}} его номер. Разобьём данные элементы на пары и произведём с ними операцию <tex>\circ</tex>. Получим некоторое количество цепочек упорядоченных пар. Также разбиваем их на пары и производим операцию <tex>\circ</tex>. Так действуем, пока не останется одна цепочка. Выписывая вторые элементы данных упорядоченных пар в том порядке, в каком они представлены в цепочке, получим первоначальную перестановку.
  
Цепочка наподобие $[4, 4][1,3]$ представляет "_ _ _ _ 4 _ 3 _ $\infty$", где "_" означает пропуск. Операция $\alpha \circ \beta$ вставляет пропуски и заполнения из $\beta$ на место пропусков в $\alpha$.
+
Цепочка наподобие <tex>[4, 4][1,3]</tex> представляет "_ _ _ _ 4 _ 3 _ <tex>\infty</tex>", где "_" означает пропуск. Операция <tex>\alpha \circ \beta</tex> вставляет пропуски и заполнения из <tex>\beta</tex> на место пропусков в <tex>\alpha</tex>.
  
 
== Пример ==
 
== Пример ==
  
* $[4, 1, 6, 3, 2, 2, 1, 1, 1, 0]$ - таблица инверсий.
+
* <tex>[4, 1, 6, 3, 2, 2, 1, 1, 1, 0]</tex> - таблица инверсий.
 +
* <tex>[4,1]\circ[1,2], [6,3]\circ[3,4], [2,5]\circ[2,6], [1,7]\circ[1,8], [1,9]\circ[0,10]</tex>
 +
* <tex>[1,2][2,1]\circ[3,4][2,3], [2,5][0,6]\circ[1,7][0,8], [0,10][0,9]</tex>
 +
* <tex>[1,2][2,1][0,4][2,3]\circ[1,7][0,5][0,6][0,8], [0,10][0,9]</tex>
 +
* <tex>[1,2][0,7][0,5][0,1][0,4][0,6][0,8][0,3]\circ[0,10][0,9]</tex>
 +
* <tex>[0,10][0,2][0,7][0,5][0,1][0,4][0,6][0,8][0,3][0,9]</tex>
  
 
+
Получаем перестановку <tex>[10, 2, 7, 5, 1, 4, 6, 8, 3, 9]</tex>
* $[4,1]\circ[1,2], [6,3]\circ[3,4], [2,5]\circ[2,6], [1,7]\circ[1,8], [1,9]\circ[0,10]$
 
 
 
 
 
* $[1,2][2,1]\circ[3,4][2,3], [2,5][0,6]\circ[1,7][0,8], [0,10][0,9]$
 
 
 
 
 
* $[1,2][2,1][0,4][2,3]\circ[1,7][0,5][0,6][0,8], [0,10][0,9]$
 
 
 
 
 
* $[1,2][0,7][0,5][0,1][0,4][0,6][0,8][0,3]\circ[0,10][0,9]$
 
 
 
 
 
* $[0,10][0,2][0,7][0,5][0,1][0,4][0,6][0,8][0,3][0,9]$
 
 
 
 
 
Получаем перестановку $[10, 2, 7, 5, 1, 4, 6, 8, 3, 9]$
 
  
 
= Источники =
 
= Источники =
  
 
* Д. Кнут - Искусство программирования, том 3.
 
* Д. Кнут - Искусство программирования, том 3.
</wikitex>
 

Версия 13:36, 12 января 2012

Пусть [math] P = (p_1,p_2,\dots,p_n)[/math] является перестановкой чисел [math] 1, 2,\dots, n[/math].


Определение:
Инверсией в перестановке [math]P[/math] называется всякая пара индексов [math]i, j[/math] такая, что [math]1\leqslant i\lt j\leqslant n[/math] и [math]P[i]\gt P[j][/math].


Определение:
Таблицей инверсий перестановки [math] P [/math] называют такую последовательность [math] T = (t_1,t_2,\dots,t_n)[/math], в которой [math]t_i[/math] равно числу элементов перестановки [math] P [/math], стоящих в [math] P [/math] левее числа [math]i[/math] и больших [math]i[/math].


Алгоритм построения

Таблицу инверсий тривиально построить по определению. Для каждого элемента перестановки считаем количество элементов, больших данного и стоящих в перестановке левее него. Алгоритм построения в псевдокоде выглядит так:

T[1..n] = 0
For i = 1..n
  For j = 1..(i - 1)
    if P[j] > P[i]
      T[P[i]] = T[P[i]] + 1

Сложность данного алгоритма — [math]O(n^2)[/math]. Уменьшить время работы можно используя дерево отрезков.

Получим из исходной перестановки обратную и запишем её в массив [math]M[/math]. Также заведём массив [math]S[/math] длиной [math]n[/math], инициализированный нулями. После обработки [math]i[/math]-го элемента будем вносить значение 1 в ячейку [math]S[M[i]][/math]. Обработку начинаем с последнего элемента и двигаемся к началу. Пусть, функция [math]Sum(i)[/math] возвращает значение суммы элементов массива [math]S[/math] от 1 до [math]i[/math]. Тогда [math]i[/math]-й элемент таблицы инверсий находится так:

[math]T[i] = Sum(M[i])[/math] 

Данная формула даёт правильный ответ, так как [math]S[M[i]][/math] содержит единицу только в том случае, если мы уже обработали элемент, стоящий в [math]M[i][/math]-й позиции. Так как обработка идёт от больших чисел к меньшим, [math]Sum(M[i])[/math] выдаст нужное нам количество чисел, больших [math]i[/math] и стоящих в перестановке левее него.

Функция [math]Sum[/math] реализуется с помощью дерева отрезков. Каждое изменение массива и обращение к функции [math]Sum[/math] влечёт за собой [math]\log_2n[/math] операций. Таким образом получаем сложность алгоритма [math]O(n\log_2n)[/math]

Также существует достаточно простой алгоритм построения таблицы инверсий с использованием сортировки слиянием. Вначале выписываются следующие упорядоченные пары чисел: в [math]i[/math]-ой паре на первом месте стоит [math]i[/math]-й элемент перестановки, а на втором — 0. Далее применяется сортировка слиянием, при чем при выписывании элемента из второй цепочки упорядоченных пар, число на второй позиции в данном элементе увеличивается на количество элементов, оставшихся в первой цепочке. В конце сортировки получим цепочку упорядоченных пар. Выписывая вторые элементы данных упорядоченных пар, получим искомую таблицу инверсий. Сложность данного алгоритма совпадает со сложностью сортировки слиянием и составляет [math]O(n\log_2n)[/math].

Алгоритм восстановления

Для восстановления таблицы перестановки из таблицы инверсий создаем таблицу, которую будем расширять, по мере добавления в неё чисел. Добавляем в эту таблицу число [math]i[/math] (где [math]i[/math] от [math]n[/math] до 1) на позицию [math]k+1[/math], где [math]k[/math] - число в таблице инверсий на [math]i[/math]-том месте. Данный алгоритм довольно прост в реализации, но без использования дополнительных структур данных, имеет сложность [math]O(n^2)[/math], т. к. для вставки элемента в определённую позицию, требуется порядка [math]n[/math] перестановок элементов.

Приведём алгоритм восстановления с использованием сортировки слиянием, имеющий сложность [math]O(n\log_2n)[/math].

Пусть [math]\alpha[/math] и [math]\beta[/math] - цепочки упорядоченных пар целых неотрицательных чисел [math][m_1, n_1]\dots[m_k, n_k][/math]. Рассмотрим двоичную операцию [math]\circ[/math], рекурсивно определенную на парах таких цепочек следующим образом:

$([m, n]\alpha)\circ([m', n']\beta)=\left\{\begin{aligned}[]
[m,n](\alpha \circ ([m'-m, n']\beta)), m \le m',\\
[m', n'](([m-m'-1, n]\alpha) \circ \beta), m>m'.\\
\end{aligned}
\right.$

Сопоставим каждому элементу таблицы инверсий его номер. Получится множество упорядоченных пар чисел [math][m_1, n_1]\dots[m_k, n_k][/math], где [math]m_i[/math] — сам элемент, а [math]n_i[/math] — его номер. Разобьём данные элементы на пары и произведём с ними операцию [math]\circ[/math]. Получим некоторое количество цепочек упорядоченных пар. Также разбиваем их на пары и производим операцию [math]\circ[/math]. Так действуем, пока не останется одна цепочка. Выписывая вторые элементы данных упорядоченных пар в том порядке, в каком они представлены в цепочке, получим первоначальную перестановку.

Цепочка наподобие [math][4, 4][1,3][/math] представляет "_ _ _ _ 4 _ 3 _ [math]\infty[/math]", где "_" означает пропуск. Операция [math]\alpha \circ \beta[/math] вставляет пропуски и заполнения из [math]\beta[/math] на место пропусков в [math]\alpha[/math].

Пример

  • [math][4, 1, 6, 3, 2, 2, 1, 1, 1, 0][/math] - таблица инверсий.
  • [math][4,1]\circ[1,2], [6,3]\circ[3,4], [2,5]\circ[2,6], [1,7]\circ[1,8], [1,9]\circ[0,10][/math]
  • [math][1,2][2,1]\circ[3,4][2,3], [2,5][0,6]\circ[1,7][0,8], [0,10][0,9][/math]
  • [math][1,2][2,1][0,4][2,3]\circ[1,7][0,5][0,6][0,8], [0,10][0,9][/math]
  • [math][1,2][0,7][0,5][0,1][0,4][0,6][0,8][0,3]\circ[0,10][0,9][/math]
  • [math][0,10][0,2][0,7][0,5][0,1][0,4][0,6][0,8][0,3][0,9][/math]

Получаем перестановку [math][10, 2, 7, 5, 1, 4, 6, 8, 3, 9][/math]

Источники

  • Д. Кнут - Искусство программирования, том 3.