Skip to content Skip to sidebar Skip to footer

Devide Int Into Lower Whole Ints

I have a random int in the range of 30-60 which I get using randint(30,60). Let's say it's 40. I want to split this number in exactly 7 random whole ints. So for instance [5,5,5,5,

Solution 1:

A set of whole numbers that sum to a number n is called a partition of n; if order matters then it's called a composition.

Here's a reasonably fast way to produce random compositions.

import random

def random_partition(n, size):
    seq = []
    while size > 1:
        x = random.randint(1, 1 + n - size)
        seq.append(x)
        n -= x
        size -= 1
    seq.append(n)
    return seq

n = 40 
for _ in range(20):
    print(random_partition(n, 7))

typical output

[26, 2, 8, 1, 1, 1, 1]
[30, 2, 1, 3, 1, 1, 2]
[26, 5, 3, 1, 2, 2, 1]
[2, 25, 9, 1, 1, 1, 1]
[28, 2, 2, 2, 1, 2, 3]
[23, 1, 9, 3, 2, 1, 1]
[3, 26, 1, 7, 1, 1, 1]
[25, 1, 7, 1, 2, 1, 3]
[10, 8, 11, 5, 3, 1, 2]
[19, 16, 1, 1, 1, 1, 1]
[12, 23, 1, 1, 1, 1, 1]
[1, 14, 15, 7, 1, 1, 1]
[29, 5, 1, 1, 2, 1, 1]
[25, 1, 3, 3, 1, 2, 5]
[10, 12, 10, 4, 1, 2, 1]
[13, 4, 6, 14, 1, 1, 1]
[31, 3, 1, 1, 1, 1, 2]
[16, 11, 9, 1, 1, 1, 1]
[3, 26, 5, 3, 1, 1, 1]
[31, 2, 1, 2, 2, 1, 1]

We use 1 + n - size as the upper limit because the other size - 1 numbers are at least 1.

Here's a fairly efficient way to generate all partitions of a given integer. Note that these are ordered; you could use random.shuffle if you want to produce random compositions from these partitions.

We first print all partitions of 16 of size 5, and then we count the number of partitions of 40 of size 7 (= 2738).

This code was derived from an algorithm by Jerome Kelleher.

def partitionR(num, size):
    a = [0, num] + [0] * (num - 1)
    size -= 1
    k = 1
    while k > 0:
        x = a[k - 1] + 1
        y = a[k] - 1
        k -= 1
        while x <= y and k < size:
            a[k] = x
            y -= x
            k += 1
        a[k] = x + y
        if k == size:
            yield a[:k + 1]

for u in partitionR(16, 5):
    print(u)

print('- ' * 32)
print(sum(1 for _ in partitionR(40, 7)))

output

[1, 1, 1, 1, 12]
[1, 1, 1, 2, 11]
[1, 1, 1, 3, 10]
[1, 1, 1, 4, 9]
[1, 1, 1, 5, 8]
[1, 1, 1, 6, 7]
[1, 1, 2, 2, 10]
[1, 1, 2, 3, 9]
[1, 1, 2, 4, 8]
[1, 1, 2, 5, 7]
[1, 1, 2, 6, 6]
[1, 1, 3, 3, 8]
[1, 1, 3, 4, 7]
[1, 1, 3, 5, 6]
[1, 1, 4, 4, 6]
[1, 1, 4, 5, 5]
[1, 2, 2, 2, 9]
[1, 2, 2, 3, 8]
[1, 2, 2, 4, 7]
[1, 2, 2, 5, 6]
[1, 2, 3, 3, 7]
[1, 2, 3, 4, 6]
[1, 2, 3, 5, 5]
[1, 2, 4, 4, 5]
[1, 3, 3, 3, 6]
[1, 3, 3, 4, 5]
[1, 3, 4, 4, 4]
[2, 2, 2, 2, 8]
[2, 2, 2, 3, 7]
[2, 2, 2, 4, 6]
[2, 2, 2, 5, 5]
[2, 2, 3, 3, 6]
[2, 2, 3, 4, 5]
[2, 2, 4, 4, 4]
[2, 3, 3, 3, 5]
[2, 3, 3, 4, 4]
[3, 3, 3, 3, 4]
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
2738

Solution 2:

If you only care about getting an arbitrary set of numbers that add up to your total rather than an exhaustive iteration over all combinations, the following should get you what you need.

def get_parts(total, num_parts=7, max_part=15):
    running_total = 0
    for i in range(num_parts - 1):
        remaining_total = total - running_total
        upper_limit = min(max_part, remaining_total - num_parts + 1 + i)
        # need to make sure there will be enough left
        lower_limit = max(1, remaining_total - max_part*(num_parts - i - 1))
        part = randint(lower_limit, upper_limit)
        running_total += part
        yield part
    yield total - running_total

>>> list(get_parts(40))
[2, 7, 10, 11, 1, 4, 5]

>>> list(get_parts(40))
[7, 13, 11, 6, 1, 1, 1]

>>> list(get_parts(50, 4))
[6, 14, 15, 15]

Of course, the items in each list above is not truly random and will favor larger numbers earlier in the list and smaller numbers later. You can feed these lists through random.shuffle() if you want more of an element of pseudorandomness.


Solution 3:

From Python Integer Partitioning with given k partitions

def partitionfunc(n,k,l=1):
    '''n is the integer to partition, k is the length of partitions, l is the min partition element size'''
    if k < 1:
        raise StopIteration
    if k == 1:
        if n >= l:
            yield (n,)
        raise StopIteration
    for i in range(l,n//k+1):
        for result in partitionfunc(n-i,k-1,i):
            yield (i,)+result
list(partitionfunc(40,7))

Solution 4:

You can do a simple iteration over all possible combinations of the first 6 values (where the sum does not exceed 40), and calculate the 7th value.

for a in range(41):
    for b in range(41-a):
        for c in range(41-(a+b)):
            for d in range(41-(a+b+c)):
                for e in range(41-(a+b+c+d)):
                    for f in range(41-(a+b+c+d+e)):
                        g = 40 - (a+b+c+d+e+f)
                        # Do what you need to do here

You can cut the amount of time required by the loop almost in half (according to tests using timeit) by precomputing the sums:

for a in range(41):
    for b in range(41-a):
        ab = a + b
        for c in range(41-ab):
            abc = ab + c
            for d in range(41-abc):
                abcd = abc + d
                for e in range(41-abcd):
                    abcde = abcd + e
                    for f in range(41-abcde):
                        g = 40 - (abcde + f)
                        # Do what you need to do here

Post a Comment for "Devide Int Into Lower Whole Ints"