Відповіді:
Відповідна стаття щодо Programming.Guide: Динамічне програмування проти запам'ятовування проти табуляції
У чому різниця між запам'ятовуванням та динамічним програмуванням?
Пам'ять - це термін, що описує оптимізаційну техніку, де ви кешуєте раніше обчислені результати та повертаєте кешований результат, коли знову потрібні ті ж обчислення.
Динамічне програмування - це методика для вирішення завдань рекурсивного характеру, ітеративно і застосовується, коли обчислення субпроблем перекриваються.
Динамічне програмування, як правило, реалізується за допомогою табуляції, але також може бути реалізовано за допомогою запам'ятовування. Отже, як бачите, жоден не є "підмножиною" іншого.
Розумне наступне питання: Чим відрізняється таблиця (типова методика динамічного програмування) від запам'ятовування?
Коли ви вирішуєте задачу динамічного програмування за допомогою табуляції, ви вирішуєте проблему " знизу вгору ", тобто спочатку вирішуючи всі пов'язані підпроблеми, як правило, заповнюючи n- розмірну таблицю. Виходячи з результатів у таблиці, потім розраховується рішення "верхньої" / оригінальної проблеми.
Якщо ви використовуєте запам'ятовування для вирішення проблеми, ви робите це, підтримуючи карту вже вирішених підзадач. Ви робите це " зверху вниз " в тому сенсі, що ви вирішуєте спочатку "верхню" проблему (яка зазвичай повторюється вниз для вирішення підзадач).
Добрий слайд звідси (посилання зараз мертве, проте слайд все-таки хороший):
- Якщо всі підпрограми повинні бути вирішені принаймні один раз, алгоритм динамічного програмування знизу вгору зазвичай перевершує запам'ятовуваний алгоритм зверху вниз постійним коефіцієнтом
- Немає накладних витрат на рекурсію і менше накладних витрат на підтримку столу
- Є деякі проблеми, для яких звичайний шаблон доступу до таблиці в алгоритмі динамічного програмування можна використовувати, щоб ще більше зменшити потреби в часі або просторі.
- Якщо деякі підпрограми в просторі підпроблеми взагалі не потрібно вирішувати, запам'ятоване рішення має перевагу у вирішенні лише тих підпроблем, які обов'язково потрібні
Додаткові ресурси:
Динамічне програмування - це алгоритмічна парадигма, яка вирішує задану складну задачу шляхом розбиття її на підпрограми та зберігає результати підпроблем, щоб уникнути повторного обчислення одних і тих же результатів.
http://www.geeksforgeeks.org/dynamic-programming-set-1/
Пам'ять - це простий метод відстеження раніше розв’язаних рішень (часто реалізується як пара значень хеш-ключа, на відміну від табуляції, яка часто базується на масивах), щоб вони не перераховувались, коли їх знову зустрічали. Її можна використовувати як в методах знизу вгору, так і вгорі.
Дивіться цю дискусію щодо запам’ятовування проти табуляції.
Таким чином, динамічне програмування - це метод вирішення певних класів задач шляхом вирішення співвідношень рекурсії / рекурсії та зберігання раніше знайдених рішень через таблицю чи запам'ятовування. Пам'ять - це метод відстеження рішень раніше вирішених проблем і може використовуватися з будь-якою функцією, яка має унікальні детерміновані рішення для заданого набору входів.
Динамічне програмування часто називають Пам'ять!
Пам'ять - це техніка зверху вниз (почніть розв’язувати задану задачу, розбивши її), а динамічне програмування - це техніка знизу вгору (починайте вирішувати з тривіальної підпроблеми, до заданої задачі)
DP знаходить рішення, починаючи з базових випадків і просувається вгору. DP вирішує всі підпроблеми, оскільки робить це знизу вгору
На відміну від Memoization, який вирішує лише необхідні підпроблеми
DP має потенціал для перетворення рівномірних рішень грубої сили в алгоритми поліноміального часу.
DP може бути набагато ефективнішим, оскільки його ітераційний
Навпаки, спогади повинні платити за (часто значні) накладні витрати через рекурсію.
Щоб бути простішим, пам'ять використовує підхід зверху вниз для вирішення проблеми, тобто починається з основної (головної) проблеми, потім розбиває її на підпроблеми і вирішує ці підпроблеми аналогічно. У такому підході одна і та ж підпроблема може виникати багаторазово і вимагає більше циклу процесора, отже збільшуючи часову складність. Тоді як в динамічному програмуванні одна і та ж підпроблема не буде вирішена багаторазово, але попередній результат буде використаний для оптимізації рішення.
(1) Пам'ять та DP - це концептуально - це те саме. Тому що: розглянемо визначення DP: "підпрограми, що перекриваються" "та оптимальна підструктура". Пам'ять повністю володіє цими 2.
(2) Пам'ять - це DP з ризиком переповнення стека - рекурсія глибока. DP знизу вгору не має цього ризику.
(3) Для пам’яті потрібна хеш-таблиця. Отже, додатковий простір та деякий час пошуку.
Отже, щоб відповісти на питання:
- Концептуально , (1) означає, що вони однакові.
- Враховуючи (2), якщо ви дійсно хочете, запам'ятовування - це підмножина DP, в тому сенсі, що проблема, що вирішується запам'ятовуванням, може бути вирішена DP, але проблема, що вирішується DP, не може бути вирішена шляхом запам'ятовування (тому що це може стек переповнення).
- Враховуючи (3), вони мають незначні відмінності у роботі.
З Вікіпедії:
Пам'ятка
В обчислювальній роботі запам'ятовування - це технологія оптимізації, що застосовується головним чином для прискорення роботи комп'ютерних програм за допомогою функціональних викликів, щоб не повторювати обчислення результатів для раніше оброблених входів.
Динамічне програмування
У математиці та інформатиці динамічне програмування - це метод вирішення складних задач шляхом їх розбиття на більш прості підпрограми.
Розбиваючи проблему на менші / простіші підпрограми, ми часто стикаємося з однією і тією ж підпроблемою більше, ніж один раз - тому ми використовуємо Пам'ять для збереження результатів попередніх обчислень, тому нам не потрібно їх повторювати.
Динамічне програмування часто стикається з ситуаціями, коли має сенс використовувати запам'ятовування, але ви можете використовувати будь-яку техніку, не обов'язково використовуючи іншу.
Як пам'ять, так і динамічне програмування вирішує окрему підпроблему лише один раз.
Пам'ять використовує рекурсію і працює зверху вниз, тоді як динамічне програмування рухається в зворотному напрямку, вирішуючи проблему знизу вгору.
Нижче наведена цікава аналогія -
Зверху вниз - спочатку ви скажете, що я захоплю світ. Як ти це зробиш? Ви кажете, що я перейму Азію першим. Як ти це зробиш? Першим я візьму на себе Індію. Я стану головним міністром Делі, тощо.
Знизу вгору - Ви кажете, що я стану КС Делі. Тоді візьму на себе Індію, потім усі інші країни Азії і нарешті я візьму на себе світ.
Я хотів би піти з прикладом ;
Проблема:
Ви піднімаєтесь по сходовій шафі. Щоб дійти до вершини, потрібно п ять кроків.
Кожен раз ви можете піднятися на 1 або 2 сходинки. Скількома різними способами можна піднятися на вершину?
Рекурсія з пам'яттю
Таким чином ми обрізаємо (видалення зайвого матеріалу з дерева або чагарника) дерево рекурсії за допомогою масиву пам’яті та зменшуємо розмір дерева рекурсії до nn.
public class Solution {
public int climbStairs(int n) {
int memo[] = new int[n + 1];
return climb_Stairs(0, n, memo);
}
public int climb_Stairs(int i, int n, int memo[]) {
if (i > n) {
return 0;
}
if (i == n) {
return 1;
}
if (memo[i] > 0) {
return memo[i];
}
memo[i] = climb_Stairs(i + 1, n, memo) + climb_Stairs(i + 2, n, memo);
return memo[i];
}
}
Динамічне програмування
Як ми бачимо, ця проблема може бути розбита на підпрограми, і вона містить оптимальну властивість підструктури, тобто її оптимальне рішення може бути побудовано ефективно з оптимальних рішень її підпроблем, ми можемо використовувати динамічне програмування для вирішення цієї проблеми.
public class Solution {
public int climbStairs(int n) {
if (n == 1) {
return 1;
}
int[] dp = new int[n + 1];
dp[1] = 1;
dp[2] = 2;
for (int i = 3; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
}
Приклади беруть із https://leetcode.com/problems/climbing-stairs/
Подумайте лише про два способи,
У Пам'яті ми переходимо з (1.), де ми зберігаємо кожен виклик функції в кеш і повертаємося звідти. Це трохи дорого, оскільки включає рекурсивні дзвінки.
У динамічному програмуванні ми йдемо з (2.), де ми підтримуємо таблицю, знизу вгору, вирішуючи підпрограми, використовуючи дані, збережені в таблиці, що зазвичай називають dp-таблицею.
Примітка:
Обидва застосовні до проблем із перезакритими підзадачами.
Пам'ять виконує порівняно погано з DP, через накладні витрати, що виникають під час рекурсивних викликів функцій.
У динамічному програмуванні ,
У Запам'ятовування ,