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

Алгоритмические основы информатики. Учебное пособие

.pdf
Скачиваний:
0
Добавлен:
12.08.2026
Размер:
774 Кб
Скачать

7.Дана квадратная матрица порядка M. Обнулить элементы матрицы, лежащие на главной диагонали и выше неё. Условный оператор не использовать.

8.Дана квадратная матрица порядка M. Обнулить элементы матрицы, лежащие одновременно выше главной диагонали и выше побочной диагонали. Условный оператор не использовать.

9.Дана квадратная матрица порядка M. Обнулить элементы матрицы, лежащие одновременно выше главной диагонали и ниже побочной диагонали. Условный оператор не использовать.

10.Дана квадратная матрица порядка M. Обнулить элементы матрицы, лежащие одновременно ниже главной диагонали (включая эту диагональ) и выше побочной диагонали (также включая эту диагональ). Условный оператор не использовать.

11.В матрице размером N × M найти максимальный среди элементов, лежащих ниже побочной диагонали, и минимальный среди элементов, лежащих выше главной диагонали.

12.Из матрицы размером N × M получить матрицу размером (N –

1)× (M–1) путем удаления из исходной матрицы строки и столбца, на пересечении которых расположен элемент с наибольшим по модулю значением.

13.В матрице размером N × M переставить строки так, чтобы на главной диагонали матрицы были расположены элементы, наибольшие по абсолютной величине.

14.Для матрицы размером N × M вывести на экран все седловые точки. Элемент матрицы называется седловой точкой, если он является наименьшим в своей строке и одновременно наибольшим в своем столбце или наоборот.

15.Определить, является ли заданная квадратная матрица n-го порядка симметричной относительно побочной диагонали.

91

РАЗДЕЛ 3 Рекурсивные алгоритмы

Рекурсия – процесс, при котором функция или метод вызывает сама себя. Это может происходить несколько раз, пока не будет выполнено определенное условие. Рекурсивные алгоритмы – алгоритмы, которые используют рекурсию для решения задачи. Они могут быть очень эффективными, так как позволяют разбить задачу на более мелкие подзадачи, которые затем решаются рекурсивно.

Одним из примеров рекурсивного алгоритма является сортировка слиянием. Этот алгоритм разбивает массив на две части, сортирует каждую из них, а затем объединяет их в один отсортированный массив. Другой пример – это бинарный поиск. В этом алгоритме мы делим массив на две половины и проверяем, есть ли искомый элемент в первой или второй половине. Если элемент не найден, мы продолжаем делить половины и проверять их, пока не найдем искомый элемент.

Рекурсия может быть опасной, если она используется неправильно. Она может привести к бесконечной рекурсии, когда функция вызывает сама себя бесконечно много раз. Чтобы избежать этого, необходимо использовать условия остановки, которые определяют, когда рекурсия должна остановиться.

Одним из примеров рекурсивного алгоритма обработки натурального числового ряда является функция, которая вычисляет факториал числа. Факториал числа – произведение всех натуральных чисел от 1 до этого числа включительно. Например, факториал числа 5 равен

120 (1 * 2 * 3 * 4 * 5).

// Вычислить N! (N - факториал). #include <iostream>

using namespace std;

long double fact(int N)

{

if(N < 0) // если пользователь ввел отрицательное число return 0; // возвращаем ноль

if (N == 0) // если пользователь ввел ноль,

return 1; // возвращаем факториал от нуля - не удивляетесь, но это 1 =)

92

else // Во всех остальных случаях

return N * fact(N - 1); // делаем рекурсию.

}

int main()

{

int N;

setlocale(0,""); // Включаем кириллицу

cout << "chislo dlya vychisleniya faktoriala: "; cin >> N;

cout << "faktorial" << N << " = " << fact(N) << endl << endl; // fact(N) – функция для вычисления факториала.

return 0;

}

Эта программа сначала считывает число n с клавиатуры, а затем вызывает рекурсивную функцию factorial с этим числом в качестве аргумента. Функция factorial вычисляет факториал этого числа и возвращает результат. Затем этот результат выводится на экран.

Функция нахождения n-го члена ряда Фибоначчи рекурсивно выглядит следующим образом:

function fib(n): if n <= 1: return n

else:

return fib(n - 1) + fib(n - 2)

