Proving optimized function is still correct
Terry Reedy
tjreedy at home.com
Tue Nov 13 11:55:48 EST 2001
"Terry Reedy" <tjreedy at home.com> wrote in message
news:ct2I7.212078$5A3.78366686 at news1.rdc2.pa.home.com...
>
> <salvasan at yahoo.com> wrote in message
> news:9sq46c$o93$1 at news.carib-link.net...
> > I gave a colleague of mine a Python function to optimise:
...
> description of what your function computes: the number of ways of
> distributing n indistinguishable items among m labelled bins, with
> bins possibly left empty, and where 'labelled' means that each
> permutation among bins is counted separately.
...
> The following is a more direct recursion:
...
> def d(m,n):
> if m == 1 or n == 0: return 1
> cnt = 0
> for i in range(n+1): cnt = cnt + d(m-1,n-i) # put i items in 1 bin
and distribute rest
> return cnt
After posting above, I realized that
d(m,n) == sum(i=0,n; d(m-1, n-i)) == sum(i=n,0; d(m-1,n-i)) ==
sum(i=0,n; d(m-1,i)) and
d(m,n-1) = = sum(i=0,n-1; d(m-1,i)) imply the simpler recursion
d(m,n) = d(m,n-1) + d(m-1,n), with easily proven solution
(n+m-1)!/n!(m-1)!.
A recursive implementation like
def d(m,n): return (m == 1 or n == 0) and 1 or d(m,n-1) + d(m-1,n)
is inefficient in the same way as recursive fibonacci
def fib(n): return n <= 1 and 1 or fib(n-2) + fib(n-1)
in that both needlessly recompute the function for 'lower' values of
the parameter(s). The following iterative solution computes each
function value only once.
def d2(m,n):
if m == 1 or n == 0: return 1
temp = (n+1) * [1] # change 1 to 1L to force long computation to
avoid overflow
for i in range(2,m+1):
for ni in range(1,n+1):
temp[ni] = temp[ni-1] + temp[ni]
return temp[n]
>>> d2(6,4)
126
Unlike the factorial formula, this does not overflow until the answer
does. If one needed to call this function many times for small enough
values of m and n, one could modify d2 to save all computed values in
a table for later lookup.
Terry J. Reedy
More information about the Python-list
mailing list