Предположим, у вас есть функция CalcDistance(i)
, которая вычисляет «расстояние» до 1
. Например, CalcDistance(1) == 0
и CalcDistance(13) == 9
. Вот наивная рекурсивная реализация этой функции (в C #):
public static int CalcDistance(long i)
{
if (i == 1)
return 0;
return (i % 2 == 0) ? CalcDistance(i / 2) + 1 : CalcDistance(3 * i + 1) + 1;
}
Проблема в том, что эта функция должна вычислять расстояние многих чисел снова и снова. Вы можете сделать его немного умнее (и намного быстрее), предоставив ему память. Например, давайте создадим статический массив, который может хранить расстояние для первого миллиона чисел:
static int[] list = new int[1000000];
Мы предварительно заполняем каждое значение в списке -1
, чтобы указать, что значение для этой позиции еще не рассчитано. После этого мы можем оптимизировать функцию CalcDistance()
:
public static int CalcDistance(long i)
{
if (i == 1)
return 0;
if (i >= 1000000)
return (i % 2 == 0) ? CalcDistance(i / 2) + 1 : CalcDistance(3 * i + 1) + 1;
if (list[i] == -1)
list[i] = (i % 2 == 0) ? CalcDistance(i / 2) + 1: CalcDistance(3 * i + 1) + 1;
return list[i];
}
Если i >= 1000000
, то мы не можем использовать наш список, поэтому мы всегда должны его вычислять. Если i < 1000000
, то мы проверяем, есть ли значение в списке. Если нет, мы сначала вычисляем его и сохраняем в списке. В противном случае мы просто возвращаем значение из списка. С этим кодом для обработки всех миллионов чисел потребовалось около 120 мс.
Это очень простой пример запоминания. Я использую простой список для хранения промежуточных значений в этом примере. При необходимости вы можете использовать более сложные структуры данных, такие как хеш-таблицы, векторы или графики.