Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Алгоритмы и структуры данных.doc
Скачиваний:
0
Добавлен:
01.03.2025
Размер:
1.14 Mб
Скачать

4.4Генерация перестановок за одну транспозицию соседних элементов

Алгоритм предложен С. М. Джонсоном и Х. Ф. Троттером. Этот алгоритм генерирует каждую очередную перестановку путем однократной транспозиции двух соседних элементов предыдущей перестановки.

Суть алгоритма в следующем. Пусть задана последовательность элементов

1, 2, 3, …, n

И пусть сгенерирован блок из (n-1)! всех перестановок элементов

2, 3, …, n

Тогда требуемая последовательность перестановок элементов

1, 2, 3, … n

образуется путем вставки элемента 1 всеми возможными способами в каждую перестановку элементов

2, 3, …, n

Пример. Пусть задана последовательность элементов 1, 2, 3. Пусть получен блок всех перестановок элементов 2, 3. Это

2 3

3 2

Тогда получим следующий набор перестановок:

1 2 3

2 1 3

2 3 1

3 2 1

3 1 2

1 3 2

Очевидно, что элемент 1 в пределах блока перемещается попеременно вперед и назад (n-1)! раз. При каждом очередном направлении перемещения он занимает последовательно позиции от 1 до n-1.

Рассмотренный пример позволяет легко построить рекурсивный алгоритм генерации требуемой последовательности перестановок при заданном n. Однако такой рекурсивный алгоритм имеет очевидный и существенный недостаток. Для получения перестановок из n элементов в памяти необходимо хранить все перестановки из n-1 элементов, что ведет к большому расходу памяти.

Для устранения указанного недостатка предложен не рекурсивный вариант этого алгоритма. В алгоритме вводятся два вспомогательных массива Pr и C из n элементов каждый.

Пусть массив P, содержит последовательность вида

P[1]

P[2]

P[3]

P[n-3]

P[n-2]

P[n-1]

P[n]

a1

A2

a3

an

an

an

an

Для каждого i, 1 =< I <= n:

  • элемент булева массива Pr[i] содержит информацию о том, куда переносится элемент P[i] - вперед – слева направо (Pr[i] = true) или назад – справа налево (Pr[i] = false).

  • элемент массива C[i] показывает, какую из возможных n-i+1 позиций на своем пути вперед или назад элемент i занимает относительно элементов i+1, …, n, образующих блок перестановок для элементов i.

Для примера рассмотрим содержимое массивов P, C, Pr при генерации всех перестановок элементов 1, 2, 3, 4. В массиве Pr при Pr[i]=T элемент i движется вправо, а при Pr[i]=F этот элемент движется влево.

Первоначально для каждого i, P[i]=i, C[i]=1, Pr[i]=T. Следовательно, все элементы располагаются на своих исходных позициях и им разрешено движение вправо.

P 1 2 3 4

C 1 1 1 1

Pr T T T T

Элемент 1 движется вправо. C[1] меняет свое значение.

P 2 1 3 4

C 2 1 1 1

Pr T T T T

P 2 3 1 4

C 3 1 1 1

Pr T T T T

P 2 3 4 1

C 4 1 1 1

Pr T T T T

Элемент 2 движется вправо. C[2]; C[1], и Pr[1] меняют свое значение. Теперь 1 может двигаться влево.

P 3 2 4 1

C 1 2 1 1

Pr F T T T

Элемент 1 движется влево. C[1] меняет свое значение.

P 3 2 1 4

C 2 2 1 1

Pr F T T T

P 3 1 2 4

C 3 2 1 1

Pr F T T T

P 1 3 2 4

C 4 2 1 1

Pr F T T T

Элемент 2 движется вправо. C[2]; C[1] и Pr[1] меняют свое значение.

P 1 3 4 2

C 1 3 1 1

Pr T T T T

Элемент 1 движется вправо. C[1] меняет свое значение.

P 3 1 4 2

C 2 3 1 1

Pr T T T T

P 3 4 1 2

C 3 3 1 1

Pr T T T T

P 3 4 2 1

C 4 3 1 1

