Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
BOOK_С_INTUIT.doc
Скачиваний:
4
Добавлен:
19.09.2019
Размер:
7.91 Mб
Скачать

Переместить (a, b);

hanoi(n–1, C, B, A);

где hanoi() – имя рекурсивной функции [10].

Пример промежуточного положения Ханойской башни при n = 4 показан на рис. 18.4.

Р ис. 18.4. Промежуточное положение Ханойской башни при n = 4

Число элементарных перемещений равно , где n – количество исходных дисков [10]. С увеличением n число перемещений быстро нарастает.

На рис. 18.5 приведена зависимость элементарных перемещений от числа дисков.

Р ис. 18.5. Зависимость элементарных перемещений от числа дисков

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

В связи со степенным ростом числа рекурсивных вызовов в программе ограничимся величиной n = 15.

Программный код решения примера

#include <stdio.h>

#include <conio.h>

#include <math.h>

// Пртотип рекурсивной функции

void hanoi(int n,char a, char b, char c);

int main(void) {

char a = 'A'; // исходное основание

char b = 'B'; // конечное основание

char c = 'C'; // промежуточное основание

double n; // число дисков

char str[81] = "15"; // инициализация строки

puts("\n Hanoi Tower; A - starting; B - end; C - intermediate");

while (0.01) { // 0.01 - как истинное значение

printf("\n Enter the number of disks: ");

scanf_s(" %s",str, 80);

n = atof(str);

if ( n <= 0 || ceil(n) != n) {

printf("\n Error input. Press any key: ");

_getch();

continue;

}

else if (n > 15.0) {

printf("\n Very large number. Press any key: ");

_getch();

continue;

}

else

break;

}

puts("\n Elementary transferences:");

hanoi((int)n, a, b, c);

printf("\n\n Press any key: ");

_getch();

return 0;

}

// Определение рекурсивной функции

void hanoi(int num, char a, char b, char c)

{

if(num > 0) {

hanoi(num-1,a,c,b);

printf("\t%c --> %c\n",a,b);

hanoi(num-1,c,b,a);

}

}

Результат выполнения программы при n = 5 показан на рис. 18.6.

Р ис. 18.6. Перемещения дисков в Ханойской башне при n = 5

Задание 4

  1. Определите тип рекурсии, используемой в программе.

  2. Для случая, когда не все элементарные перемещения выводятся на экран дисплея, предусмотрите вывод результата программы в текстовый файл с именем compX.txt, где X – номер компьютера, на котором выполняется лабораторная работа.

  3. Программным путем выполните подсчет общего количества обращений к рекурсивной функции. Сделайте также вывод порядкового номера перемещения на экран дисплея. Порядковый номер расположите слева от элементарного перемещения. Постройте (в MS Excel или в MATLAB) зависимость количества обращений к рекурсивной функции от заданного числа дисков.

  4. Измените аргументы рекурсивной функции: включите переменные типа int для определения имени (нумерации) основания.

  5. Измените условие задачи «Ханойская башня». Сначала исходным основанием считайте В, конечным – С, промежуточным – А. Затем примите за исходное основание С, промежуточное – В, конечное – А.

Пример 5. «Задача о рюкзаке». Есть 10 предметов, о которых известны их массса и стоимость. Требуется поместить в рюкзак предметы таким образом, чтобы они не превысили допустимую массу для рюкзака при максимальной стоимости выбранных предметов. Исходные параметры модели – характеристики предметов приведены в табл.18.1 [11].

Таблица 18.1

Характеристики предметов

№ п/п

1

2

3

4

5

6

7

8

9

10

Масса

10

11

12

13

14

15

16

17

18

19

Стоимость

18

20

17

19

25

21

27

23

25

24

Данная задача широко известна [9–11], она еще называется задачей о ранце, или в английском произношении –  «knapsack problem». Предполагается, что один и тот же предмет не может быть взят несколько раз.

Для решения поставленной задачи используем рекурсивный алгоритм, описанный в работе [11], где также приводится фрагмент программы на языке программирования MODULA – 2.

