number generator
Anton Vredegoor
anton.vredegoor at gmail.com
Tue Mar 13 09:59:23 EDT 2007
Dick Moores wrote:
> If the added constraint is instead that the probability of generating
> a given list of length N be the same as that of generating any other
> list of length N, then I believe my function does the job. Of course,
> [1,46,1,1,1] and [1,1,46,1,1], as Python lists, are distinct. I ran
> this test for M == 8 and N == 4:
Yes, I believe your function is OK. But the 'fencepost' method posted
earlier in this thread also does this and it's faster AND it's only two
line of code ...
> A = []
> B = []
> for x in range(100000):
> lst = sumRndInt(8,4)
> if lst not in A:
> A.append(lst)
> B.append(1)
> else:
> i = A.index(lst)
> B[i] += 1
>
> A.sort()
Doesn't sorting break the correspondence between A and B? Also, a more
pythonic way to count would be to convert the lst into a tuple and then
do something with a dictionary. Dictionaries have faster lookup. For
example:
T = tuple(lst)
D[T] = D.get(T,0) + 1
in the loop in order to count the occurrences.
I'm totally fascinated with this stuff myself so it's a bit hard to
guess whether someone still is interested, but anyway, here are some
further explorations involving partitions. The code prints the number of
distinct permutations for each partition.
from operator import mul
def part(m,n,r):
L = [0]*n
while m:
x = pn(m-1,n-1)
if r < x:
L[n-1] += 1
m -= 1
n -= 1
else:
for i in range(n):
L[i] += 1
r -= x
m -= n
return L
def memoize(fn):
cache = {}
def proxy(*args):
try: return cache[args]
except KeyError: return cache.setdefault(args, fn(*args))
return proxy
@memoize
def pn(m,n):
if m<n or n==0:
return 0
if m==n or n==1:
return 1
return pn(m-1,n-1)+pn(m-n,n)
@memoize
def fac(n):
if n == 1:
return 1
return n*fac(n-1)
@memoize
def ncomb(n,k):
return reduce(lambda a,b: a*(n-b)/(b+1),range(k),1)
@memoize
def _np(T):
if T:
up = fac(sum(T))
down = reduce(mul,map(fac,T))
return up/down
else:
return 1
def nperm(L):
f = dict((x,L.count(x)) for x in set(L))
return _np(tuple(f.values()))
def test():
m = 50
n = 5
np=pn(m,n)
total = 0
for r in xrange(np):
x = part(m,n,r)
np = nperm(x)
total += np
print np,x
assert total == ncomb(m-1,n-1)
if __name__=='__main__':
test()
I posted some easy way of generating all outcomes by index -using a
combinations function- before in this thread. Using just a single
randint call one could output a random list of numbers adding up to
something.
But I think it would be possible to do it with distinct permutations of
these partitions too. I think I did that in old thread. The only problem
is that in order to look up which distinct permutation of which
partition is indexed there should be a way to compute a table containing
info about how many permutations a certain partition contributes to the
total. But the code would then also need a distinct permutation by index
function (I've got one but it's not exactly beautiful) while the code is
becoming very complex even now without the extra lookup table, compared
to the simple combinations code.
And compared to the two line fencepost method it's just crazy :-)
But I have a hunch there must be a way to generate such a table without
actually computing any partitions at all and then one could do some kind
of 3d bisect to look things up. Maybe in a few years, or sooner if
someone posts an idea that simplifies things.
A.
More information about the Python-list
mailing list