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

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

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

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

  1. своп 3, 1: {4, 2, 5, 1, 3}
  2. своп 5, 1: {4, 2, 1, 5, 3}
  3. своп 4, 2: {2, 4, 1, 5, 3}
  4. своп 4, 1: {2, 1, 4, 5, 3}
  5. своп 2, 1: {1, 2, 4, 5, 3}
  6. своп 5, 3: {1, 2, 4, 3, 5}
  7. своп 3, 4: {1, 2, 3, 4, 5}

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

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

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

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

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

2 ответов


это классическая задача алгоритма. Минимальное число, если swaps равно числу инверсий в массиве. Если у нас есть индекс i и индекс j такой, что aЯ > aj и i

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

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

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

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

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

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


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

// http://stackoverflow.com/questions/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;
}