Рекурсия

Понятие рекурсии. Моделирование арифметических операций. Решение первой задачи Флавия в общем случае. Решение второй задачи Флавия в общем случае. Генераторы перестановок. Метод вертикальной прогонки и последовательного замещения. Вычисления факториала.

Рубрика Экономико-математическое моделирование
Вид дипломная работа
Язык русский
Дата добавления 26.03.2009
Размер файла 394,1 K

Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже

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

Решение. Пусть рекурсивная функция isprim(n) является решением задачи и

Дальнейшие рассуждения являются иллюстрацией использования классического приема “вложения” (Дж. Маккарти, 1962). Перейдем к рассмотрению следующей обобщенной задачи. Пусть a, b, n натуральные числа и 2abn. Верно ли, что заданное n не делится ни на одно целое из отрезка [a, b]? Пусть эту задачу решает функция

Ниже приведено три рекурсивных варианта реализации этой функции. По первому из них проверке на делитель n последовательно подвергаются числа: a, a+1, … , b; по второму эти же числа в обратном порядке и, наконец, по третьему a, b, a+1, b1, … .

Контрольные примеры.

Далее, натуральное число n2 является простым, если оно не имеет делителей на отрезке , поэтому характеристическая функция isprim(n) через функцию nodiv(n,a,b) может быть выражена так:

Контрольные примеры.

Задача 23. Составить программу-функцию pi(x), которая подсчитывает количество простых чисел, не превосходящих заданное число x.

Решение. С помощью функций nodiv() или isprim() рекурсивный вариант функции pi(x) строится достаточно просто, исходя из такого декомпозиционного утверждения. Количество простых чисел, не превосходящих x, составляется из количества простых чисел меньших или равных x1 плюс значение функции isprim(x). Поэтому:

Контрольные примеры.

Задача 24. Составить программу pn(n), которая вычисляет n-ое простое число (n - натуральное).

Решение. Предварительно напишем рекурсивную подпрограмму-функ-цию minp(m) нахождения наименьшего простого числа, большего или равного m (m - натуральное число). Сделать это можно, например, так:

Контрольные примеры.

Тогда искомая функция pn(n) может быть определена так:

Контрольные примеры.

3.19 Схема Горнера

Задача 25. Составить программу-функцию вычисления значений многочлена по схеме Горнера.

Решение. Пусть f(x) многочлен с вещественными или комплексными коэффициентами и v - вектор этих коэффициентов:

(8)

(9)

Вычисление f(x) в точке x по схеме Горнера проводится от самых внутренних скобок и далее в соответствии с представлением:

Отсюда ясно, как записать рекурсивный и не рекурсивный варианты алгоритмов вычисления f(x). Нас интересует только первый из них. Параметрами задачи можно считать вектор v и точку a для вычисления f(a). Тривиальный случай - многочлен нулевой степени: f(a) = a0. Декомпозиция получается из указанной выше расстановки скобок в записи f(x). Соответствующая программа-функция выглядит, например, так.

Контрольные примеры.

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

3.20 Деление многочлена на двучлен

Задача 26. Составить программу-функцию, по которой для многочлена (8) с коэффициентами, составляющими вектор (9) и двучлена xx0 (x0 вещественное или комплексное число), вычисляется вектор c:

такой, что

и

Решение. Если в соотношении, связывающем многочлены f(x) и q(x), осуществить приведение к общему знаменателю и приравнивание коэффициентов при одинаковых степенях x, то для определения компонент вектора c получим следующую систему линейных уравнений.

Отсюда вытекает, что c = qu(v,x0), где рекурсивное определение функции qu() может выглядеть, например, так.

Контрольный пример.

Иными словами:

3.21 Произведение двух многочленов

Задача 27. Пусть коэффициенты многочленов fn(x) и gm(x) заданы компонентами векторов v и w:

(10)

(11)

Составить рекурсивную программу-функцию вычисляющую коэффициенты многочлена hn+m(x)=fn(x)gm(x) и возвращающую их в виде компонентов вектора:

Решение. Поскольку

где

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

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

Контрольный пример.

3.22 Произведение биномов

Задача 28. Составить программу-функцию возвращающую коэффициенты многочлена g(x), который получается в результате перемножения биномов (xvk): (k=0,1,…n1; n1):

Решение. Будем считать, что свободные члены биномов заданы в виде компонентов некоторого вектора v: а результат вычислений должен возвращаться также в виде вектора. Поскольку

то несложно организовать рекурсию по количеству перемножаемых биномов. Соответствующая программа функция могла бы выглядеть так:

Контрольный пример.

3.23 Деление многочлена на многочлен

Задача 29. Пусть выполнены соотношения (10)(11), то есть многочлены fn(x) и gm(x) степеней n и m (n,m0) соответственно заданы своими коэффициентами в виде компонентов векторов v и w. Пусть, далее, при m=0, то есть если gm(x) есть константа, gm(x)0. Составить программу-функцию нахождения частного q(x) и остатка r(x) при делении fn(x) на gm(x):

fn(x)=q(x)gm(x)+r(x), (12)

где степень r(x) меньше m (при m=0 r(x)0).

Решение. Представление (12) единственно. В дальнейшем нам удобно считать, что степень r(x) равна m1 с возможно нулевыми коэффициентами при старших степенях x.

При m=0 решение задачи очевидно:

q(x)=fn(x)/w0,r(x)=0. (13)

Далее, при n<=m имеем

(14)

Пусть n>m и q1(x) и r1(x) частное и остаток от деления (fn(x)an-1)/x на gm(x):

Из этого соотношения вытекает, что

Вместе с (12) это дает:

(15)

Если соотношения (13)(14) рассматривать в качестве базы рекурсии, то равенства (15) определяют декомпозицию. Считаем, что в результате вычислений должен быть сформирован и возвращен составной вектор qr=[q r]T, где q и r соответственно векторы коэффициентов многочленов q(x) и r(x). Соответствующая программа-функция, реализующая эти идеи, выглядит так:

