Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
60
Добавлен:
01.09.2020
Размер:
65.89 Кб
Скачать

МИНОБРНАУКИ РОССИИ

Санкт-Петербургский государственный

электротехнический университет

«ЛЭТИ» им. В.И. Ульянова (Ленина)

Кафедра МО ЭВМ

отчет

по лабораторной работе №1

по дисциплине «ПОСТРОЕНИЕ и АНАЛИЗ АЛГОРИТМОВ»

Тема: Поиск с возвратом

Студент гр. 8383

Ларин А.

Преподаватель

Фирсов М.А.

Санкт-Петербург

2020

Цель работы.

Изучить принцип работы алгоритма поиска с возвратом. Решить с его помощью задачу

Основные теоретические положения.

Поиск с возвратом, бэктрекинг  — общий метод нахождения решений задачи, в которой требуется полный перебор всех возможных вариантов в некотором множестве. Решение задачи методом поиска с возвратом сводится к последовательному расширению частичного решения. Если на очередном шаге такое расширение провести не удается, то возвращаются к более короткому частичному решению и продолжают поиск дальше. Данный алгоритм позволяет найти все решения поставленной задачи, если они существуют. Для ускорения метода стараются вычисления организовать таким образом, чтобы как можно раньше выявлять заведомо неподходящие варианты. Зачастую это позволяет значительно уменьшить время нахождения решения. Метод поиска с возвратом является универсальным. Достаточно легко проектировать и программировать алгоритмы решения задач с использованием этого метода. Однако время нахождения решения может быть очень велико даже при небольших размерностях задачи (количестве исходных данных), причём настолько велико (может составлять годы или даже века), что о практическом применении не может быть и речи. Поэтому при проектировании таких алгоритмов, обязательно нужно теоретически оценивать время их работы на конкретных данных. Существуют также задачи выбора, для решения которых можно построить уникальные, «быстрые» алгоритмы, позволяющие быстро получить решение даже при больших размерностях задачи. Метод поиска с возвратом в таких задачах применять неэффективно.

Задание

У Вовы много квадратных обрезков доски. Их стороны (размер) изменяются от 1 до N−1, и у него есть неограниченное число обрезков любого размера. Но ему очень хочется получить большую столешницу – квадрат размера N. Он может получить ее, собрав из уже имеющихся обрезков(квадратов).    Например, столешница размера 7×7 может быть построена из 9 обрезков.

   Внутри столешницы не должно быть пустот, обрезки не должны выходить за пределы столешницы и не должны перекрываться. Кроме того, Вова хочет использовать минимально возможное число обрезков. Входные данные    Размер столешницы – одно целое число N (2≤N≤20). Выходные данные    Одно число K, задающее минимальное количество обрезков(квадратов), из которых можно построить столешницу (квадрат) заданного размера N. Далее должны идти K строк, каждая из которых должна содержать три целых числа x, y и w, задающие координаты левого верхнего угла (1≤x, y≤N) и длину стороны соответствующего обрезка (квадрата). Пример входных данных 7 Соответствующие выходные данные 9 1 1 2 1 3 2 3 1 1 4 1 1 3 2 2 5 1 3 4 4 4 1 5 3 3 4 1

Вар. 3р. Рекурсивный бэктрекинг. Исследование кол-ва операций от размера квадрата.

Реализация

Описание алгоритма

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

Сложность по времени витка рекурсии является многочленом большой степени, с учетом вложенности — четвертую. Учитывая рекурсивный бэктрекинг сложность близка к экспоненте. Затраты по памяти пропорциональны глубине рекурсии, которая не привышает n^2.

Частичные решения хранятся в виде троек чисел с координатами и размером, а так же в виде матрицы с нулями в свободных клетках и иными значениями в местах соответствующих квадратам.

Описание функций и структур данных

Для хранения частичных решений использовался stl контейнер vector.

Для решения задачи используются следующие функции

std::vector<int> fct(int n)

Факторизация числа.

Входные данные — n число

Выходные данные — вектор множителей

void drawMap(std::vector<std::vector<int> > map)

Отображение карты квадратов в консоли

Входные данные — map карта поля

void iterate(int n, std::vector<std::vector<int> > vtx, std::vector<std::vector<int> > map, int cnt = 1, int initSize = 0, int cap = 0)

Рекурсивная функция, осуществляющая поиск с возвратом

Входные данные :

n - размер поля

vtx — вектор размещенных квадратов

map — карта поля

cnt — счетчик размещенных квадратов

initSize — начальный размер для перебора. Мера для оптимизации

cap — кольчество незаполненых клеток. Одно из условий остановки рекурсии

void backtrack(int n)

Инициализация процесса поиска

Входные данные — n размеры поля

Примененные оптимизации

Перебор мест по поясам спасает от повторного просмотра столбцов либо строк.

При нахождении хотя бы одного решения любой виток с решением хуже завершается преждевременно

Поля имеющие простые множители могут быть заполнены (мин.множитель)2 квадратами размера (размер поля)/(мин.множитель) за незначительное время

Новый квадрат устанавливается в максимально верхнюю левую клетку, что сильно сокращает перебор

Когда размер очередных квадратов достигает 1 разные варианты расстановки не рассматриваются

Тесты.

В приложении Б

Исследование

Таблица зависимости количества операций от размера поля.

