kzen.dev
  • Вопросы
  • Метки
  • Пользователи
Оповещения
Вознаграждения
Регистрация
После регистрации, сможете получать уведомления об ответах и комментариях на Ваши вопросы.
Вход
Если у Вас уже есть аккаунт, войдите чтобы проверить новые уведомления.
Тут будут вознаграждения за добавленные вопросы, ответы и комментарий.
Дополнительно
Источник
Редактировать
Sakibul Alam
Sakibul Alam
Вопрос

В чем разница между рекурсией, мемоизацией и динамическим программированием?

Возможный дубликат: Динамическое программирование и мемоизация: нисходящий и восходящий подходы

Я просмотрел много статей на эту тему, но никак не могу разобраться. Иногда рекурсия и динамическое программирование выглядят одинаково, а иногда мемоизация и динамическое программирование выглядят одинаково. Может ли кто-нибудь объяснить мне, в чем разница?

P.S. Было бы также полезно, если бы вы могли указать мне на какой-нибудь код, использующий эти три подхода для решения одной и той же задачи. (Например, в задаче о ряде Фибоначчи, я думаю, каждая статья, которую я читал, использовала рекурсию, но называла ее динамическим программированием).

43 2012-08-26T20:43:53+00:00 5
 Community
Community
Редактировал вопрос 23-го мая 2017 в 12:34
Программирование
memoization
dynamic-programming
recursion
algorithm
Решение / Ответ
Andrew Tomazos
Andrew Tomazos
26-го августа 2012 в 10:18
2012-08-26T22:18:04+00:00
Дополнительно
Источник
Редактировать
#17048945

Рассмотрим вычисление последовательности Фибоначчи:

Чистой воды рекурсию:

в

int fib(int x)
{
    if (x < 2)
        return 1;

    return fib(x-1) + fib(x-2);
}

результаты в экспоненциальное число звонков.

Рекурсия с мемоизацией/ДП:

int fib(int x)
{
    static vector<int> cache(N, -1);

    int& result = cache[x];

    if (result == -1)
    {
        if (x < 2)
            result = 1;
        else
            result = fib(x-1) + fib(x-2);
    }

    return result;
}

Теперь мы имеем линейное количество звонков в первый раз, и неизменными.

Описанный выше метод называется "ленивый" по. Мы рассчитываем на более ранние сроки в первый раз они просили.

Следующие будут также рассматриваться в ДП, но без рекурсии:

int fibresult[N];

void setup_fib()
{
    fibresult[0] = 1;
    fibresult[1] = 1;
    for (int i = 2; i < N; i++)
       fibresult[i] = fibresult[i-1] + fibresult[i-2];
}

int fib(int x) { return fibresult[x]; }

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

Также не является ни рекурсии, ни ДП:

int fib(int x)
{
    int a = 1;
    int b = 1;
    for (int i = 2; i < x; i++)
    {
        a = a + b;
        swap(a,b);
    }
    return b;
}

Он использует постоянные пространстве и линейном времени.

Также я упомяну для полноты картины существует закрытая форма для Фибоначчи, который использует рекурсию пустоты, ни ДП, что позволяет рассчитывать на постоянный срок Фибоначчи, используя математические формулы, основанные на золотой пропорции:

http://www.dreamincode.net/forums/topic/115550-fibonacci-closed-form/

Andrew Tomazos
Andrew Tomazos
Редактировал ответ 28-го сентября 2019 в 3:45
47
0
 AnT
AnT
26-го августа 2012 в 9:54
2012-08-26T21:54:45+00:00
Дополнительно
Источник
Редактировать
#17048944

Ну, рекурсия+мемоизация - это как раз специфический "вкус" динамического программирования: динамическое программирование в соответствии с подходом сверху вниз.

Точнее, нет никакой необходимости использовать именно рекурсию. Любое решение типа "разделяй и властвуй" в сочетании с мемоизацией является динамическим программированием сверху вниз. (Рекурсия - это LIFO-способ разделения и покорения, в то время как вы также можете использовать FIFO-способ разделения и покорения или любой другой способ разделения и покорения).

Поэтому правильнее будет сказать, что

divide & conquer + memoization == top-down dynamic programming

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

Однако динамическое программирование - это более общее понятие. Динамическое программирование может использовать подход "снизу вверх", что отличается от "разделяй и властвуй+памятование".

 AnT
AnT
Редактировал ответ 26-го августа 2012 в 10:01
25
0
 Ankush
Ankush
26-го августа 2012 в 8:56
2012-08-26T20:56:07+00:00
Дополнительно
Источник
Редактировать
#17048943

Я уверен, что вы можете найти подробное определение в интернете. Вот моя попытка упростить ситуацию.

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

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

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

9
0
 IVlad
IVlad
26-го августа 2012 в 11:09
2012-08-26T23:09:24+00:00
Дополнительно
Источник
Редактировать
#17048948

Это разные понятия. Они довольно часто пересекаются, но они разные.

Рекурсия происходит, когда функция вызывает сама себя, прямо или косвенно. Это все.

Пример:

a -> call a
a -> call b, b -> call c, c -> call a

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

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

5
0
 ninjagecko
ninjagecko
26-го августа 2012 в 8:46
2012-08-26T20:46:02+00:00
Дополнительно
Источник
Редактировать
#17048942

Рекурсия не имеет абсолютно никакого отношения к мемоизации и динамическому программированию; это совершенно отдельная концепция.

В противном случае, это дублирующий вопрос: https://stackoverflow.com/questions/6164629/dynamic-programming-and-memoization-top-down-vs-bottom-up-approaches.

 Community
Community
Редактировал ответ 23-го мая 2017 в 12:25
-2
0
Добавить вопрос
Категории
Все
Технологий
Культура / Отдых
Жизнь / Искусство
Наука
Профессии
Бизнес
Пользователи
Все
Новые
Популярные
1
Ilya Smirnov
Зарегистрирован 5 дней назад
2
Денис Васьков
Зарегистрирован 1 неделю назад
3
Dima Patrushev
Зарегистрирован 1 неделю назад
4
sirojidddin otaboyev
Зарегистрирован 2 недели назад
5
Елена Гайдамамакинат
Зарегистрирован 2 недели назад
ID
JA
KO
RU
© kzen.dev 2023
Источник
stackoverflow.com
под лицензией cc by-sa 3.0 с атрибуцией