Контрольные примеры.

Замечание. Функция poldiv() возвращает составной вектор [q r]T , в котором второй компонент-вектор r может содержать “ведущие” нули. При необходимости эти нули можно погасить, то есть выделить из r подвектор, начинающийся с первого из ненулевых компонент r. Сделать это можно, например, с помощью приведенной ниже рекурсивной функции nulera(u). Если все компоненты u равны нулю, то nulera(u) возвращает 0.

3.24 Разбиение целого на части

Задача 30. Составить программу-функцию подсчета количества (m) разбиений натурального числа m, то есть его представления в виде суммы натуральных чисел.

Решение. Пусть, например, m=6. Тогда разбиениями m являются его представления в виде:

6;

5+1;

4+2, 4+1+1;

3+3, 3+2+1, 3+1+1+1;

2+2+2, 2+2+1+1, 2+1+1+1+1;

1+1+1+1+1+1;

Таким образом, (m)=11 и понятно, что простым перебором возможных случаев уже при m>10 справиться с задачей достаточно сложно.

Для решения исходной задачи перейдем к рассмотрению обобщенной задачи. Составить программу-функцию подсчета количества P(m,n) разбиений натурального числа m со слагаемыми, не превосходящими n. Ясно, что (m)=P(m,m). Поэтому, достаточно научиться вычислять значения функции P(m,n). Но для неё нетрудно выделить рекурсивную базу и указать правило декомпозиции. Сделать это можно исходя из следующих вполне прозрачных свойств этой функции.

1. P(m,1)=1 существует только одно разбиение m, в котором слагаемые не превосходят единицы, а именно: m=1+1+…+1.

2. P(1,n)=1 число единица имеет только одно представление при любом n.

3. P(m,n)=P(m,m) при n>m слагаемые, большие m в разбиениях отсутствуют.

4. P(m,m)=P(m,m1)+1 существует лишь одно разбиение со слагаемым равным m. Все иные разбиения имеют слагаемые не превосходящие m1.

5. P(m,n)=P(m,n1)+P(mn,n) (n<m). Обоснование этого соотношения проводится так. Все разбиения m на сумму слагаемых, не превосходящих n можно разбить на два непересекающихся класса: суммы, не содержащие n в качестве слагаемого и суммы, содержащие такое n. Количество элементов первого класса равно P(m,n1), а количество элементов второго класса подсчитаем так. Без учета слагаемого n суммы элементов второго класса равны mn. Значит их общее количество равно P(mn,n) и, следовательно, общее количество элементов второго класса также равно этой величине. Тем самым свойство 5 установлено.

Первые два свойства определяют базу рекурсии, а три следующие задают декомпозицию. Строго в соответствии с этими утверждениями и составлена рекурсивная программа-функция deco(n,m) для вычисления величины P(m,n).

Контрольные примеры.

3.25 Максимальный и минимальный элементы

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

Рекурсивная функция maxv(v) находит максимальный элемент v ровно за n1 операцию сравнения. Декомпозиция реализуется по длине вектора и опирается на такое утверждение. Решить исходную задачу для v это то же самое, что решить её для подвектора, получаемого из v удалением его первого компонента, сравнить полученное значение с удаленным элементом и, наконец, выбрать не меньшее из них.

Контрольные примеры.

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

Аналогично строится и функция minv(v) нахождения за n1 операцию сравнения минимального по значению элемента массива. Нетрудно написать рекурсивную функцию одновременного поиска минимального и максимального значений v за 2n2 операции сравнения. Выглядеть она может, например, так:

Контрольные примеры.

Однако одновременное нахождение максимального и минимального значений v может быть реализовано гораздо эффективней. Соответствующий результат зафиксирован в следующей задаче.

Задача 31. Написать рекурсивную программу-функцию, находящую одновременно максимальный и минимальный элементы массива v за операций сравнения.

Решение. Если длина вектора v равна единице, то решением задачи можно считать вектор При длине вектора v, равной двум, решение находится за одно сравнение. Пусть длина вектора больше двух. Тогда из двух первых компонентов непосредственно найдем наибольшее и наименьшее (одно сравнение), решим исходную задачу для подвектора, получающегося из v удалением этих компонентов и, наконец, осуществим правку полученного решения для подвектора за счет первых двух компонентов (два сравнения). Именно эти идеи и реализуются рекурсивной функцией minmax():

Контрольный пример.

Подсчитаем теперь S общее количество сравнений:

Тем самым решение задачи завершено полностью.

В следующем варианте minmax() функции minmax1() используются три вспомогательных параметра: a, b, i. Первые два из них служат для фиксации текущих значений минимального и максимального элементов массива, а последний для нумерации рекурсивных обращений. Как и в предыдущем случае, каждый рекурсивный вызов уменьшает обрабатываемый массив на два элемента. Но здесь, в отличие от minmax(), они удаляются с разных его концов. Использование вспомогательной функции mima(v) избавляет нас от сложного обращения к minmax1().

Контрольный пример.

Задача 32. Написать рекурсивную программу-функцию, находящую одновременно позиции максимального и минимального элементов массива v за операций сравнения.

Решение. Для решения данной задачи можно использовать тот же самый алгоритм, который реализуется функцией minmax1(), подвергая запоминанию не текущие значения минимального и максимального элементов массива, а их индексы. Соответствующие функции posmima() и pmima() можно записать так:

Контрольный пример.

Замечание. Рекурсивная функция posmima() позволяет реализовать сортировку массива v по такому алгоритму:

Контрольные примеры.

3.26 Абракадабра

Задача 33. Последовательность из латинских букв строится следующим образом. На нулевом шаге она пуста. На каждом последующем шаге последовательность удваивается, то есть приписывается сама к себе, и к ней слева добавляется очередная буква алфавита (a,b,c,…). По заданному числу n определить символ, который стоит на n-ом месте последовательности, получившейся после шага 26.

