6

If I need to divide for example 7 into random number of elements of random size, how would I do this?

So that sometimes I would get [3,4], sometimes [2,3,1] and sometimes [2,2,1,1,0,1]?

I guess it's quite simple, but I can't seem to get the results. Here what I am trying to do code-wise (does not work):

def split_big_num(num):
    partition = randint(1,int(4))
    piece = randint(1,int(num))
    result = []
    for i in range(partition):
        element = num-piece
        result.append(element)
        piece = randint(0,element)
#What's next?
        if num - piece == 0:
            return result
    return result

EDIT: Each of the resulting numbers should be less than initial number and the number of zeroes should be no less than number of partitions.

Stpn
  • 6,202
  • 7
  • 47
  • 94
  • Specify what you mean by random number of elements. Do you mean every length of subset has the same probability of being picked? Or do you mean that every subset has the same probability of being picked? Those mean very different things. – David Robinson Apr 24 '12 at 20:14
  • 2
    Should it ever return [7]? What about [0,0,0,0,0,7]? Are they possible? – DanRedux Apr 24 '12 at 20:15
  • Sorry, I had to clarify that, no, no 7s.. – Stpn Apr 24 '12 at 20:15
  • 2
    Are there any constraints on how many zeros you can have? – Akavall Apr 24 '12 at 20:17
  • wow, great question! Yes, no more than the number of partitions.. – Stpn Apr 24 '12 at 20:18
  • try the concepts of permutation and combinations , to get all the possible combinations. – Ashwini Chaudhary Apr 24 '12 at 20:19
  • A problem that people too often have is to state they want a random number of (something), but then they fail to say what is the distribution. Without knowledge of the distribution, randomness is an unanswerable goal. –  Apr 24 '12 at 22:48

4 Answers4

13

I'd go for the next:

>>> def decomposition(i):
        while i > 0:
            n = random.randint(1, i)
            yield n
            i -= n

>>> list(decomposition(7))
[2, 4, 1]
>>> list(decomposition(7))
[2, 1, 3, 1]
>>> list(decomposition(7))
[3, 1, 3]
>>> list(decomposition(7))
[6, 1]
>>> list(decomposition(7))
[5, 1, 1]

However, I am not sure if this random distribution is perfectly uniform.

Roman Bodnarchuk
  • 29,461
  • 12
  • 59
  • 75
4

You have to define what you mean by "random". If you want an arbitrary integer partition, you can generate all integer partitions, and use random.choice. See python: Generating integer partitions This would give no results with 0. If you allow 0, you will have to allow results with a potentially infinite number of 0s.

Alternatively if you just want to take random chunks off, do this:

def arbitraryPartitionLessThan(n):
    """Returns an arbitrary non-random partition where no number is >=n"""
    while n>0:
        x = random.randrange(1,n) if n!=1 else 1
        yield x
        n -= x

It is slightly awkward due to the problem constraints that each number should be less than the original number; it would be more elegant if you allowed the original number. You can do randrange(n) if you want 0s but it wouldn't make sense unless there is a hidden reason you are not sharing.

edit in response to question edit: Since you desire the "the number of zeroes should be no less than number of partitions" you can arbitrarily add 0s to the end:

def potentiallyInfiniteCopies(x):
    while random.random()<0.5:
        yield x

x = list(arbitraryPartitionLessThan(n))
x += [0]*len(x) + list(potentiallyInfiniteCopies(0))

The question is quite arbitrary, and I highly recommend that you choose this instead as your answer:

def arbitraryPartition(n):
    """Returns an arbitrary non-random partition"""
    while n>0:
        x = random.randrange(1,n+1)
        yield x
        n -= x
Community
  • 1
  • 1
ninjagecko
  • 88,546
  • 24
  • 137
  • 145
  • The number of compositions grows exponentially quickly. Thus even a modest value of n can use up all of your memory. – btilly Apr 24 '12 at 21:51
1

Recursion to the rescue:

import random

def splitnum(num, lst=[]):
    if num == 0:
        return lst
    n = random.randint(0, num)
    return splitnum(num - n, lst + [n])

for i in range(10):
    print splitnum(7)

Result:

[1, 6]
[6, 0, 0, 1]
[5, 1, 1]
[6, 0, 1]
[2, 0, 3, 1, 1]
[7]
[2, 1, 0, 4]
[7]
[3, 4]
[2, 0, 4, 1]
Israel Unterman
  • 13,158
  • 4
  • 28
  • 35
  • @AshwiniChaudhary: calculate the odds of that sequence being returned, and there's your answer. – Wooble Apr 24 '12 at 22:33
0

This solution does not insert 0s (I do not understand what your description of your zeros rule is supposed to be), and is equally likely to generate every possible combination other than the original number by itself.

def split (n):
    answer = [1]
    for i in range(n - 1):
        if random.random() < 0.5:
            answer[-1] += 1
        else:
            answer.append(1)

    if answer == [n]:
        return split(n)
    else:
        return answer
btilly
  • 43,296
  • 3
  • 59
  • 88