
- •1Введение
- •2Сортировки
- •2.1Сортировки массивов
- •2.2Сортировка простым включением
- •2.3Сортировка простым выбором
- •2.4Сортировка простым обменом
- •2.5Сравнение простых сортировок
- •2.6 Сортировка шелла
- •2.7Пирамидальная сортировка
- •2.8Быстрая сортировка
- •2.9Поиск медианы и квантилей
- •2.10Сравнение сортировок
- •3Поиск подстроки в строке
- •3.1Поиск в строке
- •3.2Простой поиск в строке
- •3.3Поиск в строке. Алгоритм боуера-мура
- •4Генерация перестановок
- •4.1Генерация перестановок последовательности натуральных чисел
- •4.2Генерация перестановок элементов в антилексикографическом порядке
- •4.3Генерация перестановок за одну транспозицию элементов
- •4.4Генерация перестановок за одну транспозицию соседних элементов
- •5Генерация подмножеств
- •5.1Генерация всех подмножеств множества
- •5.2Генерация m -элементных подмножеств множества натуральных чисел
- •Var m: integer; {Размер подмножества}
- •Var I, j, p: integer;
- •5.3Генрация k-компонентных выборок на множестве {0, 1}
- •Var k: integer; {Количество нулей в кортеже}
- •I: integer;
- •Var I, j, p: integer;
- •If Finish then Break {Exit};
- •6Генерация разбиений
- •6.1Разбиение целых чисел
- •Var I, j: integer;
- •Var j: integer;
- •Var I, j, k: integer;
- •Var d, l, Sum: integer;
- •6.2Разбиение множеств
- •/1, 2, 3/ И /4/ затем /1, 2/ и /3, 4/ и т.Д. }
- •I, j, k, r, s: integer;
- •If not Flag2 then
- •If Flag1 then
- •If Forvd[j] then { j движется вперед}
- •7Обходы бинарных деревьев
- •7.1Процедуры прохождения бинарных деревьев
- •8Поиск на бинарных деревьях
- •8.1Процедуры поиска на бинарных деревьях
- •Рекомендованная литература
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
Примечание. В приведенных примерах подчеркнуты перестановки, в которых отдельные элементы меняют направление своего движения. Кроме того, обращаться к этим перестановкам целесообразно при рассмотрении оставшейся части этого раздела и текста программы.
Из анализа приведенного примера можно сделать следующие выводы:
Позиция C[i] элемента i в его блоке - блоке, содержащем элементы i, i+1, …, n, отсчитывается в зависимости от направления движения этого элемента. При движении вперед отсчет ведется слева направо, а при движении назад – справа налево. Например, в перестановке
1 2 3 4
при движении вперед элемент 1 занимает относительно своего блока позицию 1, а при движении назад – позицию 4. Аналогично, в перестановке
2 3 4 1
при движении вперед элемент 1 занимает относительно своего блока позицию 4, а при движении назад – позицию 1.
Элемент i в массиве P меняет направление своего движения на противоположное, если выполняется условие
C[i] = N- i +1
Алгоритм прекращает свою работу, если для всех i = 1, 2, …, n выполняется условие
C[i] = N- i +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).
Элемент i, подлежащий очередному перемещению (вперед или назад), выбирается следующим образом. Первоначально предпринимается попытка переместить элемент i = 1. Если он не достиг своей крайней (левой или правой) позиции, то выполняется его перемещение, после чего процесс выбора повторяется заново. Если элемент i = 1 достиг своей крайней (левой или правой) позиции, то предпринимается попытка переместить элемент i = 2. Если это удается, то выполняется перемещение элемента i = 2, после чего процесс выбора повторяется заново. Если элемент i = 2 достиг своей крайней (левой или правой) позиции, то предпринимается попытка переместить элемент i = 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}