Программный код решения примера

#include <stdio.h>

#include <conio.h>

#include <locale.h>

const int limW = 20; // предельная масса выбранных предметов

enum {N = 10}; // количество предметов

typedef struct

{

int weight; // масса или размер предметов

int value; // стоимость или ценность предметов

} object;

// Формирование структурного типа с параметрами модели

object obj[] = {10,18, 11,20, 12,17, 13,19, 14,25, 15,21, 16,27, 17,23, 18,25, 19,24};

int maxv; // для инициализации стоимости предметов

// Рекурсивная функция

int TRY (int i, int tw, int av) {

// Попытка включения предмета

if (tw + obj[i].weight <= limW)

if (i < N-1)

TRY(i+1, tw + obj[i].weight, av );

else if (av > maxv)

maxv = av;

// Попытка исключения предмета

if (av > maxv + obj[i].value)

if (i < N-1)

TRY(i+1, tw, av - obj[i].value);

else

maxv = av - obj[i].value;

return maxv;

}

// Главная функция программы

int main (void) {

int i, price;

int sumw = 0;

int sumv = 0;

setlocale(LC_ALL, "rus");

maxv = obj[0].value; // инициализации стоимости предметов

puts("\n\t\t\tЗАДАЧА О РЮКЗАКЕ");

puts("\t\t Характеристика предметов");

for (i = 0; i < (4*N + 12); i++)

printf("%s", "_");

printf("\n\n %12s", "Масса:");

for (i = 0; i < N; i++) {

sumw += obj[i].weight;

printf(" %3d", obj[i].weight); }

printf("\n %12s", "Стоимость:");

for (i = 0; i < N; i++) {

sumv += obj[i].value;

printf(" %3d", obj[i].value); }

puts("");

for (i = 0; i < (4*N + 12); i++)

printf("%s", "_");

printf("\n\n %32s: %d\n", "Общая масса всех предметов", sumw);

printf(" %32s: %d\n", "Общая стоимость всех предметов", sumv);

printf(" %32s: %d\n ", "Допустимая масса рюкзака", limW);

for (i = 0; i < (4*N + 12); i++)

printf("%s", "_");

// Вызов рекурсивной функции с начальными параметрами

price = TRY(0,0,sumv);

printf("\n\n Стоимость выбранных предметов: %d\n ", price );

for (i = 0; i < (4*N + 12); i++)

printf("%s", "_");

printf("\n\n ... Нажмите любую клавишу: ");

_getch();

return 0;

}

Пример выполнения программы представлен на рис. 18.7.

Р ис. 18.7. Пример выполнения программы решения задачи о рюкзаке

Представленная программа основана на следующем алгоритме [11]. В рекурсивной функции TRY() определены две ситуации для выбора предмета в рюкзак. Если речь идет о включении, то объект (предмет со своей массой и стоимостью) можно включить в выборку для укладки в рюкзак, если он подходит по массовым ограничениям. Если он не подходит, то попытки добавить еще один объект в текущую выборку можно прекратить. Когда же речь идет об исключении, то критерием приемлемости, т. е. возможности продолжения построения текущей выборки, будет то, что после данного исключения общая ценность (стоимость) будет не меньше полученного до этого момента оптимума. В данной программе реализована схема с возвратами, использующая некоторые ограничения для уменьшения роста потенциального дерева поиска, называется она алгоритмом ветвей и границ [11].

Задание 5

  1. Подсчитайте количество рекурсивных вызовов функции TRY() в зависимости от величины допустимой массы рюкзака, начиная от 10 и до 120 с шагом в 10 условных единиц. Постройте график полученной зависимости (в MS EXCEL или в MATLAB).

  2. Рекурсивную функцию TRY() расположите в отдельном файле с именем compX.c, где Х – номер компьютера, на котором выполняется лабораторная работа.

  3. Сформирйте массив данных о предметах по случайному равномерному закону из интервала [10; 20Х], где Х – номер компьютера, на котором выполняется лабораторная работа. Размер массива выберите случайно из интервала натуральных чисел [10; 18].

  4. В программу внесите изменения, которые позволили бы определить номера предметов, отобранных в рюкзак. Соответственно укажите массу и стоимость каждого предмета.