Решение. Эта задача предлагалась участникам областных туров Всероссийской олимпиады школьников по информатике в 1998 году. Приведем первые шаги формирования последовательности: 0 пустая последовательность, 1 a, 2 baa,3 cbaabaa, 4 dcbaabaacbaabaa, … . Мы имеем явно рекурсивный процесс.

Построим более общую программу-функцию, чем это требуется по условиям задачи. Пусть abra(k,n) n-ая буква в последовательности, полученной на шаге k (k=1..26). Тогда ясно, что abra(k,1) равно k-ой букве латинского алфавита. Этот факт можно взять в качестве базы рекурсии, а функцию для определения этой буквы записать так:

Декомпозицию удобно организовать по k, проводя “раскрутку” последовательности по шагам в обратном направлении. Это приводит к следующей функции:

Контрольные примеры.

3.27 Вложенные многоугольники

Задача 34. Вложенные квадраты. Пусть на плоскости первый квадрат задан точками: M1(-1,-1), M2(-1,1), M3(1,1), M4(1,-1). Второй квадрат строится так, что вершины первого квадрата являются серединами его сторон и т.д. Составить рекурсивную программу-функцию, которая по заданному натуральному n строит систему из 2n вложенных друг в друга описанным образом квадратов, точнее создает массив точек, последовательное соединение которых на плоскости отрезками и формирует эту систему (см. рис. 3.5).

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

Обратите внимание, что в beg каждый из квадратов задается не четырьмя, а пятью точками. При этом первая и последняя из них совпадают. Это поможет впоследствии правильной прорисовке квадратов.

Далее, декомпозиция определяется таким утверждением. Если уже построена матрица ma точек для 2(n1) вложенных квадратов, то, пополнив её точками матрицы , получим матрицу точек для 2n таких квадратов. Соответствующая рекурсивная функция, базирующаяся на этих фактах, может выглядеть так:

Контрольный пример. Результат вычислений представлен на рис. 3.5 c неодинаковым масштабом по осям.

Рис. 3.5. Вложенные 2n квадратов (n=4)

Задача 35. Вложенные многоугольники. Пусть на плоскости задан правильный n-угольник, вписанный в единичную окружность одна из вершин которого имеет координаты (cos(), sin()), где некоторый угол. Второй правильный n-угольник строится так, что его вершины являются серединами сторон первого многоугольника и т.д. Составить рекурсивную программу-функцию, которая по заданному натуральному n строит систему из n вложенных друг в друга описанным образом многоугольников, точнее создает массив точек, последовательное соединение которых на плоскости отрезками и формирует эту систему (см. рис. 3.6).

Решение. Это задача в каком-то смысле является обобщением предыдущей. Здесь выводятся правильные вписанные n-угольники, количество их не обязательно четно и первая из вершин начального n-угольника может лежать в любой точке единичной окружности. Правда, здесь многоугольники не “описываются” один вокруг другого, а “вписываются” друг в друга. Но существа дела это не меняет.

Прежде всего, составим программу, которая формирует массив вершин правильного n-угольника, вписанного в окружность радиуса r и содержащего вершину (rcos() rsin()). Сделать это можно, например, так:

Тогда массив polyone(n,1,) может служить базой рекурсии. Более того, эта функция при правильном выборе r и позволяет получить массив вершин любого из следующих вписанных n-угольников, что помогает организовать и декомпозицию. Если уже построена матрица ma точек для (n1)-го вписанного правильного n-угольника, то, пополнив её точками матрицы:

получим матрицу точек для 2n таких многоугольников. Соответствующая рекурсивная функция, реализующая эти идеи, может выглядеть так:

Контрольный пример. Результат вычислений представлен на рис. 3.6 c неодинаковым масштабом по осям.

Рис. 3.6. k вложенных правильных n-угольников (k=20, n=6)

Несколько слов перед формулировкой новой задачи. В основах анализа [8, с. 1738] операции сложения и умножения над натуральными числами определяются рекурсивным способом, опираясь на аксиомы Пеано “о существовании последующего числа” и “индукции”. Первая из названных аксиом звучит так: “для каждого натурального x имеется и притом только одно натуральное число, называемое его последующим и обозначаемое число x ”. Постараемся промоделировать указанные выше и некоторые иные операции.

Моделирование арифметических операций

Задача 36. Для целых неотрицательных чисел n, m разрешены операции:

нахождения последующего числа (n+1) и предыдущего числа n1 (n>0). Промоделировать с помощью рекурсивных функций операции нахождения суммы (n+m), разности (nm (nm)), умножения (nm), возведения в степень nm (n>0), частного и остатка при делении n на m (n/m).

Решение.

A. Сумма: a+b. Очевидное соотношение

задает одновременно и базу рекурсии и правило декомпозиции. По нему и построена следующая рекурсивная программа-функция:

Контрольный пример:

B. Разность: ab (ab). В данном случае база рекурсии и декомпозиция могут быть извлечены из соотношения:

Соответствующая рекурсивная программа-функция выглядит так.

Контрольный пример:

C. Умножение: ab. Будем считать, что операция сложения уже определена. Тогда соотношение, из которого легко извлекаются база и декомпозиция для данного случая выглядит следующим образом:

Соответствующая рекурсивная программа-функция может быть записана, например, так:

Контрольный пример:

D. Возведение в степень: ab (a0). Будем считать, что операция умножения уже определена. Тогда:

и рекурсивная программа-функция возведения в степень может быть задана так:

Контрольный пример:

E. Частное и остаток: a/b (b>0). В данной задаче речь идет об отыскании величин q и r из представления: a=qb+r (0r<b, q0). Будем предполагать, что операция вычитания уже определена, а ответ должен быть возвращен в виде вектора с двумя компонентами: (q r)T. Если a<b, то q=0 и r=a. Этот факт и определяет базу рекурсии. Если же b>a, то a/b=1+(ab)/b, что позволяет организовать декомпозицию. Все сказанное учтено в рекурсивной функции divi(q,a,b). В ней первый аргумент q является вспомогательным параметром. При обращениях к divi() он должен определять базовое значение частного, то есть быть равен нулю. Однако проще решать задачу с помощью вспомогательной функции div(a,b), возлагая на неё решение вопроса о правильном значении вспомогательного параметра вызовах divi(). Это довольно типичный случай при обобщениях исходной задачи за счет введения дополнительных параметров.

Контрольный пример.

Замечание. Моделирование различных операций возможно не только для целых неотрицательных чисел. Их можно было бы определить и для множества всех целых чисел и даже для множества вещественных чисел. Ограничимся рассмотрением одного примера. Напишем рекурсивную программу-функцию нахождения дробной части вещественного числа, если разрешены лишь операции x+1 и x1. Вот один из возможных достаточно ясных вариантов её определения:

Контрольный пример.

Синтаксические языковые конструкции

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

Решение. Будем считать, что речь идет о версиях Фортрана, где идентификатор определялся как последовательность из шести заглавных латинских букв и (или) цифр, начинающаяся с буквы и для символов была принята ASCII кодировка. Превратить последовательность символов в вектор соответствующих им ASCII-кодов можно с помощью встроенной функции str2vec(). Например:

Дополнительное ограничение на первый символ идентификатора (буква, но не цифра) усложняет общую проверку (или буква, или цифра). Чтобы действия были стандартными для всех символов, проверку первого из них будем осуществлять на допустимость отдельно в головной программе. Естественно там же располагать и проверку длины слова, которая должна находиться в пределах от 1 до 6. Тогда в подпрограмме останется решить вполне рекурсивную подзадачу: “если первый символ исходного слова не буква и не цифра, то формируем ответ “не идентификатор”. В противном случае, если длина слова равна единице, то возвращаем ответ “идентификатор”, а иначе укорачиваем слово на первый символ и снова решаем эту же подзадачу. Все сказанное и реализуется головной программой identity(word) и рекурсивной подпрограммой iden(v):

Контрольные примеры.

Замечание. В любом языке программирования все базовые языковые конструкции (идентификаторы, константы, переменные, выражения, метки, типы и т.п.) определяются рекурсивно. Особенно наглядно это видно, когда они представлены с помощью синтаксических диаграмм [7, c. 685703] или в форме Бэкуса-Наура. Подобные определения в рамках конкретного языка программирования могут служить наборами тренировочных заданий по написанию рекурсивных программ и даже простейших рекурсивных трансляторов.

Приведем пример. Идентификатор в Паскале определяется, как и в Фортране, но без ограничений на длину последовательности символов. С помощью синтаксической диаграммы это выглядит так, как это указано на рис.3.7., а в форме Бэкуса-Наура следующим образом (значок “::=” читается как “есть по определению”):

идентификатор::=буква

идентификатор::=идентификаторбуква

идентификатор::=идентификаторцифра

Рис. 3.7. Синтаксическая диаграмма идентификатора (Паскаль)

И в том и в другом случаях идентификатор определяется сам через себя.

3.28 Проблемная ситуация

Задача 38. При любом ли натуральном n ли рекурсивная функция

равна 1?

Решение. Хотя данная строка и начата со слова “решение”, ответа на поставленный вопрос мы не знаем и, по-видимому, на сегодняшний день его не знает никто. Более того, неизвестно, для любого ли n problem(n) вычисляется за конечное число шагов. Рассмотренную задачу называют 3n+1 проблемой. Мы включили её в список задач для того, чтобы обратить внимание читателя на следующий факт. Достаточно простые с виду рекурсивные определения функций могут таить в себе глубокие проблемы, решения которых лежат совсем не на поверхности. Тем не менее, конкретные вычисления problem(n) при разных n приводят к одному и тому же значению, равному 1. Ниже приведена рекурсивная программа для проверки истинности утверждения “problem(n)=1” при значениях n из диапазона k1..k2.

Контрольные примеры.

6. Задача Иосифа Флавия

С именем известного историка первого века Иосифа Флавия связывают следующую задачу-легенду. В ходе иудейской войны он в составе отряда из 41 воина был загнан римлянами в пещеру. Не желая сдаваться, осажденные воины решили покончить жизнь самоубийством и разработали для этого следующую процедуру. Они выстроились в круг и, начиная отсчет с конкретной позиции, каждый третий должен был убивать себя, пока не останется ни одного человека. Математически одаренный Иосиф считал подобный конец бессмысленным и потому поставил себя и своего друга на такие позиции, что после серии из 39 самоубийств они остались вдвоем, чем и спасли себе жизнь. Что это были за позиции?

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

Задача 1. По окружности в направлении движения часовой стрелки расположены n последовательных натуральных чисел от 1 до n. При перемещении по числам 1, 2, ... каждое k-ое число (k>1) вычеркивается (удаляется). Этот процесс продолжается до тех пор, пока чисел не станет меньше k. Определить оставшиеся числа.

Задача 2. По окружности в направлении движения часовой стрелки расположены n последовательных натуральных чисел от 1 до n. При перемещении по числам 1, 2, ... каждое k-ое число (k>1) вычеркивается (удаляется). Этот процесс продолжается до тех пор, пока не останется одно число. Определить его.

A. Решение первой задачи Флавия при k=2.

Ниже рассмотрены три варианта A1A3 решения задачи 1 при k=2, опирающиеся на разные идеи.

A1. Если n=2s, то после первого прохода по кругу останутся числа: 1, 3, ... 2s1 и следующий проход начнется с вычеркивания числа 3. Это все равно, как если бы мы начинали с s последовательных натуральных чисел от 1 до s, но каждое уцелевшее число удваивали и результат уменьшали на 1. Отсюда, если fla1(n) функция, решающая поставленную задачу, то fla1(1)=1 и

