Должен ли я использовать рекурсию или memoization для алгоритма?

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

Ответ 1

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

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

Ответ 2

Они не являются взаимоисключающими. Вы можете использовать их оба.

Лично я сначала создаю базовую рекурсивную функцию и добавляю memoization впоследствии в качестве шага оптимизации.

Ответ 3

Используемое эмпирическое правило основывается на количестве перекрытия, которое имеют подзадачи. Если вы вычисляете число фибоначчи (классический пример рекурсии), при использовании рекурсии много ненужного пересчета.

Например, для вычисления F (4) мне нужно знать F (3) и F (2), поэтому я вычисляю F (3), вычисляя F (2) и F (1) и т.д. Если я использовал рекурсию, я просто вычислил F (2) и большинство других F (n) дважды. Если я использую memoization, я могу просто посмотреть значение вверх.

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

Ответ 4

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

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

Ответ 5

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

Ответ 6

Не уверен, что могу сказать, не зная проблемы. Часто вы хотите использовать memoization с рекурсией. Тем не менее, мемуаризация, вероятно, будет значительно быстрее, чем рекурсия, если вы действительно можете использовать ее в качестве альтернативного решения. У них обоих есть проблемы с производительностью, но они различаются по-разному с характером проблемы/размера ввода.

Ответ 7

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

Ответ 8

Это зависит от того, что вы собираетесь делать. динамическое программирование (memoization) почти всегда быстрее. Часто LOT. (т.е. кубический в квадрат или экспоненциальный для поли), но, по моему опыту, рекурсия легче читать и отлаживать.

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

Также, (третья рука?), я нахожу, что легче найти динамическое решение после того, как я придумал рекурсивный, поэтому я в конечном итоге делаю то и другое. Но если у вас уже есть оба решения, Dynamic может быть лучшим выбором.

Я не уверен, был ли я полезен, но ты здесь. Как и во многих вариантах выбора алгоритма, YMMV.

Ответ 9

Если ваша проблема является рекурсивной, какой выбор у вас есть, но рекурсивно?

Вы можете написать свою рекурсивную функцию таким образом, чтобы короткие замыкания использовались с помощью memoization, чтобы получить максимальную скорость для второго вызова.

Ответ 10

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

Ответ 11

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

  • Время выполнения
  • читабельность

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

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

Если обе версии имеют одинаковое время выполнения и такую ​​же читаемость, нет причин выбирать друг друга. В этом случае вам нужно проверить другие вещи: изменится ли этот код? Как насчет обслуживания? Вам удобны рекурсивные алгоритмы или они дают вам кошмары?

Ответ 12

var memoizer = function (fund, memo) {
    var shell = function (arg) {
        if (typeof memo[arg] !== 'number') {
            memo[arg] = fund(shell, arg);
        }
        return memo[arg];
    };
    return shell;
};

var fibonacci = memoizer(function (recur, n) { return recur(n - 1) + recur(n - 2); }, [0, 1]);

используйте оба!

Ответ 13

Я не согласен с утверждением Томалака, что с рекурсивной проблемой у вас нет выбора, кроме как рекурсивно?

Лучшим примером является вышеупомянутая серия Фибоначчи. На моем компьютере рекурсивная версия F (45) (F для Fibonacci) занимает 15 секунд для дополнений 2269806339, итеративная версия принимает 43 дополнения и выполняется за несколько микросекунд.

Другим известным примером является Башни Ханоя. После вашего класса по теме может показаться, что рекурсия - единственный способ ее решить. Но даже здесь есть итеративное решение, хотя оно не так очевидно, как рекурсивное. Тем не менее, итеративный процесс быстрее, главным образом потому, что не требуется дорогостоящих операций стека.

Если вас интересует нерекурсивная версия Towers of Hamoi, здесь исходный код Delphi:

procedure TForm1.TowersOfHanoi(Ndisks: Word);
var
  I: LongWord;
begin
  for I := 1 to (1 shl Ndisks) do
    Memo1.Lines.Add(Format('%4d: move from pole %d to pole %d',
      [I, (I and (I - 1)) mod 3, (I or (I - 1) + 1) mod 3]));
  Memo1.Lines.Add('done')
end;

Ответ 14

Объедините оба. Оптимизируйте свое рекурсивное решение, используя memoization. Для чего предназначена мемуаризация. Для использования пространства памяти для ускорения рекурсии.