Pr T T T T

Элемент 3 движется вправо. C[3]; C[2] и Pr[2]; C[1] и Pr[1] меняют свое значение.

P 4 3 2 1

C 1 1 2 1

Pr F F T T

Элемент 1 движется влево. C[1] меняет свое значение.

P 4 3 1 2

C 2 1 2 1

Pr F F T T

P 4 1 3 2

C 3 1 2 1

Pr F F T T

P 1 4 3 2

C 4 1 2 1

Pr F F T T

Элемент 2 движется влево. C[2]; C[1] и Pr[1] меняют свое значение.

P 1 4 2 3

C 1 2 2 1

Pr T F T T

Элемент 1 движется вправо. C[1] меняет свое значение.

P 4 1 2 3

C 2 2 2 1

Pr T F T T

P 4 2 1 3

C 3 2 2 1

Pr T F T T

P 4 2 3 1

C 4 2 2 1

Pr T F T T

Элемент 2 движется влево. C[2]; C[1] и Pr[1] меняют свое значение.

P 2 4 3 1

C 1 3 2 1

Pr F F T T

Элемент 1 движется влево. C[1] меняет свое значение.

P 2 4 1 3

C 2 3 2 1

Pr F F T T

P 2 1 4 3

C 3 3 2 1

Pr F F T T

P 1 2 4 3

C 4 3 2 1

Pr F F T T

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

Из анализа приведенного примера можно сделать следующие выводы:

  1. Позиция C[i] элемента i в его блоке - блоке, содержащем элементы i, i+1, …, n, отсчитывается в зависимости от направления движения этого элемента. При движении вперед отсчет ведется слева направо, а при движении назад – справа налево. Например, в перестановке

1 2 3 4

при движении вперед элемент 1 занимает относительно своего блока позицию 1, а при движении назад – позицию 4. Аналогично, в перестановке

2 3 4 1

при движении вперед элемент 1 занимает относительно своего блока позицию 4, а при движении назад – позицию 1.

  1. Элемент i в массиве P меняет направление своего движения на противоположное, если выполняется условие

C[i] = N- i +1

  1. Алгоритм прекращает свою работу, если для всех i = 1, 2, …, n выполняется условие

C[i] = N- i +1

  1. Позиция элемента i в массиве P определяется на основании взаимосвязи следующих факторов:

  • направление движения элемента i;

  • позиция C[i] элемента i в блоке, содержащем элементы i, i+1, …, n.

  • расстояние от начала (конца) массива P до начала блока элемента i. Это расстояние измеряется числом X элементов 1, 2, …, i-1, которые находятся слева от указанного блока при движении элемента i вперед и справа – при движении элемента i назад. Число X вычисляется как число элементов j, где j < i, таких, что:

  • при направлении движения элемента i вперед эти элементы, двигаясь назад, достигли бы или занимают своё крайне левое положение.

  • при направлении движения элемента i назад эти элементы, двигаясь вперед, достигли бы или занимают своё крайне правое положение.

Для случая движения элемента i вперед его позиция в массиве P определяется следующим образом

K = C[i]+ X

а для случая движения элемента i назад его позиция в массиве P определяется следующим образом

K = N – (C[i] + X) + 1

Примечание. Следует обратить внимание, что при таком подходе к определению местонахождения элемента i в массиве P, переменная X принимает свое конкретное значение для каждого элемента i. В результате переменная X должна рассматриваться как массив, содержащий N элементов, где X[i] расстояние блока для элемента i от начала последовательности. Однако этого можно избежать, если вычислять значение переменной X для каждого элемента i при его перемещении на новое место. Именно этот способ реализуется в программе.

Следующие примеры иллюстрируют способы вычисления переменных X и K для некоторого элемента i.

P 1 3 4 2

C 1 3 1 1

Pr T T T T

элементы 1, 2 и 3 движутся вправо.

Элемент 1 в массиве P располагается на позиции K=1. Относительно своей группы (2, 3, 4) он занимает позицию 1 и слева от него нет ни одного элемента <1 т.е. (X=0).