fla1(2s)=2fla1(s) 1 (s1). (16)

Аналогичные рассуждения показывают, что

fla1(2s+1)=2fla1(s) + 1 (s1). (17)

Величины fla1(n) назовем числами Флавия. Соотношения (16) и (17) сразу же позволяют написать следующую рекурсивную программу-функцию вычисления значений fla1(n).

A2. Исследование рекуррентных соотношений (16)(17) показывает, что

fla1(2s + q)=2q+1 (s0, 0q<2s) (18)

Отсюда получаем еще один рекурсивный алгоритм для вычисления чисел Флавия (см. ниже). При этом вспомогательная рекурсивная функция power(n,0) вычисляет значение s, удовлетворяющее соотношению (18), то есть уменьшенное на 1 количество цифр двоичного разложения n, а функция fla2(n) непосредственно вычисляет число Флавия для заданного n.

A3. Еще один способ нахождения чисел Флавия дается программой-функцией flavec(v), где v=(1 2 3 ... n)T вектор. Подавать такой вектор в качестве аргумента необязательно. Проще обращаться к flavec(v) c помощью функции fla3(n), где по заданному n генерируется соответствующий вектор v. Отметим, что в flavec(v) используется рекурсивный алгоритм непосредственного вычеркивания каждого второго числа. При этом вектор v перестраивается при каждом новом перемещении по кругу.

Контрольные примеры.

1. fla1(6)=5fla2(6)=5fla3(6)=5

2. fla1(11)=7 fla2(11)=7 fla3(11)=7

3. fla1(1000)=997fla2(1000)=997fla3(1000)=997

B. Решение первой задачи Флавия в общем случае.

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

B1. Способ A3 решения задачи 1 при k=2 хоть и несколько громоздкий, но он достаточно просто переносится на общий случай. При этом естественно считать k аргументом функции. Тогда решение задачи дает рекурсивная программа-функция flave(v,k), где v=(1 2 3 ... n)T вектор. Однако проще использовать для этих целей функцию fla4(n,k), формирующую по заданному целому n требуемый вектор v, а затем уже обращающуюся k flave(v,k).

B2. Приведенный ниже способ решения первой задачи Флавия отличается от предыдущего лишь способом организации рекурсии. Здесь она реализована не по каждому вычеркнутому элементу, а по отдельным проходам по окружности. Решение задачи дается программой-функцией flavmek(v,k), где v=(1 2 3 ... n)T вектор. Однако проще использовать для этих целей функцию fla5(n,k), формирующую по заданному целому n требуемый вектор v, а затем уже обращающуюся к flavmek(v,k). Обратите внимание на используемый в fla5(n,k) метод формирования вектора v.

Контрольные примеры.

1. fla4(6,2)=5 fla5(6)=5

2. fla4(41,3)T =[16 31] fla5(11) T =[16 31]

3. fla4(1000,5)T=[563 763 802 73] fla5(1000) T=[563 763 802 73]

С. Решение второй задачи Флавия в общем случае.

Пусть функция flav(v,k), где v=(1 2 ... n)T решает поставленную задачу и пусть w вектор, полученный из v вычеркиванием одного k-го компонента. После каждой такой операции будем организовывать рекурсивный вызов flav(w,k), прекращая вычисления тогда, когда длина вектора станет равной единице. Пусть le - длина вектора v и s=mod(k,le). Нетрудно видеть, что после одного вычеркивания получим:

w=(1 2 ... le1)T при s=0 ,

w=(2 3 ... le)T при s=1 ,

w=(s+1,s+2,...,le,1,2,...,s1) при s>=2 .

Поэтому функцию flav(v,k) и обращающуюся к ней функцию fla6(n,k) можно определить следующим образом:

Контрольные примеры.

fla6(10,5)=3 fla6(5,10)=4 fla6(1000,7)=404 .

13.4 Системы счисления

A. Перевод чисел из десятичной системы в p-ичную систему

Не ограничивая общности речь можно вести о неотрицательных числах.Пусть p{2,3,…} и цифры p-ичной системы это последовательные десятичные числа: 0, 1, ... p1. Рассмотрим 6 конкретных задач. В трех первых из них речь идет о переводе естественным образом заданных десятичных чисел в p-ичную систему счисления. В следующих трех задачах речь идет о переводе десятичных чисел, цифры которых заданы в виде последовательных компонентов векторов, в p-ичную систему счисления. Во всех случаях результат формируется в виде вектора, компоненты которого p-ичные цифры исходного числа.

Задача 1. Составить программу-функцию перевода целых неотрицательных десятичных чисел m в систему счисления по основанию p.

Решение. Функция dec_p_i(m,p) решает поставленную задачу, используя рекурсивный алгоритм последовательного деления. Результат формируется в виде вектора, компоненты которого p-ичные цифры m.

Контрольные примеры.

.

Замечания.

1. Если разряды p-ичного числа необходимо формировать не от старшего разряда, а от младшего и далее, то в программе dec_p_i() первый и второй аргументы функции stack() необходимо поменять местами.

2. При переводе неотрицательных десятичных чисел в конкретную систему счисления, в функции dec_p_i() достаточно иметь один аргумент. Например, перевод в двоичную систему можно осуществлять следующей программой-функцией dec_b_i(m).

Контрольные примеры.

Как мы уже отмечали при реализации функций dec_p_i(m,p) и dec_b_i(m) использован рекурсивный вариант алгоритма последовательного деления выделения цифр p-ичной системы для целых чисел. Пояснений требуют лишь фрагменты вида identity(1)x. Дело в том, что функция stack() в качестве своих аргументов использует векторы или матрицы. И смысл записи identity(1)x состоит в превращении скаляра х в матрицу размера 11 с элементом x.

