Я изучаю SICP и написал две процедуры для вычисления суммы 1 / n ^ 2, первая генерирует рекурсивный процесс, а вторая - итерационный процесс:
(define (sum-rec a b)
(if (> a b)
0
(exact->inexact (+ (/ 1 (* a a)) (sum-rec (1+ a) b)))))
(define (sum-it a b)
(define (sum_iter a tot)
(if (> a b)
tot
(sum_iter (1+ a) (+ (/ 1 (* a a)) tot))))
(exact->inexact (sum_iter a 0)))
Я проверил, что обе процедуры дают одинаковые результаты при вызове с небольшими значениями b
, и что результат приближается к $ pi ^ 2/6 $, поскольку b
становится больше, как и ожидалось.
Но удивительно, что вызов (sum-rec 1 250000)
почти мгновенный, тогда как вызов (sum-it 1 250000)
занимает вечность.
Есть ли объяснение этому?
2 ответа
Как упоминалось в комментариях, sum-it
в его нынешнем виде добавляет числа с использованием точной арифметики, которая медленнее, чем неточная арифметика, используемая в sum-rec
. Чтобы сделать эквивалентное сравнение, вот как вы должны это реализовать:
(define (sum-it a b)
(define (sum_iter a tot)
(if (> a b)
tot
(sum_iter (1+ a) (+ (/ 1.0 (* a a)) tot))))
(sum_iter a 0))
Обратите внимание, что замена 1
на 1.0
заставляет интерпретатора использовать неточную арифметику. Теперь это вернется немедленно:
(sum-it 1 250000)
=> 1.6449300668562465
Вы можете перефразировать обе эти версии, чтобы они точно или неточно выполняли арифметику, просто контролируя, какое значение они используют для нуля, и полагаясь на правила заражения. Эти два в Racket, который не имеет 1+
по умолчанию, но имеет хороший синтаксис для необязательных аргументов со значениями по умолчанию:
(define (sum-rec low high (zero 0.0))
(let recurse ([i low])
(if (> i high)
zero
(+ (/ 1 (* i i)) (recurse (+ i 1))))))
(define (sum-iter low high (zero 0.0))
(let iterate ([i low] [accum zero])
(if (> i high)
accum
(iterate (+ i 1) (+ (/ 1 (* i i)) accum)))))
Преимущество этого в том, что вы легко можете увидеть разницу в производительности для обеих версий. Недостаток в том, что вам нужен действительно умный компилятор, чтобы иметь возможность оптимизировать числовые операции здесь (я думаю, даже если бы он знал, что low
и high
были целыми числами машины, он должен был бы сделать вывод, что zero
будет числовым типом и будет генерировать копии тела функции для всех возможных типов).
Похожие вопросы
Новые вопросы
scheme
Scheme - это функциональный язык программирования в семействе Lisp, тесно смоделированный на основе лямбда-исчисления с активным (аппликативным порядком) вычислением. ПО ВОПРОСАМ о схемах URL ПОЖАЛУЙСТА, используйте тег "URL-схема".