Динамическое программирование: сколько способов получить по крайней мере N свопов пузырьковой сортировки?

Допустим, у меня есть массив элементов, для которых существует общий порядок. Расстояние пузырьковой сортировки — это количество перестановок, которые потребовались бы для сортировки массива, если бы я использовал пузырьковую сортировку. Каков эффективный (вероятно, будет включать динамическое программирование) способ вычисления количества возможных перестановок этого массива, которые будут иметь расстояние пузырьковой сортировки, меньшее или равное некоторому заранее заданному числу?

Если это упрощает задачу, можно считать, что все элементы массива уникальны (без связей).


person dsimcha    schedule 04.06.2009    source источник
comment
В заголовке вопроса указано не менее N обменов пузырьковой сортировки, а в тексте вопроса указано, что расстояние пузырьковой сортировки меньше или равно некоторому заранее заданному числу — какое оно?   -  person ShreevatsaR    schedule 04.06.2009
comment
Это законная ошибка, но на самом деле это не имеет большого значения. Это просто разные формы одного и того же вопроса.   -  person dsimcha    schedule 04.06.2009
comment
Верно, я предположил в своем ответе ниже, что вы имели в виду не более k обменов пузырьковой сортировкой. Таким образом, количество перестановок n элементов с не менее чем k обменами пузырьков равно [n! - количество перестановок с не более чем k-1].   -  person ShreevatsaR    schedule 04.06.2009


Ответы (2)


Хорошо, вот решение. Предположим, что все элементы массива различны, и далее без ограничения общности можно считать, что это {1,...,n}. (Мы всегда можем переименовать элементы так, чтобы это было так, и ничего не пострадает.)

Во-первых, мы можем заметить, что количество перестановок, выполненных пузырьковой сортировкой, равно количеству инверсий в перестановке a[1..n]: количеству пар (i,j), таких что i‹ j, но a[i]›a[j]. (Это не так уж сложно доказать.)

Итак, нам нужно количество перестановок {1,...,n} с не более чем k инверсиями. Пусть c(n,k) обозначает это число. Любую перестановку {1,...n} можно рассматривать как перестановку {1,...,n-1} и вставку {n} в нее куда-нибудь. Если вы вставите его в позицию i, он создаст ровно n-i новых инверсий. Таким образом, старая перестановка должна была иметь не более k-(n-i) инверсий. Это дает:

c(n,k) = sum_{i s.t. n-i≤k} c(n-1, k-(n-i))
       = sum_{i=max(1,n-k) to n} c(n-1, k-n+i)

И базовый случай:

c(1,0) = 1 (or better, c(0,0)=1)

(Обратите внимание, что k не больше n*(n-1)/2 ‹ n2.)


Обновление. В приведенном выше примере требуется O(n^2k) — то есть до O(n^4) — времени для вычисления c(n,k), поскольку каждый из nk c(n,k)' s требует O(n) времени для вычисления, учитывая более ранние. Мы можем улучшить в n раз, уменьшив повторение, чтобы каждое c(n,k) можно было вычислить за время O(1) с учетом предыдущих. Запишите j вместо k-n+i так, чтобы

c(n,k) = sum_{j=max(k-n+1,0) to k} c(n-1, j)

Обратите внимание, что большая часть суммы одинакова для c(n,k) и c(n,k-1). Конкретно,

When k≤n-1, c(n,k) = c(n,k-1) + c(n-1,k)
When k≥n,   c(n,k) = c(n,k-1) + c(n-1,k) - c(n-1,k-n)

Обновленная программа: (я написал ленивую версию для запоминания; вы можете сделать ее немного более эффективной, сделав ее восходящей, как обычно при динамическом программировании.)

ct = {(0,0): 1}
def c(n,k):
    if k<0: return 0
    k = min(k, n*(n-1)/2) #Or we could directly return n! if k>=n*(n-1)/2
    if (n,k) in ct: return ct[(n,k)]
    ct[(n,k)] = c(n,k-1) + c(n-1,k) - c(n-1,k-n)
    return ct[(n,k)]

if __name__ == "__main__":
    n = input("Size of array: ")
    k = input("Bubble-sort distance at most: ")
    print c(n,k)
person ShreevatsaR    schedule 04.06.2009

Взгляните на алгоритм Вагнера-Фишера для редактирования расстояний. Вероятно, вы движетесь в том же направлении: создайте таблицу наименьших свопов, которая должна быть nn в вашей задаче, используя инвариантное отношение, которое позволяет вам строить таблицу от верхнего левого угла к нижнему правому.

person Charlie Martin    schedule 04.06.2009
comment
Я не думаю, что это полезно или актуально... это о чем-то похожем, но как это поможет здесь? - person ShreevatsaR; 04.06.2009
comment
Я не согласен. Я также подозреваю, что это может быть несколько домашней работой, поэтому я намекаю на направление. - person Charlie Martin; 04.06.2009
comment
Это не проблема домашнего задания. Я попытался избавиться от всего ненужного контекста и оставить только ту часть, на которой я застрял, но на самом деле она предназначена для вычисления точного значения P для тау-корреляции Кендалла. - person dsimcha; 04.06.2009
comment
Хорошо, я был травмирован парнем в аспирантуре, который сделал эти штуки динамического программирования каждой проблемой. (На самом деле Вагнер Вагнера-Фишера.) Тем не менее, я думаю, что это именно тот образец, который вам нужен. - person Charlie Martin; 04.06.2009
comment
Я подумал об одном способе подсчета чисел в вопросе, но до сих пор не вижу способа использовать этот ответ. :-) Было бы здорово увидеть связь, правда... (помимо того, что они оба являются динамическим программированием) - person ShreevatsaR; 04.06.2009
comment
Что ж, у меня нет кода для демонстрации решения, но учтите: своп — это ровно одно удаление и вставка. Таким образом, должна быть возможность использовать WF путем сокращения. - person Charlie Martin; 04.06.2009
comment
А, понятно, спасибо. Проблема в том, что расстояние редактирования (Вагнер-Фишер или что-то еще) — это количество правок, которое потребуется, если вы решите сделать правки оптимальными, но расстояние пузырьковой сортировки — это количество свопов, которые конкретное фиксированное (и глупое) алгоритм возьмет. - person ShreevatsaR; 05.06.2009