Этот алгоритм рекурсивен, потому что он вызывает сам себя с аргументами n – 1 и n – 2. Когда n равно 1 или 0, этот алгоритм возвращает 1, потому что fib(0) = 1 и fib(1) = 1. Когда n больше 1, алгоритм вызывает fib(n – 1) и fib(n – 2), которые также вызывают fib с меньшими значениями n. В результате алгоритм проходит через ряд рекурсивных вызовов, пока не достигнет базовых случаев fib(0) или fib(1), после чего возвращается обратно вверх по дереву вызовов, собирая результаты промежуточных вычислений.

Пример 1. Строка состоит из клеток, пронумерованных от 1 до n. Состояние клетки можно изменить – если она пуста, поставить в нее

93

шашку (занять ее), иначе убрать из нее шашку (освободить ее). Вначале строка пуста. Нужно занять все клетки, соблюдая следующее правило. Изменение клетки допустимо, если она имеет номер 1 или расположена непосредственно после занятой клетки, имеющей минимальный номер среди занятых клеток.

Пояснения. Косвенная рекурсия. Строка состоит из клеток, пронумерованных от 1 до n. Состояние клетки можно изменить – если она пуста, поставить в нее шашку (занять ее), иначе убрать из нее шашку (освободить ее). Вначале строка пуста. Нужно занять все клетки, соблюдая следующее правило. Изменение клетки допустимо, если она имеет номер 1 или расположена непосредственно после занятой клетки, имеющей минимальный номер среди занятых клеток.

#include <iostream> #include <conio.h> using namespace std; void free_(int n); void fillOnly(int n)

{

if (n==1) cout<<"+1";

else

{

fillOnly(n-1); cout<<"+"<<n; free_(n-1);

}

}

void free_(int n)

{

if (n==1) cout<<"-1";

else

{

fillOnly(n-1); cout<<"-"<<n; free_(n-1);

}

}

94

void fill(int n)

{

if (n==1)

cout<<"+1"; // www.itmathrepetitor.ru else if (n==2)

cout<<"+1+2";

else

{

fillOnly(n-1); cout<<"+"<<n; fill(n-2);

}

}

int main()

{

int n; cout<<"input n: "; cin>>n;

fill(n);

}

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

1.Что такое рекурсия?

2.Как определить, что функция вызывает сама себя рекурсивно?

3.Привести основные преимущества рекурсивных алгоритмов.

4.В каких случаях рекурсивные алгоритмы могут привести к бесконечной рекурсии?

5.Как можно преобразовать рекурсивный алгоритм в итеративный

инаоборот?

Упражнения

1.Написать рекурсивную функцию, которая принимает число n

ивыводит на экран все делители этого числа.

Пример:

Для числа 6 функция должна вывести 1, 2, 3, 6. Для числа 7 функция должна вывести 1, 7.

2.Написать рекурсивную функцию, которая проверяет, является ли заданное число n палиндромом.

3.Написать рекурсивную функцию, которая вычисляет n-ый член ряда Фибоначчи.

95

4. Написать рекурсивную функцию, которая возводит число a в степень n.

Задачи

1.Написать рекурсивную функцию, которая принимает натуральное число n и возвращает сумму его цифр. Например, для числа 123 функция должна вернуть 6.

2.Написать рекурсивную функцию, которая принимает натуральное число n и определяет, является ли это число палиндромом. То есть, число должно читаться одинаково слева направо и справа налево.

3.Написать рекурсивную функцию, которая находит n-ое число Фибоначчи. Напомним, что числа Фибоначчи определяются следую-

щим образом: F(0) = 0, F(1) = 1, F(n) = F(n – 1) + F(n – 2).

4.Написать рекурсивную функцию для нахождения суммы квадратов первых n натуральных чисел.

5.Написать рекурсивную функцию, которая выводит все делители заданного числа n.

6.Написать программу, рисующую снежинку Коха порядка n.

7.Написать программу, рисующую треугольник Серпинского порядка n.

8.Напишите программу построениядраконовой ломаной порядкаn.

9.С помощью рекурсии найти сумму цифр данного натурального

числа.

10.Написать рекурсивную функцию, печатающую цифры натурального числа в а) прямом; б) обратном порядке.

96

РАЗДЕЛ 4 Нестандартная обработка чисел

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

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

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

Вязыке программирования C нет встроенной поддержки работы

сдлинными числами. Однако, можно написать свою реализацию, используя, например, строки или массивы. Вот пример программы, которая выполняет сложение двух длинных чисел:

Исходный код реализации обработки больших чисел на C++ приведен ниже:

#include <iostream> #include <vector> #include <algorithm> #include <iomanip> using namespace std;