Задача 2. Составить программу-функцию перевода правильной неотрицательной десятичной дроби y в систему счисления по основанию p.

Решение. Функция dec_p_f(y,p,k) решает поставленную задачу, используя рекурсивный алгоритм последовательного умножения. Результат формируется в виде вектора с не более чем k (k=1,2,…) компонентами, которые суть p-ичные цифры числа y, начиная от старших разрядов и далее.

Контрольные примеры.

Задача 3. Составить программу-функцию перевода неотрицательного действительного десятичного числа a в систему счисления по основанию p (p=2, 3, …).

Решение. Функция dec_p(a,p,k) решает поставленную задачу, осуществляя перевод десятичного числа a в p-ичную систему счисления. Результат вычислений формируется в виде составного вектора. Компоненты этого вектора снова векторы, содержащие соответственно цифры целой и дробной частей числа a в p-ичной системе счисления. Цифры целой и дробной части a возвращаются, начиная со старших разрядов и далее. В дробной части присутствует не более чем k (k=1,2,...) цифр. При вычислениях функция dec_p() обращается к двум рекурсивным функциям dec_p_i() и dec_p_f().

Контрольные примеры.

Задача 4. Пусть m=(v0v1…vn1)10 целое десятичное неотрицательное число, цифры которого от старшей и далее заданы последовательными компонентами вектора v=(v0, v1, …,vn1)T . Составить программу-функцию перевода m в систему счисления по основанию p.

Решение. Функция dec_p_iv(v,p) осуществляет перевод v в p-ичную систему счисления. Результат вычислений формируется, начиная от старших разрядов и далее, в виде вектора, компоненты которого суть p-ичные цифры исходного числа. Функция dec_p_iv(v,p) отличается от ранее рассмотренной функции dec_p_i(v,p) лишь формой представления первого аргумента. Поэтому её вычисление можно свести к вычислению dec_p_i(v,p) с предварительным обращением к рекурсивной функции dv_norm(v), переводящей десятичное число из векторного представления в нормальную форму. Это и сделано ниже.

Контрольные примеры.

Задача 5. Пусть y=(.v0v1…vn1)10 правильная неотрицательная десятичная дробь, цифры которой от старшей и далее заданы последовательными компонентами вектора v=(v0, v1, …,vn1)T. Составить программу-функцию перевода y в систему счисления по основанию p.

Решение. Функция dec_p_fv(v,p,k) осуществляет перевод v в p-ичную систему счисления. Результат вычислений формируется, начиная от старших разрядов и далее, в виде вектора длины не более k, компоненты которого суть p-ичные цифры исходного числа. Функция dec_p_fv(v,p,k) отличается от ранее рассмотренной функции dec_p_f(v,p,k) лишь формой представления первого аргумента. Поэтому её вычисление можно свести к вычислению dec_p_f(v,p,k) с предварительным обращением к рекурсивной функции dv_normf(v), переводящей десятичную дробь из векторного представления в нормальную форму. Это и сделано ниже.

Контрольные примеры.

Задача 6. Пусть действительное неотрицательное десятичное число a представлено двумя векторами in и fr, компоненты которых последовательные десятичные цифры, начиная от старшей и далее, соответственно целой и дробной части а. Составить программу-функцию перевода a в систему счисления по основанию p.

Решение. Функция dec_pv(in,fr,p,k) решает поставленную задачу, осуществляя перевод десятичного числа a в p-ичную систему счисления. Результат вычислений формируется в виде составного вектора. Компоненты этого вектора снова векторы, содержащие соответственно цифры целой и дробной частей числа a в p-ичной системе счисления. Цифры целой и дробной части a возвращаются, начиная со старших разрядов и далее. В дробной части присутствует не более чем k (k=1,2,...) цифр. При вычислениях функция dec_p() обращается к двум рекурсивным функциям dec_p_iv() и dec_p_fv().

Контрольные примеры.

B. Перевод чисел из p-ичной системы в десятичную систему

Пусть p{2,3,…} и цифрами p-ичной системы являются десятичные числа 0,1,... ,p1. Будем считать, что рассматриваются неотрицательные p-ичные числа, а их цифры от старшего разряда и далее задаются последовательными компонентами векторов. Рассмотрим 3 конкретные задачи.

Задача 7. Пусть m=(v0v1…vn1)p целое p-ичное неотрицательное число, цифры которого от старшей и далее заданы последовательными компонентами вектора v=(v0, v1, …,vn1)T . Составить программу-функцию перевода m в десятичную систему счисления.

Решение. Функция p_dec_i(v,p) решает поставленную задачу, используя рекурсивный алгоритм последовательного деления. Результат формируется в естественной форме.

Контрольные примеры.

Задача 8. Пусть y=(.v0v1…vn1)p правильная неотрицательная десятичная дробь, цифры которой от старшей и далее заданы последовательными компонентами вектора v=(v0, v1, …,vn1)T . Составить программу-функцию перевода y в десятичную систему счисления.

Решение. Функция p_dec_f(v,p) решает поставленную задачу, используя рекурсивный алгоритм последовательного умножения. Результат формируется в естественной форме.

Контрольные примеры.

Задача 9. Пусть действительное неотрицательное p-ичное число а представлено двумя векторами in и fr, компоненты которых последовательные p-ичные цифры, начиная от старшей и далее, соответственно целой и дробной части а. Составить программу-функцию перевода a в десятичную систему счисления.

Решение. Функция p_dec(in,fr,p) решает поставленную задачу, осуществляя перевод p-ичного числа a в десятичную систему счисления. Результат вычислений формируется в естественной форме. При вычислениях функция p_dec() обращается к двум рекурсивным функциям p_dec_i() и p_dec_f().

Контрольные примеры.

Замечание. Перевод чисел из p-ичной системы счисления в q-ичную систему (p,q{2,3,…}) можно осуществлять через промежуточную десятичную систему.

