понимание процесса простого динамического программирования

#java #algorithm #dynamic-programming

#java #алгоритм #динамическое программирование

Вопрос:

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

 int[] fibMap = new int[max]
int fibo(int i){
  if(i == 0) return 0;
  if(i == 1) return 1;
  if( fibMap[i] != 0) return fibMap[i]; // return cached result
  fibMap[i] = fibo(i-1) fibo(i-2);      //Cache result
  return fibMap[i];
}
  

Я нашел этот код в одном из справочников по Java, которые я использую, но мне трудно понять, как будет работать эта программа. Скажем, если мы захотим вычислить простой фибо (3) или фибо (5), не мог бы кто-нибудь, пожалуйста, объяснить мне, как программа будет кэшировать результат и как общий поток будет работать для этой проблемы по сравнению с обычным рекурсивным подходом без DP, как показано ниже?

 int fibo(int i){
   if(i == 0) return 0;
   if( i == 1) return 1;
   return fibo(i-1)   fibo(i-2);
}
  

Комментарии:

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

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

3. Да, я изложил это на бумаге. Чего я не понимаю, так это когда в первом цикле if, где написано fibMap[i] != 0, затем возвращает fibMap[i] . Если, скажем, i = 5, то в первую очередь fibMap[5] не будет иметь правильного значения, потому что в массиве еще ничего не сохранено. Как это все еще продолжается?

4. @JimZilla: поскольку значение неверно, затем он пытается оценить fibo(i-1) и fibo(i-2) , и когда они завершены, он сохраняет значение, а также возвращает его. Это помогает?

5. Не по теме, но не удержался: первые две строки можно заменить одной строкой: if (i < 2) return i;

Ответ №1:

Ваш код

 int fibo(int i){
  if(i == 0) return 0;
  if(i == 1) return 1;
  if( fibMap[i] != 0) return fibMap[i]; // return cached result
  fibMap[i] = fibo(i-1) fibo(i-2);      //Cache result
  return fibMap[i];
}
  

или, что эквивалентно

 int fibo(int i){
  if(i == 0) return 0;
  if(i == 1) return 1;
  if( fibMap[i] == 0)
    fibMap[i] = fibo(i-1) fibo(i-2);      //Cache result
  return fibMap[i];
}
  

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

Комментарии:

1. Спасибо, сэр. Немного поломав голову, я немного понял. Скажем, если бы нам пришлось оценивать fibo (3). Поскольку fibMap[3] == 0, он будет разделен на fibMap [3] = fibo(2) fibo (1) . теперь у fibo(2) будет новый результат в fibMap[2] = fibo(1) fibo(0), который возвращает значение 1 обратно в fibMap[2] . Теперь fibMap[3] = fibMap[2] fibo(1), что равно 1 1 = 2. Правильно ли это? Если да, то как помогает сохранение результата в массиве? Я знаю, что это глупый вопрос, но хранятся ли эти кэшированные результаты вне функции?

2. @JimZilla: Да, вы правы. Если значение найдено в массиве, fibo оно больше не вызывается. Поскольку в большинстве случаев каждый вызов генерирует дополнительные 2 вызова, вы получите экспоненциальное количество вызовов fibo , если вы не сохраните результат где-нибудь, вот почему это помогает.

3. ах! ПОЛУЧИТЕ СЕЙЧАС. Большое спасибо!