In article
<e3a4921b-de93-42ca-ab8c-44407eed5...@e1g2000pra.googlegroups.com>,
denes.cselovs
...@gmail.com wrote:
> Hi Everyone!
> I'm writing an app with some math parts, and I wrote the following fn
> for Lagrange interpolation. It's the slowest part of the app, used for
> locating peak points of curves. I hope the comments make it clear. Any
> suggestions how to make it faster?
> ;;; The following operator creates the Lagrange interpolation
> ;;; function for a curve defined by an arbitrary number of points.
> ;;; If we have 3 points, with coordinates x0 y0, x1 y1, x2 y2, then:
> ;;; poly0(x) = (x-x1)*(x-x2)/(x0-x1)*(x0-x2)
> ;;; poly1(x) = (x-x0)*(x-x2)/(x1-x0)*(x1-x2)
> ;;; poly2(x) = (x-x0)*(x-x1)/(x2-x0)*(x2-x1)
> ;;; lag(x) = y0*poly0(x) + y1*poly1(x) + y2*poly2(x)
> ;; LAGRANGE-FN returns a function for Lagrange interpolation on the
> ;; given set of coordinates. It pre-calculates some staying parts of
> ;; the polynoms and uses some combination to speed up the 'runtime'
> ;; part.
> (defun lagrange-fn (coords)
> (let* ((xs (mapcar #'first coords)) ; x coords (wcont)
> (permanent (mapcar #'(lambda (coord xx) ; permanent part of
> polynoms:
> (/ (second coord) ; Ys / denominators
> (reduce #'*
> (mapcar #'(lambda (es)
> (- xx es))
> (remove xx xs)))))
> coords xs)))
> (labels ((given-x? (x coords) ; if x = the x of
> one of the
> (if (consp coords) ; coords: return the
> (if (= x (first (car coords))) ; corresponding y
> (second (car coords)) ; (the following algorythm
> (given-x? x (cdr coords))) ; assumes that x - xn
> nil))) ; is never zero!
> (lambda (x)
> (let ((given (given-x? x coords)))
> (if given
> given
> (let* ((combinations
> (mapcar #'(lambda (xx) ; calculate all x - xn
> (- x xx)) xs)) ; combinations once
> (their-sum ; and multiply them:
> (reduce #'* combinations))) ; ONE division/polynom
> (reduce #'+ (mapcar #'(lambda (permanent-part xx)
> (* permanent-part
> (/ their-sum xx)))
> permanent combinations)))))))))
> Thanks
> d
One of the problems is that combinations like
reduce mapcar remove are nice to write down,
but come with a cost - especially when the compiler
won't be able to optimize those.
There is an attempt be the SERIES package to
have such computations optimizable in portable
code.
Unfortunately using LOOP will improve the runtime
of the code, too.
So you have a list
a) then you remove things
b) do computation with the remaining objects
c) and then you reduce the result
this means that you traverse/create a list possibly several times.
(loop with result = initial-result-value
for a in list ; map over the list
when (keep-p a) ; filter out
do (setf result (reduction-op (op a) result)
finally (return result))
for example
(loop with result = ()
for a in list
when (evenp a)
do (setf result (* (sqrt a) result))
finally (return result))
for + the LOOP macro supports SUM
Above LOOP will traverse the list only once
and will not generate intermediate lists.
You can create a nice function or macro out of that code
to keep the LOOPs from invading your code.
A macro would ensure 'inlining', but you can also
create a function and inline it - using
the right declarations and using a Lisp compiler that
would do the inlining (there are several).
Once your algorithms are lighter, you can also
use one of the more advanced compilers to produce
optimization hints (CMUCL, SBCL, SCL, LispWorks, ...).
From there you can see where added declaration would
help to create type-specific code.
--
http://lispm.dyndns.org/