Hi Laurent,
I am curious to see how fast the libgmp versions of factorial
and binomial are on your computer. Om my computer libgmp
is much faster than the Racket one.
Since the libgmp versions converts a Racket bignum into
a mpz, there is some overhead, so a proper solution
would be to use a Racket version for small numbers,
and then call the libgmp for large numbers.
An even better solution would be to have a builtin bignum->mpz.
/Jens Axel
#lang racket
(require math/private/bigfloat/gmp)
(require (for-syntax racket/base)
ffi/unsafe
ffi/unsafe/define
racket/runtime-path)
(define-runtime-path libgmp-so
(case (system-type)
[(macosx) '(so "libgmp.10.dylib")]
[(windows) '(so "libgmp-10.dll")]
[else '(so "libgmp")]))
(define (get-gmp-fun name type)
(get-ffi-obj name gmp-lib type (make-not-available name)))
(define mpz-fac-ui
(get-gmp-fun '__gmpz_fac_ui (_fun _mpz-pointer _int -> _mpz-pointer)))
(define (fact n)
(define n! (new-mpz))
(mpz-fac-ui n! n)
(mpz->integer n!))
(define mpz-bin-ui
(get-gmp-fun '__gmpz_bin_ui
(_fun _mpz-pointer _mpz-pointer _int -> _mpz-pointer)))
(define (bin n k)
(define b (new-mpz))
(mpz-bin-ui b (integer->mpz n) k)
(mpz->integer b))
(require (only-in math factorial))
(time (for ([i (in-range 2000 2100)])
(factorial i)))
(time (for ([i (in-range 2000 2100)])
(fact i)))
(require (only-in math binomial))
(time (for ([n 100])
(bin 10000 4000)))
(time (for ([n 100])
(binomial 10000 4000)))
____________________
Racket Users list:
http://lists.racket-lang.org/users
> Would it make sense to use memoization more broadly for the definition of the factorial> in the math lib, maybe with a `provide'd parameter to control how many values can be> memoized?An interesting question. First of all it seems that especially binomialcan be improved. Second I think there is a difference betweennon-permanent caching and permanent caching. I see no problemwith non-permanent caching. For caching between calls I am notsure, what is the best solution for a general purpose library.I lean towards non-caching functions as standard, but it wouldbe nice to offer both versions in the library. The same problemarises in prime? . Having a large table of small primesimprove the running time, but is a reasonable tablesize 10^3, 10^4 or 10^5 ?
(define (iota m n)
(let loop ((result '())
(n (- n 1)))
(if (<= m n)
(loop (cons n result)
(- n 1))
result)))
(define (partial-factorial m n)
;; computes the product (m+1) * ... * (n-1) * n
(if (< (- n m) 10)
(apply * (iota (+ m 1) (+ n 1)))
(* (partial-factorial m (quotient (+ m n) 2))
(partial-factorial (quotient (+ m n) 2) n))))
(define (factorial n)
(partial-factorial 0 n))
In Gambit, even in the interpreter, this is pretty fast:
(define a (time (factorial 1000000)))
(time (factorial 1000000))
But if you write all 10 cases, aren't the lists constructed anyway before calling `*'?
(for/product ([p (in-range (+ m 1) (+ n 1))]) p)
On Tue, Apr 9, 2013 at 2:42 PM, Matthew Flatt <mfl...@cs.utah.edu> wrote:
(for/product ([p (in-range (+ m 1) (+ n 1))]) p)
But I don't fully understand why a simple (for/product ([p (in-range 1 (add1 n))]) p) is as fast as the iota variants.
while the for version should be almost all big x big.
For Newton's method, you'd need a differentiable inverse, but computing
the gamma function's inverse is hard in the first place.
> But there might be a bound on the error in Stirling's approximation that
> you cna use teo determine whether that's good enough for your
> application (if you have one).
Use the first omitted series term as the signed relative error bound. If
this is e, then exp(|e|)-1 (which is nearly |e| for small e) is the
relative error bound after exponentiating.
Stirling's series isn't convergent for any particular n; i.e. for any n
there's a point at which adding more terms makes the error worse. On the
other hand, for any fixed number of terms, error decreases as *n*
increases. So the thing I'd try first is finding the n for which the
error of the non-polynomial part n * log(n) - n + 1/2 * log(2*pi*n) is
acceptable, and use its exponential
n^((2*n+1)/2) * exp(-n) * sqrt(2*pi)
for all greater n. The tricky part would be computing exp(-n) with
acceptable precision. (In fact, this is where I'd go looking for other
solutions.)
Neil ⊥
Thanks for your interesting replies. It was just my first thought.
Jos