Сортировка последовательности путем замены соседних элементов с использованием минимальных свопов

Мы имеем несортированную последовательность из N чисел (1, 2, 3, 4,... N). Мы можем сортировать всю последовательность путем замены соседних элементов в определенном порядке. Учитывая последовательность, как я могу вычислить минимальные возможные свопы, необходимые для сортировки последовательности.

В качестве примера рассмотрим последовательность {4, 2, 5, 3, 1}.

Лучший способ сортировки - использовать 7 свопов в следующем порядке

  • Обмен 3, 1: {4, 2, 5, 1, 3}
  • Swap 5, 1: {4, 2, 1, 5, 3}
  • Обмен 4, 2: {2, 4, 1, 5, 3}
  • Swap 4, 1: {2, 1, 4, 5, 3}
  • Обмен 2, 1: {1, 2, 4, 5, 3}
  • Обмен 5, 3: {1, 2, 4, 3, 5}
  • Обмен 3, 4: {1, 2, 3, 4, 5}

Жадный алгоритм не оказался плодотворным. Контрпример был прост в построении. Следующим очевидным выбором для решения было динамическое программирование.

Скажем, что мы имеем несортированную последовательность: {A1, A2,... Ai, A (i + 1),..., An}. Мы знаем минимальное количество свопов, необходимых для сортировки последовательности {Ai, A (i + 1),..., An} - Min [Ai, A (i + 1),..., An}. Проблема заключается в поиске Min [A (i-1), Ai,..., An].

Ну, первая мысль, которая появилась в моей голове, заключалась в том, чтобы просто добавить количество шагов, необходимых для размещения A (i-1) в правильном месте в уже отсортированной последовательности {Ai,..., An}. Это работает: пример, заданный в вопросе, был решен с использованием того же метода.

Но я не смог доказать обоснованность этого решения. Это часто бывает со мной. Когда я думаю, что я решил проблему, лучшее, что я могу сделать, это получить "интуитивное" доказательство. Я в старшей школе и не имею формального обучения в алгоритмах как таковых. Я делаю это чисто из интереса.

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

Ответ 1

Это классическая проблема алгоритма. Минимальное число, если свопы равны числу инверсий в массиве. Если у нас есть индекс я и индекс j такие, что a i > a j и я < j, то это называется инверсией. Докажем это утверждение! Мне понадобится несколько лемм на пути:

Лемма 1: Если нет инверсии двух смежных элементов, массив сортируется.
Доказательство.. Предположим, что никакие два смежных элемента не образуют инверсию. Это означает, что a i <= a я + 1 для всех я в интервале [0, n-1]. Поскольку <= является транзитивным, это означает, что массив сортируется.

Лемма 2: Единственный своп двух смежных элементов уменьшит общее число инверсий в массиве максимум 1.
Доказательство:, когда мы заменяем два смежных элемента a i и я + 1 их относительное положение по отношению ко всем остальным элементам массива оставаться без изменений. То есть для всех элементов, которые были после я + 1, они все равно будут после я + 1 и для всех элементов до i, они все равно будут перед a i. Это также означает, что если i или я + 1 сформировал инверсию с элементом a j, то они все равно будут иметь обратную форму с это после обмена. Поэтому, если мы заменим i и я + 1, мы затронем только инверсии, которые эти два элемента использовали для формирования. Поскольку два элемента могут участвовать не более чем в одной инверсии, мы также доказали лемму.

Лемма 3: Нам нужно выполнить по крайней мере NI свопы смежных элементов, чтобы отсортировать массив, где NI - количество инверсий в массиве
Доказательство: В отсортированном массиве нет инверсий. Кроме того, согласно лемме 2, один своп может уменьшить количество инверсий не более чем на один. Таким образом, нам нужно выполнить как минимум столько свопов, сколько количество инверсий.

Лемма 4: Мы всегда можем сортировать массив, выполняющий NI-свопы смежных элементов, где точно так же, как и выше, NI - количество инверсий в массиве.
Доказательство.. Если мы предположим, что в нашем массиве нет инверсии двух смежных элементов, то согласно лемме 1 массив будет отсортирован, и мы закончим.
В противном случае существует хотя бы одна пара смежных элементов, которые образуют инверсию. Мы можем их поменять и таким образом уменьшить общее количество инверсий ровно один раз. Мы можем продолжать выполнять эту операцию ровно через NI.

Теперь я доказал свое утверждение с самого начала ответа.

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

Ответ 2

Благодаря объяснению @Ivaylo Strandjev, чтобы сделать ответ более полным, вот реализация Java:

// http://stackoverflow.com/info/20990127/sorting-a-sequence-by-swapping-adjacent-elements-using-minimum-swaps
// The minimum number if swaps is equal to the number of inversions in the array
public static long sortWithSwap(int [] a) {
    return invCount(a, 0, a.length-1);
}

private static long invCount(int[] a, int left, int right) {
    if(left >= right)   return 0;
    int mid = left + (right-left)/2;
    long cnt = invCount(a, left, mid) + invCount(a, mid+1, right);
    cnt += merge(a, left, mid, right);
    return cnt;
}

private static long merge(int[] a, int left, int mid, int right) {
    long cnt = 0;
    int i = left, j = mid+1, k = left;
    int[] b = new int[a.length];
    while(i<=mid && j<=right) {
        if(a[i] <= a[j])    b[k++] = a[i++];
        else {
            b[k++] = a[j++];
            cnt += mid - i + 1;
        }
    }

    while(i <= mid) {
        b[k++] = a[i++];
    }
    while(j <= right) {
        b[k++] = a[j++];
    }

    for(i=left; i<=right; i++)  a[i] = b[i];
    return cnt;
}