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

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

< Лекция 34 || Лекция 35: 1234 || Лекция 36 >

Пример 3. Задача о разбиении целого на части.

Найдите количество разбиений натурального числа на сумму натуральных слагаемых.

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

Например, разбиение числа 6 будет представлено 11 комбинациями:

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

Рассмотрим решение в общем виде. Пусть зависимость R(n,k) вычисляет количество разбиений числа n на сумму слагаемых, не превосходящих k. Опишем свойства R(n,k).

Если в сумме все слагаемые не превосходят 1, то такое представление единственно, то есть R(n,k)=1.

Если рассматриваемое число равно 1, то при любом натуральном значении второго параметра разбиение также единственно: R(n,k)=1.

Если второй параметр превосходит значение первого , то имеет место равенство R(n,k)=R(n,n), так как для представления натурального числа в сумму не могут входить числа, превосходящие его.

Если в сумму входит слагаемое, равное первому параметру, то такое представление также единственно (содержит только это слагаемое), поэтому имеет место равенство: R(n,n)=R(n,n-1)+1.

Осталось рассмотреть случай (n>k). Разобьем все представления числа n на непересекающиеся разложения: в одни обязательно будет входить слагаемое k, а другие суммы не содержат k. Первая группа сумм, содержащая k, эквивалентна зависимости R(n-k,k), что следует после вычитания числа k из каждой суммы. Вторая группа сумм содержит разбиение числа n на слагаемые, каждое из которых не превосходит k-1, то есть число таких представлений равно R(n,k-1). Так как обе группы сумм не пересекаются, то R(n,k)=R(n-k,k)+R(n,k-1).

Разработаем рекурсивную триаду.

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

База рекурсии: исходя из свойств рассмотренной зависимости, выделяются два базовых случая:

при n=1     R(n,k)=1,

при k=1     R(n,k)=1.

Декомпозиция: общий случай задачи сводится к трем случаям, которые и составляют декомпозиционные отношения.

при n=k     R(n,k)=R(n,n-1)+1,

при n<k     R(n,k)=R(n,n),

при n>k     R(n,k)=R(n-k,k)+R(n,k-1).

#include "stdafx.h"
#include <iostream>
using namespace std;
unsigned long int Razbienie(unsigned long int n, 
                            unsigned long int k);

int _tmain(int argc, _TCHAR* argv[]){
  unsigned long int number, max,num;
  printf ("\nВведите натуральное число: ");
  scanf ("%d", &number);
  printf ("Введите максимальное натуральное слагаемое в 
           сумме: ");
  scanf ("%d", &max); 
  num=Razbienie(number,max);
  printf ("Число %d можно представить в виде суммы с 
           максимальным слагаемым %d.", number, max);
  printf ("\nКоличество разбиений равно %d",num);
  system("pause");
  return 0;
}

unsigned long int Razbienie(unsigned long int n,
                            unsigned long int k){
  if(n==1 || k==1)  return 1;
  if(n<=k)  return Razbienie(n,n-1)+1;        
  return Razbienie(n,k-1)+Razbienie(n-k,k);
}

Пример 4. Задача о переводе натурального числа в шестнадцатеричную систему счисления.

Дано натуральное число, не выходящее за пределы типа unsigned long. Число представлено в десятичной системе счисления. Переведите его в систему счисления с основанием 16.

Пусть требуется перевести целое число n из десятичной в р -ичную систему счисления (по условию задачи, р = 16), то есть найти такое k, чтобы выполнялось равенство n10=kp.

Параметризация: n – данное натуральное число, роснование системы счисления.

База рекурсии: на основании правил перевода чисел из десятичной системы в систему счисления с основанием р, деление нацело на основание системы выполняется до тех пор, пока неполное частное не станет равным нулю, то есть: если целая часть частного n и р равна нулю, то k = n. Данное условие можно реализовать иначе, сравнив n и р: целая часть частного равна нулю, если n < р.

Декомпозиция: в общем случае k формируется из цифр целой части частного n и р, представленной в системе счисления с основанием р, и остатка от деления n на p.

#include "stdafx.h"
#include <iostream>
using namespace std;
#define maxline 50 
void perevod( unsigned long n, unsigned int p,FILE *pf);

int _tmain(int argc, _TCHAR* argv[]){
  unsigned long number10;
  unsigned int osn=16; 
  char number16[maxline];
  FILE *f;
  if ((f=fopen("out.txt", "w"))==NULL)
    perror("out.txt");
  else {
    printf ("\nВведите число в десятичной системе: "); 
    scanf("%ld", &number10);
    perevod(number10, osn, f); 
   fclose(f);
  }
  if ((f=fopen("out.txt", "r"))==NULL)
    perror("out.txt"); 
  else {  
    fscanf(f,"%s",number16);
    printf("\n %ld(10)=%s(16)", number10, number16);
    fclose(f); 
  }
  system("pause");
  return 0;
}

void perevod(unsigned long n, unsigned int p, FILE *pf){
  char c;
  unsigned int r;
  if(n >= p) perevod (n/p, p, pf);//декомпозиция
  r=n%p;
  c=r < 10 ? char (r+48) : char (r+55);
  putc(c, pf);
}

Ключевые термины

База рекурсии – это тривиальный случай, при котором решение задачи очевидно, то есть не требуется обращение функции к себе.

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

Декомпозиция – это выражение общего случая через более простые подзадачи с измененными параметрами.

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

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

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

Параметризация – это выделение из постановки задачи параметров, которые используются для описания условия задачи и решения.

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

Прямая рекурсия – это непосредственное обращение рекурсивной функции к себе, но с иным набором входных данных.

Рекурсивная триада – это этапы решения задач рекурсивным методом.

Рекурсивная функция – это функция, которая в своем теле содержит обращение к самой себе с измененным набором параметров.

Рекурсивный алгоритм – это алгоритм, в определении которого содержится прямой или косвенный вызов этого же алгоритма.

Рекурсия – это определение объекта посредством ссылки на себя.

Краткие итоги

  1. Рекурсия характеризуется определением объекта посредством ссылки на себя.
  2. Рекурсивные алгоритмы содержат в своем теле прямое или опосредованное обращение с самим себе.
  3. Рекурсивные функции содержат в своем теле обращение к самим себе с измененным набором параметров в виде прямой рекурсии. При этом обращение к себе может быть организовано посредством косвенной рекурсии – через цепочку взаимных обращений функций, замыкающихся в итоге на первоначальную функцию.
  4. Решение задач рекурсивными способами проводится посредством разработки рекурсивной триады.
  5. Целесообразность применения рекурсии в программировании обусловлена спецификой задач, в постановке которых явно или опосредовано указывается на возможность сведения задачи к подзадачам, аналогичным самой задаче.
  6. Рекурсивные методы решения задач широко используются при моделировании задач из различных предметных областей.
  7. Рекурсивные алгоритмы относятся к ресурсоемким алгоритмам. Для оценки сложности рекурсивных алгоритмов учитывается число вершин полного рекурсивного дерева, количество передаваемых параметров, временные затраты на организацию стековых слоев.
< Лекция 34 || Лекция 35: 1234 || Лекция 36 >
Денис Курбатов
Денис Курбатов
Владислав Нагорный
Владислав Нагорный

Подскажите, пожалуйста, планируете ли вы возобновление программ высшего образования? Если да, есть ли какие-то примерные сроки?

Спасибо!