Элемент 2 в массиве P располагается на позиции K=4. Относительно своей группы (3, 4) он занимает позицию 3 и слева от него находится только один элемент 1<2, который, двигаясь назад, достиг бы своего крайнего левого положения (X=1).

Элемент 3 в массиве P располагается на позиции K=2. Относительно своей группы (4) он занимает позицию 1 и слева от него находится только один элемент 1<3, который, двигаясь назад, достиг бы своего крайнего левого положения (X=1).

С другой стороны, для случая:

P 2 4 1 3

C 2 3 2 1

Pr F F T T

элементы 1 и 2 движутся влево, а элементы 3 и 4 движется вправо.

Элемент 1 в массиве P располагается на позиции K=3. Относительно своей группы (2, 4, 3) он занимает позицию 2 и справа от него нет ни одного элемента <1, т.е. (X=0).

Элемент 2 в массиве P располагается на позиции K=1. Относительно своей группы (3, 4) он занимает позицию 3 и справа от него находится только один элемент 1<2, который, двигаясь вперед, достиг бы своего крайнего правого положения (X=1).

  1. Элемент i, подлежащий очередному перемещению (вперед или назад), выбирается следующим образом. Первоначально предпринимается попытка переместить элемент i = 1. Если он не достиг своей крайней (левой или правой) позиции, то выполняется его перемещение, после чего процесс выбора повторяется заново. Если элемент i = 1 достиг своей крайней (левой или правой) позиции, то предпринимается попытка переместить элемент i = 2. Если это удается, то выполняется перемещение элемента i = 2, после чего процесс выбора повторяется заново. Если элемент i = 2 достиг своей крайней (левой или правой) позиции, то предпринимается попытка переместить элемент= 3 и т.д.

Таким образом, элемент i = 1 может последовательно перемещаться на целый ряд позиций, тогда как элементы i = 2 и i = 3 и т.д. на каждом этапе могут перемещаться только на одну позицию, после чего управление вновь получает элемент i = 1.

program Permutation4;

uses Crt;

const N = 4;

var I: integer; {Элемент исходной последовательности, подлежащий перемещению.}

K: integer; {Позиция элемента I, подлежащего перемещению}

X: integer; {Используется для определения позиции K, элемента, подлежащего перемещению.}

J, Work: integer;

P: array [1..N] of integer; {Массив элементов}

C: array [1..N] of integer; {Массив позиций элементов в блоке}

Pr: array [1..N] of boolean; {Массив направлений движения элементов}

begin

Clrscr;

for J := 1 to N do

begin {Формируются исходные состояния рабочих массивов}

P[J] := J;

C[J] := 1;

Pr[J] := true;

end;

C[N] := 0; {Элемент в позиции N последний в последовательности. Он не имеет своего блока перестановок и, следовательно, не может перемещаться относительно этого блока. Поэтому C[N] меняться не будет, а для удобства создания алгоритма примем C[N]= 0}

for J := 1 to N do Write (P[J], ' ');

Writeln;

I := 1;

while I < N do

begin

I := 1; {Начальный выбор элемента, подлежащего перемещению}

X := 0;

{Уточнение элемента, подлежащего перемещению, и направления движения элементов}

while C[I] = N - I + 1 do

begin

Pr[I] := not Pr[I]; {изменение направления движения}

C[I] := 1;

if Pr[I] then X := X + 1; {Подсчет количества элементов, начиная с 1, которые при своем движении назад стали на свое место в начале массива P}

I := I + 1; {Уточнение элемента, который подлежит перемещению}

end;

if I < N then {Продолжение генерации}

begin

{Определение K - позиции элемента, подлежащего перемещению, в массиве P}

if Pr[I] then K := C[I] + X {При направлении движения вперед}

else K := N - I + 1 - C[I] + X; {При направлении движения назад}

{Выполнение транспозиции}

Work := P[K];

P[K] := P[K+1];

P[K+1] := Work;

for J := 1 to N do Write (P[J], ' ');

Writeln;

C[I] := C[I] + 1; {Корректировка положения элемента I в его блоке}

end;

end;

end. {Permutation4}