On Sat, Jan 23, 2010 at 4:55 PM, kj <no.em...@please.post> wrote:

> Before I go off to re-invent a thoroughly invented wheel, I thought
> I'd ask around for some existing module for computing binomial
> coefficient, hypergeometric coefficients, and other factorial-based
> combinatorial indices.  I'm looking for something that can handle
> fairly large factorials (on the order of 10000!), using floating-point
> approximations as needed, and is smart about optimizations,
> memoizations, etc.
>

I don't have code for any of the other stuff, but I have some efficient code
(see below) for computing exact integer factorials.  It's a
divide-and-conquer algorithm.  I'm not sure of the exact time complexity,
but it appears to somewhere between n*log n and n**2.

python2.6 -m timeit -s 'from utils import factorial' 'x = factorial(10000)'
10 loops, best of 3: 27.7 msec per loop

If that's not fast enough, I suggest using the gamma function to compute
floating-point approximations, as gamma(n) == (n-1)!.  Gamma isn't yet
included in the Python standard library (http://bugs.python.org/issue3366),
but you can use the ctypes module to get it from the system C library on
many platforms.

For computing binomial coefficients, you can use the lgamma function (log of
gamma), also found in my system C libraries.  Since choose(n, k) =
exp(lgamma(n) - lgamma(k) - lgamma(n-k)).

If you go with floating point, watch out for floating point rounding error.
;-)

def factorial(n):
    "http://www.luschny.de/math/factorial/binarysplitfact.html";
    p = 1
    r = 1
    p, r = factorial_loop(n, p, r)
    return r << n_minus_num_of_bits(n)

def factorial_loop(n, p, r):
    if n <= 2: return p, r
    p, r = factorial_loop(n // 2, p, r)
    p *= part_product(n // 2 + 1 + ((n // 2) & 1),
                      n - 1 + (n & 1))
    r *= p
    return p, r

def part_product(n, m):
    if (m <= (n+1)): return n
    if (m == (n+2)): return n*m
    k = (n+m) // 2
    if ((k & 1) != 1):
        k -= 1
    return part_product(n, k) * part_product(k+2, m)

def n_minus_num_of_bits(v):
    w = v
    if w >= 2**32-1:
        raise OverflowError
    w -= (0xaaaaaaaa & w) >> 1
    w = (w & 0x33333333) + ((w >> 2) & 0x33333333)
    w = w + (w >> 4) & 0x0f0f0f0f
    w += w >> 8
    w += w >> 16
    return v - (w & 0xff)

--
Daniel Stutzbach, Ph.D.
President, Stutzbach Enterprises, LLC <http://stutzbachenterprises.com>
-- 
http://mail.python.org/mailman/listinfo/python-list

Reply via email to