вторник, 11 мая 2010 г.

Немножко упражнений на Common Lisp

Решил поупражняться тут в Lisp'е, пописать математических функций в функциональном же стиле.

1. Вычисление квадратного корня

Я специально определил собственный вариант функции abs, мне так интереснее :)

;;;; Приближённое вычисление квадратного корня

;; Модуль числа
(defun abs-value (num)
(if (> num 0.0)
num
(- 0.0 num)))

;; Логическая функция, возвращающая истину,
;; если числа a и b отличаются друг от друга
;; менее, чем на precision
(defun precision-ok (a b precision)
(< (abs-value (- a b)) precision))

;; Рекурсивная функция, использующая root
;; как приближённое значение корня num.
;; Функция вызывает сама себя до тех пор,
;; пока точность результата не достигнет precision
(defun square-root-iter (num root precision)
(if (precision-ok num (* root root) precision)
root
(square-root-iter num (/ (+ (/ num root) root) 2) precision)))

;; "Лицо" функции вычисления квадратного корня
;; Использует 1.0 в качестве приближённого значения корня
;; и точность 0.001
(defun square-root (num)
(square-root-iter num 1.0 0.001))

;; Вычисление квадратного корня 25
(square-root 25.0)

2. Вычисление числа из ряда Фибоначчи

Для примера я решил вычислить двдацатитысячное по порядку число в ряду Фибоначчи.

;;;; Вычисление 20000-го числа в ряду Фибоначчи

;; Рекурсивная функция, вычисляющая число Фибоначчи
;; Вызывает себя num раз, используя a и b в качестве
;; двух предыдущих чисел в ряду
(defun fib-iter (a b num)
(if (eql num 1)
a
(fib-iter b (+ a b) (- num 1))))

;; "Лицо" функции вычисления числа Фибоначчи
(defun fib (num)
(fib-iter 1 1 num))

;; Вычисление 20000-го числа в ряду Фибоначчи
(fib 20000)

3. Вычисление числа Фи ("Золотого сечения") двумя способами

Сначала я воспользовался для вычислений уже полученной в прошлом примере функцией для вычисления чисел Фибоначчи, а затем написал второй вариант функции, более оптимизированный.

;;;; Сравнение двух способов вычисления числа Фи
;;;; - "Золотого сечения"

;; Рекурсивная функция, вычисляющая число Фибоначчи
;; Вызывает себя num раз, используя a и b в качестве
;; двух предыдущих чисел в ряду
(defun fib-iter (a b num)
(if (eql num 1)
a
(fib-iter b (+ a b) (- num 1))))

;; "Лицо" функции вычисления числа Фибоначчи
(defun fib (num)
(fib-iter 1 1 num))

;; Вычисление числа Фи через два вызова функции,
;; вычисляющей число в ряду Фибоначчи
(defun phi ()
(* 1.0 (/ (fib 2000) (fib 1999))))

;; Вычисление числа Фи
;; Совмещённое вычисление двух последовательных
;; чисел в ряду Фибоначчи с последующим делением
;; следующего числа на предыдущее
(defun phi2-iter (a b num)
(if (eql num 1)
(* 1.0 (/ b a))
(phi2-iter b (+ a b) (- num 1))))

;; "Лицо" функции вычисления числа Фи
(defun phi2 ()
(phi2-iter 1 1 2000))

;; Сравниваем результаты вычисления обеих функций,
;; попутно измеряя время, потраченное на вычисления
;; Первая функция должна быть медленнее в 2 раза
;; второй
(eql (time (phi)) (time (phi2)))

А вот и результаты работы программы:

Evaluation took:
0.001 seconds of real time
0.000000 seconds of total run time (0.000000 user, 0.000000 system)
0.00% CPU
2,279,699 processor cycles
400,944 bytes consed
Evaluation took:
0.001 seconds of real time
0.000000 seconds of total run time (0.000000 user, 0.000000 system)
0.00% CPU
1,470,207 processor cycles
204,584 bytes consed
T

Первая функция не только отработала медленнее, но и затребовала для своего выполнения почти в два раза больше памяти.

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

Отправить комментарий