combinatorial python math scipy combinations combinatorics

python - combinatorial - ¿Es `scipy.misc.comb` más rápido que un cálculo binomial ad-hoc?



combinatorial in python (1)

¿Es concluyente que ahora el scipy.misc.comb es de hecho más rápido que la implementación ad-hoc?

Según una respuesta anterior, Estadísticas: combinaciones en Python , esta función homebrew es más rápida que scipy.misc.comb cuando se calculan las combinaciones nCr :

def choose(n, k): """ A fast way to calculate binomial coefficients by Andrew Dalke (contrib). """ if 0 <= k <= n: ntok = 1 ktok = 1 for t in xrange(1, min(k, n - k) + 1): ntok *= n ktok *= t n -= 1 return ntok // ktok else: return 0

Pero después de ejecutar algunas pruebas en mi propia máquina, este no parece ser el caso, usando esta secuencia de comandos:

from scipy.misc import comb import random, time def choose(n, k): """ A fast way to calculate binomial coefficients by Andrew Dalke (contrib). """ if 0 <= k <= n: ntok = 1 ktok = 1 for t in xrange(1, min(k, n - k) + 1): ntok *= n ktok *= t n -= 1 return ntok // ktok else: return 0 def timing(f): def wrap(*args): time1 = time.time() ret = f(*args) time2 = time.time() print ''%s function took %0.3f ms'' % (f.__name__, (time2-time1)*1000.0) return ret return wrap @timing def test_func(combination_func, nk): for n,k in nk: combination_func(n, k) nk = [] for _ in range(1000): n = int(random.random() * 10000) k = random.randint(0,n) nk.append((n,k)) test_func(comb, nk) test_func(choose, nk)

Obtengo el siguiente resultado:

$ python test.py /usr/lib/python2.7/dist-packages/scipy/misc/common.py:295: RuntimeWarning: overflow encountered in exp vals = exp(lgam(N+1) - lgam(N-k+1) - lgam(k+1)) 999 test_func function took 32.869 ms 999 test_func function took 1859.125 ms $ python test.py /usr/lib/python2.7/dist-packages/scipy/misc/common.py:295: RuntimeWarning: overflow encountered in exp vals = exp(lgam(N+1) - lgam(N-k+1) - lgam(k+1)) 999 test_func function took 32.265 ms 999 test_func function took 1878.550 ms

¿La prueba de perfil de tiempo mostró que el nuevo scipy.misc.comb es más rápido que la función ad-hoc choose() ? ¿Hay algún error en mi script de prueba que hace que el tiempo sea inexacto?

¿Por qué es que scipy.misc.comb es más rápido ahora? ¿Es por algunos trucos de envoltura cython / c ?

EDITADO

Después del comentario de @WarrenWeckesser:

Al usar la aproximación de coma flotante predeterminada al usar scipy.misc.comb() , el cómputo se rompe debido al desbordamiento de coma flotante.

(Consulte http://docs.scipy.org/doc/scipy-0.16.0/reference/generated/scipy.misc.comb.html para obtener documentación)

Cuando se prueba con exact=True que computa con enteros largos en lugar de coma flotante usando la función siguiente, es mucho más lento cuando se calculan 1000 combinaciones:

@timing def test_func(combination_func, nk): for i, (n,k) in enumerate(nk): combination_func(n, k, exact=True)

[fuera]:

$ python test.py test_func function took 3312.211 ms test_func function took 1764.523 ms $ python test.py test_func function took 3320.198 ms test_func function took 1782.280 ms


En referencia al código fuente de scipy.misc.comb , la rutina de actualización del resultado es:

val = 1 for j in xrange(min(k, N-k)): val = (val*(N-j))//(j+1) return val

mientras que la rutina de actualización que sugirió es:

ntok = 1 ktok = 1 for t in xrange(1, min(k, n - k) + 1): ntok *= n ktok *= t n -= 1 return ntok // ktok

Supongo que la razón por la que la implementación de SciPy es más lenta se debe al hecho de que la subrutina implica una división entera en cada iteración, mientras que la suya solo llama a la división una vez en la declaración de devolución.