Опубликован: 14.12.2010 | Уровень: для всех | Доступ: платный
Лекция 19:

Рекурсивные алгоритмы и функции

< Лекция 18 || Лекция 19: 1234 || Лекция 20 >

Задание 5

  1. Подсчитайте количество рекурсивных вызовов функции TRY() в зависимости от величины допустимого веса рюкзака, начиная от 10 до 120 с шагом в 10 условных единиц. Постройте график полученной зависимости (в MS EXCEL или в MATLAB).
  2. Рекурсивную функцию TRY() расположите в отдельном файле с именем compX.c, где Х – номер компьютера, на котором выполняется лабораторная работа.
  3. Сформирйте массив данных о предметах по случайному равномерному закону из интервала [10; 20*Х], где Х – номер компьютера, на котором выполняется лабораторная работа. Размер массива выберите случайно из следующего интервала натуральных чисел: [10; 18].
  4. В программу внесите изменения, которые бы позволили определить номера предметов, отобранные в рюкзак. Соответственно укажите вес и стоимость каждого предмета.

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

Суть алгоритма быстрой сортировки заключается в следующем. Выбираем наугад какой-либо элемент х исходного массива. Будем просматривать слева наш массив до тех пор, пока не встретим элемент ai > x, затем будем просматривать массив справа, пока не встретим a_i \leqslant х. Поменяем местами эти два элемента и продолжим процесс просмотра и обмена до тех пор, пока оба просмотра не встретятся. В результате исходный массив окажется разбитым на две части, левая часть будет содержать элементы меньше или равные х, а правая часть – элементы больше х. Применив эту процедуру разделения к левой и правой частям массива от точки встречи, получим четыре части и т. д., пока в каждой части окажется только один элемент [18.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. 4Размерность массива, подлежащего сортировке, и данные массива задайте случайно из интервалов [10; 100] и [10Х; 50Х], где Х – номер компьютера, на котором выполняется лабораторная работа.
  5. Подсчитайте количество рекурсивных вызовов при сортировке массивов, задаваемых случайным образом.

Пример 7. Напишите программу сортировки одномерного массива целых чисел на основе рекурсивного алгоритма слиянием.

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

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

Особенностью алгоритма слиянием является то, что при сортировке требуется временный массив для результата.

Алгоритм сортировки слиянием может быть использован при сортировке внешних данных, например данных, записанных в отдельные файлы [18.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++];
	} // end while

	// Случай, когда в одном из подмассивов остались элементы
	   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++]) ;
} // end merge

// Рекурсивная сортировка подмассивов
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);
		}
	}
} // end sub

// Функция как интерфейс, для удобства вызова программы
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;
	int 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 г.

Сортировка слиянием считается предпочтительной в случае, когда требуется застраховаться от наихудшего случая, возможного при использовании быстрой сортировки [18.12].

Задание 7

  1. Все вспомогательные функции программы разместите во внешние файлы с именами compX_1.c, compX_2.c и т. д., где Х – номер компьютера, на котором выполняется лабораторная работа (1, 2, ...). Протестируйте программу на контрольном примере. Подсчитайте количество рекурсивных вызовов.
  2. Подготовьте два не отсортированных массива и произведите их слияние в один отсортированный массив. Данные массивов задаются случайно по равномерному закону из интервалов [–5Х; 5Х] и [0; 10Х], где Х – номер компьютера, на котором выполняется лабораторная работа (1, 2,...). Размерности массивов должны задаваться с клавиатуры из интервала [10; 20].
  3. Напишите программу по сортировке слиянием двух массивов, расположенных в текстовых файлах (внешняя сортировка слиянием). Результат сортировки разместите также в текстовый файл с именем compX.txt, где Х – номер компьютера, на котором выполняется лабораторная работа.

Контрольные вопросы

  1. Когда следует применять рекурсивные алгоритмы?
  2. Какие известны методы и приемы устранения "хвостовой" рекурсии?
  3. Какие проблемы могут возникать при реализации рекурсивных алгоритмов на электронных вычислительных машинах?
  4. В чем отличие глубины рекурсии от рекурсивного вызова?
  5. Какие задачи в программировании можно назвать, где применение рекурсии оправдано?
< Лекция 18 || Лекция 19: 1234 || Лекция 20 >
Мухаммадюсуф Курбонов
Мухаммадюсуф Курбонов
Александр Соболев
Александр Соболев
Россия
Артем Полутин
Артем Полутин
Россия, Саранск