Элементарной операцией является одна попытка постановки квадрата размера и места выбранных перебором.

Размер

Время сек

Операции

Результат(к-во квадратов)

2

-

10

4

3

-

96

6

4

-

56

4

5

-

5578

8

6

-

162

4

7

-

193277

9

8

-

352

4

92

-

891

9

10

-

650

4

11

40

285647660

11

12

-

1080

4

Логарифмический график

ф

фыв

Ф

Простым числам на логарифмическом графике соответствует прямая, что наводит на мысль об экспоненциальной зависимости

Выводы.

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

Приложение А(листинг программы)

#include <iostream>

#include <vector>

#include<time.h>

#define _MAX(x,y) ((x)>(y)?(x):(y))

#define _MIM(x,y) ((x)<(y)?(x):(y))

#define HTR(H)

bool DEBUG = false;

std::vector<std::vector<int> > bestVtx;

std::vector<std::vector<int> > bestMap;

int bestCnt = 0;

long long ops=0;

/*

ESC[ 38;2;⟨r⟩;⟨g⟩;⟨b⟩ m Select RGB foreground color

ESC[ 48;2;⟨r⟩;⟨g⟩;⟨b⟩ m Select RGB background color

*/

std::vector<int> fct(int n){

std::vector<int> vec;

for(int i = 1; i <= n; ++i)

{

if(n % i == 0)

vec.push_back(i);

}

return vec;

}

void drawMap(std::vector<std::vector<int> > map){

for(auto it = map.begin();it!=map.end();it++){

for(auto it1=it->begin();it1!=it->end();it1++){

std::cout<<(char)(*it1<=9?*it1 + '0':*it1-10 + 'a');

// std::cout<<(char)(*it1 + '0');

}

std::cout<<std::endl;

}

}

void iterate(int n, std::vector<std::vector<int> > vtx, std::vector<std::vector<int> > map, int cnt = 1, int initSize = 0, int cap = 0) {

if(bestCnt && cnt>=bestCnt)return;

if (cap==0)cap = n * n;//Capacity

//drawMap(map);

for (int size = initSize > 0 ? initSize : n - 1; size >= 1; size--) {//For all sizes size

auto vec = fct(n);

if(vec.size()>2)size = n/vec[1];//Check for prime factors

for (int i = 0; i <= n - size; i++) {//Starting with each row i

int occCol = -1;

for (int col = 0; col < n; col++) {//Through whole horizontal lane col:0..n

for (int j = i; j < i + size; j++) {//For its full width(vertical) j:0..size ROW - J

if (map[j][col] != 0) {

occCol = col;

}

ops++;

}

if (col - occCol == size) {//insert shape

auto newVtx = vtx;

std::vector<int> curVtx;

curVtx.push_back(i);

curVtx.push_back(occCol + 1);

curVtx.push_back(size);

if(DEBUG){

for(int foo = 0;foo<cnt;foo++){

std::cout <<"\t";

}

std::cout<<curVtx[0]+1<<" "<<curVtx[1]+1<<" "<<curVtx[2]<<std::endl;

}

newVtx.push_back(curVtx);

auto newMap = map;

for (int j = i; j < i + size; j++) {//Row

for (int k = occCol + 1; k <= col; k++) {//Col

newMap[j][k] = cnt;

}

}

//drawMap(newMap);

if (cap - size * size > 0)iterate(n, newVtx, newMap, cnt + 1, size, cap - size * size);

else {//iterations are done

if (bestCnt == 0 || cnt < bestCnt) {

bestCnt = cnt;

bestMap = newMap;

bestVtx = newVtx;

if(DEBUG){

for(int foo = 0;foo<cnt;foo++){

std::cout <<"\t";

}

std::cout<<"New Best"<<std::endl;

}

}

return;

}

if(vec.size()>2&&bestCnt == vec[1]*vec[1])return;//Check for prime factors

if(size==1)return;

}else if(col - occCol == 2*size)return;

}

}

}

}

void backtrack(int n) {

std::vector<std::vector<int> > map;

std::vector<std::vector<int> > vtx;

bestCnt = 0;

for (int i = 0; i < n; i++) {

std::vector<int> tmp(n, 0);

map.push_back(tmp);

}

if(DEBUG)std::cout <<"Interresults:"<< std::endl;

iterate(n,vtx,map);

std::cout << std::endl;

}

int main() {

int n=5;

for(int n = 12; n<14; n++) {

std::cout << "Here we go!\t\t" << n << std::endl;

auto tim = time(0);

backtrack(n);

tim = time(0) - tim;

std::cout << "time:\t\t\t" << tim << std::endl;

std::cout << "num of sqares\t" << bestCnt << std::endl;

std::cout << "num of ops\t\t" << ops << std::endl;

//std::cout << bestVtx[0][0]<<bestVtx[0][1]<<bestVtx[0][2] << std::endl;

for(auto it = bestVtx.begin();it!=bestVtx.end();it++){

std::cout<<(*it)[0]+1<<" "<<(*it)[1]+1<<" "<<(*it)[2]<<std::endl;

}

std::cout << std::endl;

drawMap(bestMap);

bestCnt = 0;

ops = 0;

std::cout <<"======================================="<< std::endl;

}

return 0;

}

Соседние файлы в папке 1_Backtracking