LINUX.ORG.RU

Хвостовая рекурсия.

 


2

3

Разбираюсь с хвостовой рекурсией.

Реализовал алгоритм поиска пирамидального числа:

-- recursion version
--
fermaPyr 0 = 0
fermaPyr n = fermaTria n + fermaTria (n - 1)

fermaTria 0 = 0
fermaTria n = n + fermaTria (n - 1)

-- tail recursion version
-- 
fermaPyrAcc n = let fAcc 0 a = a
                    fAcc n a = fAcc (n - 1) (fermaTriaAcc n + a)
                in fAcc n 0

fermaTriaAcc n = let fAcc 0 a = a
                     fAcc n a = fAcc (n - 1) (n + a)
                 in fAcc n 0

-- tail recursion ver.2
--
fermaPyrAcc2 0 = 0
fermaPyrAcc2 n = fermaTriaAcc n + fermaTriaAcc (n - 1)



main = do
    let a = fermaPyr 1000000
        b = fermaPyrAcc 10000
        c = fermaPyrAcc2 1000000
    putStrLn $ show a
    --putStrLn $ show b
    --putStrLn $ show c

Запуск варианта а:

     177,963,104 bytes allocated in the heap
     187,666,896 bytes copied during GC
      36,388,928 bytes maximum residency (8 sample(s))
          36,384 bytes maximum slop
              82 MB total memory in use (0 MB lost due to fragmentation)

                                    Tot time (elapsed)  Avg pause  Max pause
  Gen  0       237 colls,     0 par    0.13s    0.13s     0.0005s    0.0014s
  Gen  1         8 colls,     0 par    0.09s    0.10s     0.0119s    0.0285s

  INIT    time    0.00s  (  0.00s elapsed)
  MUT     time    0.10s  (  0.10s elapsed)
  GC      time    0.22s  (  0.22s elapsed)
  RP      time    0.00s  (  0.00s elapsed)
  PROF    time    0.00s  (  0.00s elapsed)
  EXIT    time    0.01s  (  0.01s elapsed)
  Total   time    0.33s  (  0.33s elapsed)

  %GC     time      67.7%  (67.7% elapsed)

  Alloc rate    1,831,837,945 bytes per MUT second

  Productivity  32.3% of total user, 31.9% of total elapsed

Всё нормально, расход памяти, как и ожидалось, немалый, время исполнения тоже.

Запускаю вариант b:

   3,201,017,336 bytes allocated in the heap
         697,536 bytes copied during GC
          46,240 bytes maximum residency (2 sample(s))
          23,392 bytes maximum slop
               1 MB total memory in use (0 MB lost due to fragmentation)

                                    Tot time (elapsed)  Avg pause  Max pause
  Gen  0      6104 colls,     0 par    0.03s    0.03s     0.0000s    0.0000s
  Gen  1         2 colls,     0 par    0.00s    0.00s     0.0001s    0.0002s

  INIT    time    0.00s  (  0.00s elapsed)
  MUT     time    1.96s  (  1.98s elapsed)
  GC      time    0.03s  (  0.03s elapsed)
  RP      time    0.00s  (  0.00s elapsed)
  PROF    time    0.00s  (  0.00s elapsed)
  EXIT    time    0.00s  (  0.00s elapsed)
  Total   time    1.99s  (  2.01s elapsed)

  %GC     time       1.7%  (1.7% elapsed)

  Alloc rate    1,635,694,726 bytes per MUT second

  Productivity  98.3% of total user, 97.4% of total elapsed

Здесь, памяти расходуется гораздо меньше, но время исполнения… при аргументе на 2 порядка меньше, чем в других вариантах, работает в несколько раз медленнее. Увеличив аргумент на порядок, я уже не дожидался окончания расчётов.

Вариант с:

     128,057,440 bytes allocated in the heap
          29,752 bytes copied during GC
          46,240 bytes maximum residency (2 sample(s))
          23,392 bytes maximum slop
               1 MB total memory in use (0 MB lost due to fragmentation)

                                    Tot time (elapsed)  Avg pause  Max pause
  Gen  0       243 colls,     0 par    0.00s    0.00s     0.0000s    0.0001s
  Gen  1         2 colls,     0 par    0.00s    0.00s     0.0001s    0.0002s

  INIT    time    0.00s  (  0.00s elapsed)
  MUT     time    0.08s  (  0.09s elapsed)
  GC      time    0.00s  (  0.00s elapsed)
  RP      time    0.00s  (  0.00s elapsed)
  PROF    time    0.00s  (  0.00s elapsed)
  EXIT    time    0.00s  (  0.00s elapsed)
  Total   time    0.08s  (  0.09s elapsed)

  %GC     time       2.0%  (2.1% elapsed)

  Alloc rate    1,571,481,972 bytes per MUT second

  Productivity  97.8% of total user, 94.8% of total elapsed

Самый быстрый и меньше всего расходует память.

Для меня не очевидна разница между вариантами b и с. Почему оно так? Я ожидал, что вариант b будет самым лучшим т.к. в функции fermaPyrAcc2 нет хвостовой рекурсии, она только в её внутреннем цикле.


Ответ на: комментарий от HolyBoy

Смысл этого упражнения был в том, чтобы понять, как работает хвостовая рекурсия.

Да ты оптимист. Тема очень непростая. Могу сказать одну вещь. Если ты возьмешь любую из этих рекурсивных функций, да перепишешь их так, что они будут возвращать не конечный результат, а его вычисление в монаде Cont, то ты автоматом получишь хвостовую рекурсию. Только я не думаю, что от этого станет легче ;)

dave ★★★★★
()

Просто я хочу предупредить, что ничего плохого не будет в том, если возникнут сложности с пониманием хвостовой рекурсии)

dave ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.