Как я могу использовать теорему Мастера для описания рекурсии? - PullRequest
6 голосов
/ 20 октября 2008

Недавно я изучал рекурсию; как писать, анализировать и т. д. Некоторое время я думал, что повторение и рекурсия - это одно и то же, но некоторые проблемы с недавними домашними заданиями и тестами заставляют меня думать, что есть небольшие различия, что «повторение» - это опишите рекурсивную программу или функцию.

Это все было очень гречески для меня до недавнего времени, когда я понял, что есть нечто, называемое «основная теорема», используемое для написания «повторения» для задач или программ. Я читал страницу википедии, но, как обычно, все написано так, что я не совсем понимаю, о чем идет речь. Я гораздо лучше учусь на примерах.

Итак, несколько вопросов: Допустим, вам дано это повторение:

r (n) = 2 * r (n-2) + r (n-1);
r (1) = r (2) = 1

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

Теперь предположим, что вас попросили найти повторение, T (n), для количества добавлений, выполненных программой, созданной из предыдущего повторения. Я вижу, что базовый случай, вероятно, будет T (1) = T (2) = 0, но я не уверен, куда идти дальше.

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

РЕДАКТИРОВАТЬ: Хорошо, я просмотрел некоторые из моих прошлых заданий, чтобы найти другой пример того, где меня спрашивают, «найти повторение», что является частью этого вопроса, с которым у меня возникли проблемы с почтой. .

Повторение, которое описывает в лучшем способ количество операций сложения в следующем фрагменте программы (при вызове с l == 1 и r == n)

int example(A, int l, int r) {
  if (l == r)
    return 2;
  return (A[l] + example(A, l+1, r);
}

Ответы [ 5 ]

8 голосов
/ 20 октября 2008

Несколько лет назад Мохамад Акра и Луай Бацци доказали результат, обобщающий метод Мастера - он почти всегда лучше. Вы действительно не должны больше использовать основную теорему ...

См., Например, эту запись: http://courses.csail.mit.edu/6.046/spring04/handouts/akrabazzi.pdf

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

2 голосов
/ 13 августа 2010

Захари:

Допустим, вам дано это рецидив:

r (n) = 2 * r (n-2) + r (n-1); r (1) = r (2) = 1

Действительно ли это в форме основная теорема? Если да, то на словах, что это говорит?

Я думаю, что ваше рекуррентное отношение говорит о том, что для функции «r» с «n» в качестве ее параметра (представляющего общее количество вводимых вами наборов данных), все, что вы получите в n-й позиции набор данных - это выход n-1-й позиции плюс вдвое больше, чем результат n-2-й позиции, без какой-либо рекурсивной работы. Когда вы пытаетесь решить рекуррентное отношение, вы пытаетесь выразить его так, чтобы не было рекурсии.

Однако я не думаю, что это в правильной форме для метода основной теоремы. Ваше утверждение является «линейным рекуррентным отношением второго порядка с постоянными коэффициентами». Судя по всему, согласно моему старому учебнику по дискретной математике, эта форма вам нужна для решения рекуррентного отношения.

Вот форма, которую они дают:

r(n) = a*r(n-1) + b*r(n-2) + f(n)

Для 'a' и 'b' - некоторые константы, а f (n) - некоторая функция от n. В вашем утверждении a = 1, b = 2 и f (n) = 0. Всякий раз, когда f (n) равен нулю, рекуррентное отношение называется «однородным». Итак, ваше выражение лица однородно.

Я не думаю, что вы можете решить гомогенное рекуррентное отношение, используя метод Theoerm метода Master, потому что f (n) = 0. Ни один из случаев для теоремы метода Master не допускает этого, потому что n-to-power-of -все не может быть равным нулю. Я могу ошибаться, потому что я на самом деле не эксперт в этом, но я не думаю, что можно решить гомогенное рекуррентное отношение с помощью Мастер-метода.

Я понимаю, что способ решения однородного рекуррентного отношения состоит в 5 шагах:

1) Сформируйте характеристическое уравнение, которое имеет вид:

x^k - c[1]*x^k-1 - c[2]*x^k-2 - ... - c[k-1]*x - c[k] = 0

Если у вас есть только 2 рекурсивных экземпляра в вашем гомогенном рекуррентном отношении, то вам нужно всего лишь изменить свое уравнение на квадратное уравнение, где

