# number generator

Duncan Booth duncan.booth at invalid.invalid
Mon Mar 12 12:14:13 CET 2007

```aleax at mac.com (Alex Martelli) wrote:

> Without any specification regarding the distributions required for the
> "5 random numbers" it's really impossible to say whether these are
> better or worse than other proposed solutions.

FWIW, I decided it would be fun to see what kind of implementation I
could come up test driven and avoiding reading the thread or background
references too much first. So starting from:

import unittest

def partition(total, n, min):
return [50]

class PartitionTests(unittest.TestCase):
def testsinglevalue(self):
self.assertEqual([50], partition(50, 1, 1))

if __name__=='__main__':
unittest.main()

I eventually worked my way through 15 revisions to the code below.

The tests were added in the order you see them below. The commented out
function is the one I had arrived at before I added the first
distribution test which triggered a major refactor (although the code
ended up remarkably similar): if you uncomment it all but the
distribution tests pass.

I don't really like the arbitrary limits for the distribution tests, but
I'm not sure how else to test that sort of thing. And as Alex said,
without knowing what distribution the OP wanted the definition I chose
to use is completely arbitrary.

----- partition.py -------
import unittest, collections
from random import randint, sample

def partition(total, n, min):
maxtotal = total - n*(min-1)
posts = sorted(sample(xrange(1, maxtotal), n-1))
return [ (b-a)+min-1 for (a,b) in zip([0]+posts, posts+[maxtotal]) ]

# def partition(total, n, min):
#     maxtotal = total - (n*min)
#     sums = sorted(randint(0, maxtotal) for i in range(n-1))
#     return [(b-a)+min for (a,b) in zip([0]+sums, sums+[maxtotal])]

class PartitionTests(unittest.TestCase):
def testsinglevalue(self):
self.assertEqual([50], partition(50, 1, 1))

def testnvalues(self):
self.assertEqual([1]*5, partition(5, 5, 1))

def testnminusone(self):
self.assertEqual([1]*4+[2], sorted(partition(6, 5, 1)))

def testnminusone2(self):
# Check we get all possible results eventually
expected = set([(1,1,2), (1,2,1), (2,1,1)])
for i in range(100):
got = tuple(partition(4,3,1))
if got in expected:
expected.remove(got)
if not len(expected):
break
self.assertEqual(len(expected), 0)

def testdistribution(self):
# Make sure we get each of 3 possible outcomes roughly
# equally often.
actual = collections.defaultdict(int)
for i in range(1000):
actual[tuple(partition(4,3,1))] += 1
counts = actual.values()
assert (min(counts) > 250)
assert (max(counts) < 400)

def testdistribution2(self):
# More arbitrary limits for the distribution. 10 possible
# outcomes this time.
actual = collections.defaultdict(int)
ntries = 10000
for i in range(ntries):
actual[tuple(partition(6,3,1))] += 1
counts = actual.values()
assert (min(counts) > 900)
assert (max(counts) < 1100)

def testmintwo(self):
self.assertEqual([2]*50, partition(100,50,2))

def testminzero(self):
self.assertEqual([0]*20, partition(0,20,0))

def testcantdoit(self):
self.assertRaises(ValueError, partition, 100, 51, 2)

if __name__=='__main__':
unittest.main()

--------------------------

```