13.5 Генераторы перестановок

Ранее в разделе 12.9 мы описали один из алгоритмов получения n! перестановок из элементов множества S={a0, a1, …, an1}, где ak (k=0..n1) попарно различные действительные числа. Считая, что S={1,2,…,n}, обсудим еще несколько вариантов рекурсивных алгоритмов генерирования перестановок. Отличаются друг от друга они разными характеристиками: быстродействием, компактностью записи, количеством транспозиций при получении очередной перестановки и т.д.

A. Метод вертикальной прогонки. При S={1} имеем одну перестановку 1. Если уже имеется последовательность перестановок из n1 элемента {1,2,…,n1}, то получить все перестановки из n элементов {1,2,…n} можно способом, который мы будем именовать как “метод вертикальной прогонки” элемента n. Суть его в следующем. Рассмотрим n идентичных экземпляров (групп) последовательностей перестановок из элементов {1,2,…,n1}. В первом экземпляре в конец каждой перестановки поместим элемент n. Во втором экземпляре этот элемент поместим на предпоследнем месте и т.д. Наконец, в последнем экземпляре поместим n перед первым элементом. На рисунке 13.4 описанная процедура демонстрируется при переходе от n=3 к n=4. Нетрудно понять, что указанные действия любую перестановку из n1 элемента переводят в некоторую перестановку из n элементов. При этом общее количество полученных перестановок равно n!. Остается показать, что среди них нет совпадающих. Если взять две сгенерированные перестановки внутри одной группы, то они будут различаться друг от друга позициями элементов {1,2,…,n1} и, значит, не могут быть совпадающими. Если же взять перестановки из разных групп, то позиции элемента n в них будут разными и перестановки опять не могут быть совпадающими. Таким образом, все полученные n! перестановок различны.

Рис. 13.6. Генерирование перестановок методом вертикальной прогонки.

Описанный алгоритм вертикальной прогонки реализуется рекурсивной программой-функцией permut1(n):

Контрольный пример

B. Метод последовательного замещения. При S={1} имеем одну перестановку 1. Если уже имеется последовательность перестановок из n1 элемента {1,2,…,n1}, то получить все перестановки из n элементов {1,2,…n} можно способом, который мы будем именовать как “метод последовательного замещения” элемента n. Суть его в следующем. Рассмотрим n идентичных экземпляров (групп) последовательностей перестановок из элементов {1,2,…,n1}. В первом экземпляре, как и в методе вертикальной прогонки, в конец каждой перестановки поместим элемент n. В каждой перестановке второго экземпляра поменяем местами элементы n и 1. В каждой перестановке третьего экземпляра поменяем местами элементы n и 2 и т.д. Наконец, в последнем экземпляре поменяем местами элементы n и n1. На рисунке 13.5 описанная процедура демонстрируется при переходе от n=3 к n=4. Нетрудно понять, что указанные действия любую перестановку из n1 элемента переводят в некоторую перестановку из n элементов. При этом общее количество полученных перестановок равно n!. Остается показать, что среди них нет совпадающих. Если взять две сгенерированные перестановки внутри одной группы, то они обязательно будут различаться друг от друга расположением элементов на позициях от 1 до n1 и, значит, не могут быть совпадающими. Если же взять перестановки из разных групп, то на последних позициях у них стоят разные элементы и перестановки опять не могут быть совпадающими. Таким образом, все полученные n! перестановок различны.

Рис. 13.5. Генерирование перестановок методом последовательного

замещения

Описанный алгоритм последовательного замещения реализуется рекурсивной программой-функцией permut2(n):

С. Перестановки в антилексикографическом порядке. На множестве P всех перестановок <x0,x1,…,xn-1> из элементов {1,2,…,n} определим два типа порядка.

Лексикографический порядок на P вводится следующим образом. Для

(<a0,a1,…,an1>,<b0,b1,…,bn1>P) (19)

положим

<a0,a1,…,an1> <' <b0,b1,…,bn1>

(k 0) [(ak bk) & (s < k) [(as = bs)]], (20)

где символ <' использован в качестве знака лексикографического сравнения перестановок.

Заметим, что если вместо чисел 1,2,…,n использовать буквы с естественным порядком следования их в алфавите, то лексикографический порядок определяет стандартную последовательность, в которой слова длины n появляются в словаре. И это справедливо для алфавитов любых языков.

Аналогично вводится и антилексикографический порядок на P. Для (19) положим

<a0,a1,…,an1> <” <b0,b1,…,bn1>

(k n1) [(ak > bk) & (s > k) [(as = bs)]], (21)

где символ <” использован в качестве знака антилексикографического сравнения перестановок.

Построим рекурсивную программу-функцию, генерирующую перестановки элементов {1,2,…n} в антилексикографическом порядке. Соответствующий алгоритм может базироваться на следующих двух утверждениях, непосредственно вытекающих из определения 21.

Утверждение 1. Если множество перестановок P упорядочено антилексикографически, то начальная и конечная перестановки это соответственно <1,2,…,n> и <n,n1,…,1>.

Утверждение 2. Упорядоченная антилексикографически последовательность перестановок по значению последнего элемента в них может быть разбита на n блоков длины (n1)!. При этом q-й блок на последнем месте имеет элемент равный (nq+1), а первые n1 позиций этого блока определяют последовательность перестановок множества {1,2,…,n}\{q} в антилексикографическом порядке.

Головная функция permut3(n) решает поставленную задачу. В ней по значению n для рекурсивной программы-функции permu(p) формируется начальная перестановка p. В permu(p) и проводятся все вычисления. На рис. 13.6 представлен результат выполнения permut3(n) при n=3 и n=4. Для n=4 полученные перестановки расположены по столбцам.

Рис. 13.6. Генерирование перестановок в антилексикографическом порядке

