Upgrade to Pro — share decks privately, control downloads, hide ads and more …

САКОД – 2 курс весна 2021 – 4 занятие

САКОД – 2 курс весна 2021 – 4 занятие

ТиМПИ

March 16, 2021
Tweet

More Decks by ТиМПИ

Other Decks in Programming

Transcript

  1. 2/15 2/15 Размещение Перестановка Сочетание Комбинаторные структуры ! ( )!

    k n n A n k   : ; U U U n    (𝑛, 𝑘)-выборка — набор элементов 1 2 , ( ) , , , k i a a a a U   ! n P n  ) ! ! ! ( k n n C n k k   
  2. 3/15 3/15 Комбинаторные алгоритмы — это алгоритмы, которые имеют дело

    с комбинаторными структурами, которые представляют собой множества, упорядоченные n-кортежи и любые структуры, которые могут быть построены из них. Комбинаторные алгоритмы включают алгоритмы: • Генерации: перечислить все структуры данного типа (сочетания и перестановки). • Поиска: найти хотя бы одну структуру с заданным свойством Комбинаторные алгоритмы
  3. 4/15 4/15 Описание алгоритма С каждым элементом перестановки связываем направление.

    Направление — указатель на соседний элемент (может указывать на элемент справа или слева). Элемент перестановки называется мобильным, если его направление указывает на меньший соседний элемент. 1) Создается первая перестановка. Ряд чисел по возрастанию 1, 2, 3, ... n. Направление каждого элемента указывает влево. 2) Ищем наибольший мобильный элемент. Если не находим, то алгоритм закончен. 3) Производим обмен, найденного мобильного элемента с элементом, на который указывает направление найденного мобильного элемента. 4) Меняем направление у всех элементов, которые больше чем найденный на шаге 2 элемент. 5) Переходим к шагу 2. Алгоритм Джонсона-Троттера [1,2,3 ] [1,3,2 ] [1,2,3 ] [3,1,2 ] [1,3,2 ] [3,2,1 ] [3,1,2 ] [3,2,1 ] [2,3,1 ] [2,3,1 ] [2,1, 3] end
  4. 5/15 5/15 Лексикографический порядок перестановок Лексикографический порядок — отношение линейного

    порядка на множестве последовательностей. 1) Последовательность А предшествует последовательности В (А < В), если первые n элементов из этих последовательностей совпадают, а n +1 элемент В больше n+1 элемента А. 2) Последовательность А является началом последовательности В. :1,2,3,4,5 :1,2,3,5,4 A B 1 1 n n a b    :1,2,3 :1,2,3,5,4 A B
  5. 6/15 6/15 Алгоритм Нарайаны 1) Найти такой максимальный индекс i

    для которого 𝐴𝑖 < 𝐴𝑖+1 (оптимально выполнять поиск с конца перестановки). 2) Найти максимальный индекс j для которого 𝐴𝑗 > 𝐴𝑖 (оптимально выполнять поиск с конца перестановки). 3) Выполнить обмен 𝐴𝑖 и 𝐴𝑗 элемента местами 4) Записать последовательность 𝐴𝑖+1 , … , 𝐴𝑛в обратном порядке [1,2,3] → [1,2,3] → [1,2,3] → [1,3,2] i=1 j=2 [1,3,2] → [1,3,2] → [1,3,2] → [2,3,1] → [2,3,1] → [2,1,3] i=0 j=2
  6. 7/15 7/15 Генерация перестановок [0, 1, 2, 3] [0, 1,

    3, 2] [0, 2, 1, 3] [0, 2, 3, 1] [0, 3, 1, 2] [0, 3, 2, 1] [1, 0, 2, 3] [1, 0, 3, 2] [1, 2, 0, 3] [1, 2, 3, 0] [1, 3, 0, 2] [1, 3, 2, 0] [2, 0, 1, 3] [2, 0, 3, 1] [2, 1, 0, 3] [2, 1, 3, 0] [2, 3, 0, 1] [2, 3, 1, 0] [3, 0, 1, 2] [3, 0, 2, 1] [3, 1, 0, 2] [3, 1, 2, 0] [3, 2, 0, 1] [3, 2, 1, 0] static void swap(unsigned int *ar, unsigned int first, unsigned int second) { unsigned int temp = ar[first]; ar[first] = ar[second]; ar[second] = temp; } unsigned int next_permutation(unsigned int *ar, size_t len) { unsigned int i1, i2; unsigned int result = 0; /* Ищем наивысший индекс i1, такой, что ar[i1] является первым из пары элементов в порядке возрастания*/ for (i1 = len - 2, i2 = len - 1; ar[i2] <= ar[i1] && i1 != 0; i1--, i2--); if (ar[i2] <= ar[i1]) { /* Если не найден, массив – наивысшая перестановка => полностью перевернуть*/ reverse(ar, len); } else { /* Находим крайний правый элемент справа от i1, который больше ar[i1]*/ for (i2 = len - 1; i2 > i1 && ar[i2] <= ar[i1]; i2--); swap(ar, i1, i2); /* Перевернуть остальные*/ reverse(ar + i1 + 1, len - i1 - 1); result = 1; } return result; } static void reverse(unsigned int *ar, size_t len) { unsigned int i, j; for (i = 0, j = len - 1; i < j; i++, j--) { swap(ar, i, j); } }
  7. 8/15 8/15 Циклический сдвиг влево Под циклическим сдвигом влево подразумевается

    смещение всех значений последовательности на одну позицию влево. При этом первый элемент последовательности становится последним элементом. Если выполнить сдвиг влево определенное количество раз (размер последовательности), то получим опять начальную последовательность. [1,2,3] → [2,3,1] → [3,1,2] → [1,2,3]
  8. 9/15 9/15 Генерация перестановок с помощью циклических сдвигов 1) Создать

    последовательность размером n. Заполнить ее значениями равными индексам элементов. Создать вспомогательную переменную k. Установить ее значение равное индексу последнего элемента. 2) Вывести последовательность. Установить значение k равное индексу последнего элемента. 3) Выполнить циклический сдвиг влево элементов последовательности от первого до k-го. Если k-й элемент последовательности не равен k, то перейти к пункту 2. В противном случае перейти к пункту 4. 4) Задать новое значение k = k-1. Если k равен индексу первого элемента последовательности, то закончить алгоритм. В противном случае перейти к пункту 3. [0,1,2] [1,2,0] [2,0,1] [0,1,2] [0,1,2] [1,0,2] [1,0,2] [0,2,1] [2,1,0] [1,0,2] [1,0,2] [0,1,2] [0,1,2] end k k k k k
  9. 10/15 10/15 Генерация сочетаний Генерируем все сочетания из n целых

    чисел [0,1,2...n-1] по k. 1) Создаем последовательность размером k+2 элемента. Первые k — элементов устанавливаем равными индексу элемента. Элемент k+1 устанавливаем равным n, k+2 — равный 0. 2) Начиная с начала последовательности проверяем условие 𝑘𝑖 +1 = 𝑘𝑖+1 . Если это так, то устанавливаем элемент равный его индексу. Как только это условие нарушено, то переходим к пункту 3. 3) Если индекс элемента, где нарушено условие, больше k, то заканчиваем алгоритм, если нет, то увеличиваем элемент, на который указывает индекс, на единицу. Возвращаемся к пункту 2. [0,1,2,5,0] → [0,1,2,5,0] → [0,1,3,5,0] → [0,1,3,5,0] → [0,2,3,5,0] → → [0,2,3,5,0] → [1,2,3,5,0] → [1,2,3,5,0] → [0,1,4,5,0] → → [0,1,4,5,0] → [0,2,4,5,0] → [0,2,4,5,0] → [1,2,4,5,0] → →[1,2,4,5,0] →[0,3,4,5,0] → [0,3,4,5,0] → [1,3,4,5,0] → →[1,3,4,5,0] → [2,3,4,5,0] → [2,3,4,5,0]
  10. 11/15 11/15 Генерация размещений без повторений 1) Создаем последовательность длиной

    n. Заполняем ее значениями равными индексу элемента. Задаём k. Возвращаем первые k — элементов. Это первое размещение. 2) Получаем значение стоящее на k-1 индексе в последовательности. Проводим поиск начиная от k-го индекса и до конца. Ищем индекс минимального элемента который больше элемента стоящего на k-1 индексе. 3) Возможны два случая: a) Если такой индекс был найден (в дальнейшем j) то производим обмен значений на k-1 и j индексах. В качестве следующего размещения возвращаем первые k элементов. Переходим к 2. b) Индекс не найден. Переходим к 4. 4) Проводим реверс от k-го индекса и до конца последовательности. Выполняем поиск начиная от k-1 индекса и до 0 (обратный проход). Ищем такой элемент что s[i]<s[i+1]. Возможны два случая. a) Такой элемент найден. Тогда выполняем поиск начиная с конца последовательности и до i. Ищем индекс минимального элемента который будет больше чем s[i]. Меняем их местами и производим реверс последовательности от i+1 индекса. Возвращаем первые k элементов. Переходим к 2. b) Такой элемент не найден. Заканчиваем алгоритм.
  11. 12/15 12/15 Генерация размещений без повторений [0,1,2,3,4] → [0,1,2,3,4] →

    [0,1,3,2,4] → [0,1,3,2,4] → [0,1,4,2,3] → [0,1,4,2,3] → [0,1,4,3,2] → [0,2,4,3,1] → [0,2,1,3,4] → [0,2,1,3,4] → [0,2,3,1,4] → [0,2,3,1,4] → [0,2,4,1,3] .... — Указатель на k-1 индекс — Обмен значений — Реверс — Возврат размещения — Указатель на искомое 0,1,2
  12. 13/15 13/15 Генерация размещений с повторениями 1)Создаем последовательность длиной k.

    Заполняем ее значениями равными нулю. Возвращаем первые k — элементов. Это первое размещение. 2) Начиная с конца последовательности ищем элемент, значение которого меньше чем n-1. a) Такой элемент найден. Увеличиваем его значение на единицу. Устанавливаем значения для всех элементов которые стоят правее равными 0. Возвращаем первые k элементов. Переходим к 2. b) Такой элемент не найден. Заканчиваем алгоритм. [0,0,0] → [0,0,0] →[0,0,1] → [0,0,1] →[0,0,2] →[0,0,2] → [0,0,3] →[0,0,3] → →[0,0,4] →[0,0,4] → [0,1,0] →[0,1,0] →[0,1,1] → [0,1,1] →[0,1,2] →[0,1,2] → →[0,1,3] →[0,1,3] → [0,1,4] →[0,1,4] → [0,2,0] →…
  13. 14/15 14/15 Генерация разбиения числа Разбиение числа n — это

    представление n в виде суммы положительных целых чисел, называемых частями. При этом порядок следования частей не учитывается, то есть разбиения, отличающиеся только порядком частей, считаются равными. 1)[1,1,1,1,1] 2)[2,1,1,1] 3)[2,2,1] 4)[3,1,1] 5)[3,2] 6)[4,1] 7)[5] 1)Создаем последовательность длиной n. Заполняем ее единицами. 2)Если длина последовательности равна 1 заканчиваем алгоритм. В противном случае последовательность есть следующее разбиение. Используем его и переходим к 3. 3)Находим ближайший к началу последовательности минимальный элемент (поиск проводим от начала до предпоследнего элемента). Увеличиваем его на единицу. Уменьшаем на единицу последний элемент последовательности. Вычисляем сумму элементов правее найденного минимального элемента. Создаем новую последовательность из элементов предыдущей последовательности начиная с начала и до найденного минимального и такого количества единиц как найденная ранее сумма. Переходим к 2. [1,1,1,1,1] → [2,1,1,1,0] → [2,1,1,1] → [2,1,1,1] → [2,2,1,0] → →[2,2,1] → [2,2,1] → [3,2,0] → [3,1,1] → [3,1,1] → [3,2,0] → →[3,2] → [3,2] →[4,1] → [4,1] → [4,1] → [5,0] → [5] → end Минимальный элемент Суммирование элементов Возврат разбиения
  14. 15/15 15/15 Разбиение числа на m частей Разбиение целого числа

    n на m частей — представление числа n в виде кортежа длинной m сумма элементов которого равна n. При этом должно выполняться условие, что n≥m≥2. 1)8 = 5+1+1+1 2)8 = 4+2+1+1 3)8 = 3+3+1+1 4)8 = 3+2+2+1 5)8 = 2+2+2+2 Алгоритм Гиденбурга 1) Создаем последовательность 𝑎 длинной 𝑚. Устанавливаем 𝑎0 = 𝑛 − 𝑚 + 1, остальные элементы равные 1. 2) Возвращаем последовательность в качестве разбиения. Выполнить проверку 𝑎1 < 𝑎0 -1, если это условие истинно, перейти к 3, в противном случае перейти к 4. 3) Установить 𝑎0=𝑎0-1, 𝑎1=𝑎1+1. Перейти к 2. 4) Начиная с второго элемента последовательности ищем элемент удовлетворяющий условию 𝑎𝑗 < 𝑎0 -1. Если такого элемента не найдено заканчиваем алгоритм. Вычисляем сумму всех элементов до найденного элемента минус 1 (s). Увеличим значение найденного элемента на единицу. Выполняем проход от второго элемента до найденного элемента последовательности. На каждом шаге устанавливаем значение равное значению найденного элемента, при этом уменьшая значение s на эту величину. После завершения прохода устанавливаем значение первого элемента последовательности как s. Перейти к 2. [5,1,1,1] → [5,1,1,1] →[4,2,1,1] → [4,2,1,1] → [3,3,1,1] →[3,3,1,1] → [3,2,2,1] → →[3,2,2,1] →[2,2,2,2] → end Уменьшение на единицу Увеличение на единицу Возврат разбиения Суммирование 0,1,2