x^2 - a*x - b = 0

Это потому, что рекуррентное отношение вида

r(n) = a*r(n-1) + b*r(n-2)

Может быть переписано как

r(n) - a*r(n-1) - b*r(n-2) = 0

2) После того как ваше рекуррентное отношение переписано как характеристическое уравнение, затем найдите корни (x [1] и x [2]) характеристического уравнения.

3) С вашими корнями ваше решение теперь будет одной из двух форм:

if x[1]!=x[2]
    c[1]*x[1]^n + c[2]*x[2]^n
else
    c[1]*x[1]^n + n*c[2]*x[2]^n

для случаев, когда n> 2. 4) С новой формой вашего рекурсивного решения вы используете начальные условия (r (1) и r (2)), чтобы найти c [1] и c [2]

Следуя вашему примеру, вот что мы получаем:

1) r (n) = 1 * r (n-1) + 2 * r (n-2) => x ^ 2 - x - 2 = 0

2) Решение для х

x = (-1 +- sqrt(-1^2 - 4(1)(-2)))/2(1)

    x[1] = ((-1 + 3)/2) = 1
    x[2] = ((-1 - 3)/2) = -2

3) Поскольку x [1]! = X [2], ваше решение имеет вид:

c[1](x[1])^n + c[2](x[2])^n

4) Теперь используйте ваши начальные условия, чтобы найти две константы c [1] и c [2]:

c[1](1)^1 + c[2](-2)^1 = 1
c[1](1)^2 + c[2](-2)^2 = 1

Честно говоря, я не уверен, каковы ваши константы в этой ситуации, я остановился на этом. Я предполагаю, что вам нужно будет вводить числа до тех пор, пока вы не получите значение для c [1] и c [2], которое будет удовлетворять этим двум выражениям. Либо это, либо выполните сокращение строки на матрице C, где C равно:

[ 1 1   | 1 ]
[ 1 2   | 1 ] 

Захари:

Повторение, которое описывает в лучшем способ количество операций сложения в следующем фрагменте программы (при вызове с l == 1 и r == n)

int example(A, int l, int r) {
  if (l == r)
    return 2;
  return (A[l] + example(A, l+1, r);
}

Вот значения временной сложности для заданного вами кода, когда r> l:

int example(A, int l, int r) {      =>  T(r) = 0
  if (l == r)               =>  T(r) = 1
    return 2;               =>  T(r) = 1
  return (A[l] + example(A, l+1, r);    =>  T(r) = 1 + T(r-(l+1))
}

Total:                      T(r) = 3 + T(r-(l+1))

Иначе, когда r == l, тогда T (r) = 2, потому что оператор if и return оба требуют 1 шаг на выполнение.

1 голос
/ 12 августа 2010

«Я не совсем уверен, что такое« повторение »»

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

И цель их решения, я думаю, состоит в том, чтобы перейти от рекурсивного определения к тому, которого нет. Скажем, если бы вы имели T (0) = 2 и T (n) = 2 + T (n-1) для всех n> 0, вам нужно было бы перейти от выражения «T (n) = 2 + T (n) -1) "к одному как" 2n + 2 ".

Источники: 1) «Дискретная математика с теорией графов - второе издание», Эдгар Г. Гудэйр и Майкл М. Парментер 2) «Компьютерные алгоритмы С ++» Эллиса Горовица, Сартажа Сахни и Сангутевара Раджасекарана.

1 голос
/ 20 октября 2008

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

public int r(int input) {
    if (input == 1 || input == 2) {
        return 1;
    } else {
        return 2 * r(input - 2) + r(input -1)
    }
}

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

1 голос
/ 20 октября 2008

Ваш метод, написанный в коде с использованием рекурсивной функции, будет выглядеть так:

function r(int n) 
{
  if (n == 2) return 1;
  if (n == 1) return 1;
  return 2 * r(n-2) + r(n-1);  // I guess we're assuming n > 2
}

Я не уверен, что такое "рекуррентность", но рекурсивная функция - это просто та, которая вызывает сама себя.

Рекурсивным функциям требуется escape-предложение (в некоторых нерекурсивных случаях - например, "if n == 1 return 1") для предотвращения ошибки Stack Overflow (т.е. функция вызывается настолько, что у интерпретатора заканчивается память или другие ресурсы)

...