Пример 6. Написать программу сортировки одномерного массива целых чисел на основе рекурсивного алгоритма быстрой сортировки Хоара.

Суть алгоритма быстрой сортировки заключается в следующем. Выбираем наугад какой-либо элемент х исходного массива. Будем просматривать слева наш массив до тех пор, пока не встретим элемент ai > x, затем будем просматривать массив справа, пока не встретим ai  х. Поменяем местами эти два элемента и продолжим процесс просмотра и обмена до тех пор, пока оба просмотра не встретятся. В результате исходный массив окажется разбитым на две части, левая будет содержать элементы меньше или равные х, а правая – элементы больше х. Применив процедуру разделения к левой и правой частям массива от точки встречи, получим четыре части и т. д., пока в каждой из них не окажется только один элемент [12]. В качестве граничного элемента х для разделения обычно выбирают средний элемент массива.

Программный код решения примера

/* Быстрая сортировка Хоара.

* Рекурсивный вариант.

*/

#include <stdio.h>

#include <conio.h>

// Массив, подлежащий сортировке по возрастанию

int A[] = {1, 5, 7, -5, 0, 12, 8, 5, 4, -10, 3, 6, 18};

// Прототип рекурсивной функции

void Quick_Sort(int A[], int L, int R);

int main (void) {

int i;

int N;

N = sizeof(A)/sizeof(A[0]);

puts("\n\t\t QUICK SORT");

printf("\n\t The original array of size %d:\n ", N);

for (i = 0; i < N; i++)

printf(" %3d ", A[i]);

// Вызов рекурсивной функции сортировки

Quick_Sort(A, 0, N-1);

printf("\n\n\t The sorting array:\n ");

for (i = 0; i < N; i++)

printf(" %3d ", A[i]);

printf("\n\n ... Press any key: ");

_getch();

return 0;

}

// Определение рекурсивной функции

void Quick_Sort(int A[], int L, int R) {

int i, j, k;

int x; // граничный элемент для разделения массива

i = L;

j = R;

x = A[(int)(L + R)/2];

do { while (A[i] < x)

i++;

while (x < A[j])

j--;

if (i <= j) {

k = A[i]; A[i] = A[j]; A[j] = k;

i++; j--;

}

} while (i < j);

if (L < j)

Quick_Sort(A, L, j);

if (i < R)

Quick_Sort(A, i, R);

}

П ример выполнения программы показан на рис. 18.8.

Рис. 18.8. Пример быстрой сортировки одномерного массива

Задание 6

  1. В программу внесите изменения для сортировки вещественных данных.

  2. В программу внесите изменения для сортировки массива по убыванию.

  3. Рекурсивную функцию программы разместите во внешнем файле с именем compX.c, где Х – номер компьютера, на котором выполняется лабораторная работа.

  4. Размерность массива, подлежащего сортировке, и данные массива задайте случайно из интервалов [10; 100] и [10Х; 50Х], где Х – номер компьютера, на котором выполняется лабораторная работа.

  5. Подсчитайте количество рекурсивных вызовов при сортировке массивов, задаваемых случайным образом.

Пример 7. Написать программу сортировки одномерного массива целых чисел на основе рекурсивного алгоритма слиянием. При алгоритме слиянием сортировку производят по частям, а затем отдельные части соединяют в единое целое [5].

Выберем следующую схему разбиения данных. Будем разрезать массив данных на две равные части, образуя два подмассива. Затем сортировка слиянием рекурсивно вызывает себя для каждого из двух подмассивов. Конечным шагом является слияние двух сортированных подмассивов в один сортированный массив [13]. Особенностью алгоритма слиянием является то, что при сортировке требуется временный массив для результата. Данный алгоритм может быть использован при сортировке внешних данных, например, записанных в отдельные файлы [5].