//Деление для отрицательных чисел int my_div(int num, int diver) { if ((num < 0) && (num % diver)) return num / diver - 1;

else

return num / diver;

}

//Взятие по модулю для отрицательных чисел int my_mod(int num, int diver) {

if ((num < 0) && (num % diver))

97

return num % diver + diver; else

return num % diver;

}

//Класс "большое число", описывает способ хранения большого числа и

//длинную арифметику class BigNumber { private: vector<int> chunks; int sign;

//Настроиваемые параметры хранения большого числа //Количество символов в строке, которые станут одной "чан-

кой"

static const int BASE = 2;

//Зависит от BASE (BASE10 = 10^BASE), используется при нормализации

static const int BASE10 = 100; //Служебные функции

BigNumber _plus(BigNumber &a); BigNumber _minus(BigNumber &a); void _normalizationSigns(); void _normalizationChunks(); void _resize(int newsize); public:

BigNumber operator + (BigNumber &num); BigNumber operator - (BigNumber &num);

friend ostream & operator << (ostream &os, BigNumber &num); int getBASE() {

return this->BASE;

}

//Конструктор, строку конвертирует в большое число

BigNumber(string str) { int i;

if (BASE != 1) {

//Записываем с конца по BASE символов строки в чанки for (i = str.size() - BASE; i >= BASE - 1; i -= BASE) { chunks.push_back(stoi(str.substr(i, BASE)));

}

}

else {

for (i = str.size() - BASE; i >= BASE; i -= BASE) { chunks.push_back(stoi(str.substr(i, BASE)));

}

}

98

//Дошли до начала строки, тут ищем знак и записываем последнюю чанку

if (str[0] == '-') { sign = -1;

if (i + BASE - 1 != 0) { chunks.push_back(stoi(str.substr(1, i + BASE - 1)));

}

}

else { sign = 1;

chunks.push_back(stoi(str.substr(0, i+BASE)));

}

}

//Конструктор без аргументов - "пустое" положительное число

BigNumber() { sign = 1;

}

}; //Изменение размера массива с чанками

void BigNumber::_resize(int newSize) { chunks.resize(newSize);

}

/*

*Функция нормализует большое число так, чтобы

*во всех чанках лежали BASE-разрядные числа

*/

void BigNumber::_normalizationChunks() {

int over = 0; //"Лишнее", которое будет кочевать в следую-

щие чанки

for (int i = 0; i < chunks.size() - 1; i++) {

chunks[i] += over; //Прибавляем привесок от прошлых чанок over = my_div(chunks[i], BASE10); //Считаем перевес в теку-

щей чанке

chunks[i] = my_mod(chunks[i], BASE10); //Выравниваем чанку по базе

}

//Прибавляем перевес к последней чанке chunks[chunks.size() - 1] += over; //Обрабатываем перевес в последней чанке if (chunks[chunks.size() - 1] / BASE10) {

over = my_div(chunks[chunks.size() - 1], BASE10); chunks[chunks.size() - 1] = my_mod(chunks[chunks.size() -

1], BASE10);

chunks.push_back(over); //Создаем нову чанку с остатками

}

99

return;

}

//Функция нормализует большое число для печати так, //чтобы все чанки были положительными, но sign = -1(если

число отрицательное)

void BigNumber::_normalizationSigns() { //Если в последней чанке отрицательное число if (chunks[chunks.size() - 1] < 0) {

sign = -sign; //Меняем знак числа

chunks[0] = BASE10 - chunks[0]; //Нормализуем первую чанку for (int i = 1; i < chunks.size(); i++) {

chunks[i] = (BASE10 - chunks[i] - 1) % BASE10; //Нормализуем ост. чанки

}

}

//Убираем из числа нулевые чанки int i = chunks.size() - 1; while (chunks[i] == 0) {

if (i == 0) { sign = 1; return;

}

chunks.pop_back(); i--;

}

return;

}

//Функция сложения

BigNumber BigNumber::_plus(BigNumber &num) { BigNumber res;

res.sign = this->sign;

for (int i = 0; i < this->chunks.size(); i++) { res.chunks.push_back(this->chunks[i] + num.chunks[i]);

}

return res;

}

//Функция вычитания

BigNumber BigNumber::_minus(BigNumber &num) { BigNumber res;

res.sign = this->sign;

for (int i = 0; i < this->chunks.size(); i++) { res.chunks.push_back(this->chunks[i] - num.chunks[i]);

}

return res;

}

100

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