D. Перестановки в лексикографическом порядке. В предыдущем разделе мы ввели понятие лексикографического порядка. Алгоритм генерирования перестановок в таком порядке и соответствующие программы-функции могут быть построены на идеях, близких к тем, которые использовались при генерировании перестановок в антилексикографическом порядке. Поэтому здесь мы ограничимся лишь приведением соответствующих функций permut(p) и permut4(n) и представим полученный по ним результат вычислений для n=3 и n=4 (см. рис. 13.7). Для n=4 полученные перестановки расположены по столбцам.

Рис. 13.7. Генерирование перестановок в лексикографическом порядке

E. Перестановки c одной транспозицией соседних элементов. В этом пункте рассматривается алгоритм генерирования последовательности перестановок U из элементов множества {1,2,…,n} такой, что любая перестановка U, кроме начальной, получается из предыдущей перестановки одной транспозицией соседних элементов. Проиллюстрируем на примере идею соответствующего алгоритма, приписываемого Джонсону [] и Троттеру []. Предположим, что для элементов {1,2,…,n1} уже построена требуемая последовательность перестановок U. Тогда из элементов {1,2,…,n} необходимая последовательность может быть построена перемещениями элемента n между начальной и конечной позициями каждой перестановки U. При этом перемещения должны производиться попеременно вперед и назад (n1)! раз так, как это показано на рис. 13.8.

Рекурсивная программа-функция permut5(n) реализует этот, схематично описанный, алгоритм. На рис. 13.8 приведены полученные по permut5(n) перестановки для n=3 и n=4.

Рис. 13.8. Генерирование перестановок c транспозицией соседних элементов

В заключение автор выражает признательность профессорам. Ваграменко Я.А. и Добровольскому Н.М. за консультации и советы при написании пособия.

Литература

1. Кнут Д. Искусство программирования для ЭBM. Основные алгоритмы: т. 1, M.: Мир, 1976.

2. Кнут Д. Искусство программирования для ЭBM. Получисленные алгоритмы: т. 2, М.: Мир, 1977.

3. Кнут Д. Искусство программирования для ЭBM. Сортировка и поиск: т. 3, М.: Мир, 1978.

4. Лекции лауреатов премии Тьюринга. М.: Мир, 1985.

5. Бауэр Ф.Л., Гнац Р., Хилл У. Информатика. Задачи и решения. М.: Мир, 1978 г.

6. Бауэр Ф.Л., Гооз Г. Информатика. T. 1, М.: Мир, 1990 г.

7. Бауэр Ф.Л., Гооз Г. Информатика. T. 2, М.: Мир, 1990 г.

8. Ландау Э. Основы анализа. М.: изд. Иностранной литературы, 1947.

9. http://www-cs-staff.stanford.edu/~knuth/taocp.html#vol4-{volume4}).


Подобные документы

  • Исследование задачи оптимизации ресурсов при планировании товарооборота торгового предприятия в общем виде. Формирование математической модели задачи. Решение симплекс-методом. Свободные члены системы ограничений и определение главных требований к ним.

    курсовая работа [68,6 K], добавлен 21.06.2011

  • Решение графическим методом задачи линейного программирования с двумя неизвестными. Решение транспортной задачи методом северо-западного угла и методом минимальной стоимости. Системы массового обслуживания. Стохастическая модель управления запасами.

    контрольная работа [458,1 K], добавлен 16.03.2012

  • Математическая постановка и алгоритм решения транспортной задачи. Сбалансированность и опорное решение задачи. Методы потенциалов и северо-западного угла. Блок-схема. Формы входной и выходной информации. Инструкция для пользователя и программиста.

    курсовая работа [113,8 K], добавлен 10.11.2008

  • Способ перевозки при котором затраты связанные с перевозкой минимальны. Распределительный метод достижения оптимального плана. Метод последовательного улучшения плана перевозок. Написание программы. Visual Basic for Applications. Описание алгоритма.

    курсовая работа [34,6 K], добавлен 20.11.2008

  • Симплекс метод решения задач линейного программирования. Построение модели и решение задачи определения оптимального плана производства симплексным методом. Построение двойственной задачи. Решение задачи оптимизации в табличном процессоре MS Excel.

    курсовая работа [458,6 K], добавлен 10.12.2013

  • Основные методы решения задачи оптимального закрепления операций за станками. Разработка экономико-математической модели задачи. Интерпретация результатов и выработка управленческого решения. Решение задачи "вручную", используя транспортную модель.

    курсовая работа [1,0 M], добавлен 25.01.2013

  • Математическая формулировка экономико-математической задачи. Вербальная постановка и разработка задачи о составлении графика персонала. Решение задачи о составлении графика персонала с помощью программы Microsoft Excel. Выработка управленческого решения.

    курсовая работа [1,2 M], добавлен 12.01.2018

  • Суть математического моделирования процессов и теории оптимизации. Метод дихотомии и золотого сечения. Поиск точки min методом правильного симплекса. Графическое решение задачи линейного программирования, моделирование и оптимизация трёхмерного объекта.

    курсовая работа [1,8 M], добавлен 15.01.2010

  • Задачи операционного исследования. Построение базовой аналитической модели. Описание вычислительной процедуры. Решение задачи оптимизации на основе технологии симплекс-метода. Анализ результатов базовой аналитической модели и предложения по модификации.

    курсовая работа [1,5 M], добавлен 12.12.2009

  • Оптимальный план прямой задачи. Значения функций целочисленного и нецелочисленного решений. Оптимальное решение двойственной задачи и условия дополняющей нежесткости. Условия канонической задачи линейного программирования. Метод Жордана–Гаусса.

    контрольная работа [323,0 K], добавлен 20.01.2011

Работы в архивах красиво оформлены согласно требованиям ВУЗов и содержат рисунки, диаграммы, формулы и т.д.
PPT, PPTX и PDF-файлы представлены только в архивах.
Рекомендуем скачать работу.