Proving optimized function is still correct
Terry Reedy
tjreedy at home.com
Tue Nov 13 00:46:48 EST 2001
<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:
Responding would have been easier if you had given a succinct
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.
> def count_combos( m, n, trial=[] ):
> #is trial complete?
> if len(trial) == m:
> #check if trial is valid
> total = 0
> for x in trial:
> total = total + x
> if total == n:
> #count this valid trial
> return 1
> #otherwise do not count it
> return 0
> else:
> #RECURSIVE CALL
> count = 0
> for i in range( n+1 ):
> #extend trial by another test element
> trial.append(i)
> #total up valid trials found
> count = count + count_combos( m, n, trial )
> #remove last appended element
> del( trial[-1] )
> return count
The above generates the (n+1)**m ways of putting 0..n items in each
bin independently and then counts those for which the separate numbers
add up to n. This brute-force method is about maximally inefficient.
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) # ie, put i items in bin
m and distribute rest
return cnt
>>> d(6,4)
126
> The preconditions are that arguments m and n are always positive
integers.
No, n may be 0, as shown by inspection of your code (and consideration
of the problem).
> She amazingly reduced the above code to the following:
No, she tossed it out and instead calculates the well-known formula
that solves this standard combinatorial problem.
> def count_combos_optimised( m, n ):
> numer = 1
> denom = 1
> j = m+n-1
> i = 1
> while i<m:
> numer = numer * j
> denom = denom * i
> j = j - 1
> i = i + 1
> return numer/denom
This calculates (n+m-1)!/n!(m-1)!. Example: 9*8*7*6*5 / 5*4*3*2*1 =
126.
> The optimised version operates a lot faster, but how can I be sure
> that count_combos_optimised( m, n ) and count_combos( m, n ) will
> return the same values for every possible pairing of positive
integers
> (m,n) ?
The standard method would be mathematical induction: substitute the
factorial formula into the recursive formula embodied in d() above and
show equalilty (ugh). Fortunately, in this case, there is a direct
argument. Line up n+m-1 items, bounded on each end by a boundary
marker. Select m-1 and turn them into boundary markers. This leave n
items distributed among k distinguishable (by order) bins. There are
(n+m-1)!/n!(m-1)! ways of doing this.
Terry J. Reedy
More information about the Python-list
mailing list