Программный код решения примера

#include <stdio.h>

#include <conio.h>

#include <stdlib.h>

// Заданный произвольный числовой массив

int A[] = {9, 3, 4, 5, 8, 1, 0, 3, 2, -3, 12, 10, -7, 8};

// Основная функция сортировки, merge - сливать, соединять

void merge (int arr[],int *temp,int start,int mid,int end)

{

int i = 0; // для временного массива

int i_lower = start;

int i_upper = mid + 1;

int i_arr = start;

// Пока ни один из подмассивов не пуст

while ( (i_lower <= mid) && (i_upper <= end) ) {

if (arr[i_lower] < arr[i_upper])

temp[i++] = arr[i_lower++];

else

temp[i++] = arr[i_upper++];

}

// Случай, когда в одном из подмассивов остались элементы

if (i_lower <= mid) {

// assert (i_upper > end);

for ( ; i_lower <= mid; temp[i++] = arr[i_lower++]);

}

else

for ( ; i_upper <= end; temp[i++] = arr[i_upper++]);

// Когда размер массива равен end - start + 1

if (i == end - start + 1)

for ( i = 0; i_arr <= end; arr[i_arr++] = temp[i++]) ;

}

// Рекурсивная сортировка подмассивов

void sub (int arr[], int *temp, int head, int tail ) {

// head - голова

// tail - хвост

int mid;

if (head >= tail)

return;

if (tail > head) {

mid = (head + tail) / 2; // средняя точка массива

// assert ( (mid >= head) && (mid <= tail) );

if (mid >= head && mid <= tail) {

// Сортировка подмассивов

sub (arr, temp, head, mid);

sub (arr, temp, mid+1, tail);

merge (arr, temp, head, mid, tail); // объединение результатов

}

}

}

// Функция как интерфейс для удобства вызова программы

void merge_sort (int arr[], int size) {

int head = 0;

int tail = size - 1;

int *temp;

temp = (int *) malloc (size*sizeof(int));

sub (arr, temp, head, tail);

free(temp);

}

int main (void) {

int i, N;

N = sizeof(A)/sizeof(A[0]);

// Вывод на консоль исходного массива

printf("\n\t The original array of size %d:\n ", N);

for (i = 0; i < N; i++)

printf(" %2d", A[i]);

merge_sort (A, N);// вызов функции для сортировки массива

// Вывод на консоль отсортированного массива

printf("\n\n\t After sorting:\n ");

for (i = 0; i < N; i++)

printf(" %2d", A[i]);

printf("\n\n ... Press any key: ");

_getch();

return 0; }

Р езультат выполнения программы приведен на рис. 18.9.

Рис. 18.9. Пример сортировки слиянием одномерного массива

В алгоритме сортировки слиянием можно выделить три этапа.

  1. Сортируемый массив разбивается на две половины примерно одинакового размера.

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

  3. Два упорядоченных массива половинного размера соединяются в один.

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

Алгоритм сортировки слиянием был предложен Джоном фон Нейманом в 1945 г. Такая сортировка считается предпочтительной в случае, когда требуется застраховаться от наихудшего случая, возможного при использовании быстрой сортировки [12].

Задание 7

  1. Все вспомогательные функции программы разместите во внешние файлы с именами compX_1.c, compX_2.c и т. д., где Х – номер компьютера, на котором выполняется лабораторная работа. Протестируйте программу на примере и подсчитайте количество рекурсивных вызовов.

  2. Подготовьте два неотсортированных массива и произведите их слияние в один отсортированный массив. Данные массивов задаются случайно по равномерному закону из интервалов [–5Х; 5Х] и [0; 10Х], где Х – номер компьютера, на котором выполняется лабораторная работа. Размерности массивов должны задаваться с клавиатуры.

  3. Напишите программу по сортировке слиянием двух массивов, расположенных в текстовых файлах (внешняя сортировка слиянием). Результат сортировки разместите также в текстовый файл с именем compX.txt, где Х – номер компьютера, на котором выполняется лабораторная работа.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]