Generating random numbers to obtain a fixed sum(python)

12,382

Solution 1

This might not be the most efficient way but it will work

totals = [54, 1536, 36, 14]

nums = []
x = np.random.randint(0, i, size=(6,))
for i in totals:
    while sum(x) != i: x = np.random.randint(0, i, size=(6,))
    nums.append(x)
print(nums)

[array([ 3, 19, 21, 11, 0, 0]), array([111, 155, 224, 511, 457, 78]), array([ 8, 5, 4, 12, 2, 5]), array([3, 1, 3, 2, 1, 4])]


This is a way more efficient way to do this

totals = [54,1536,36,14,9,360, 0]

nums = []
for i in totals:
    if i == 0: 
        nums.append([0 for i in range(6)])
        continue
    total = i
    temp = []
    for i in range(5):
        val = np.random.randint(0, total)
        temp.append(val)
        total -= val
    temp.append(total)
    nums.append(temp)

print(nums)

[[22, 4, 16, 0, 2, 10], [775, 49, 255, 112, 185, 160], [2, 10, 18, 2, 0, 4], [10, 2, 1, 0, 0, 1], [8, 0, 0, 0, 0, 1], [330, 26, 1, 0, 2, 1], [0, 0, 0, 0, 0, 0]]

Solution 2

Generating a list of random numbers that sum to a certain integer is a very difficult task. Keeping track of the remaining quantity and generating items sequentially with the remaining available quantity results in a non-uniform distribution, where the first numbers in the series are generally much larger than the others. On top of that, the last one will always be different from zero because the previous items in the list will never sum up to the desired total (random generators usually use open intervals in the maximum). Shuffling the list after generation might help a bit but won't generally give good results either.

A solution could be to generate random numbers and then normalize the result, eventually rounding it if you need them to be integers.

import numpy as np
totals = np.array([54,1536,36,14])  # don't use Sum because sum is a reserved keyword and it's confusing

a = np.random.random((6, 4))  # create random numbers
a = a/np.sum(a, axis=0) * totals  # force them to sum to totals

# Ignore the following if you don't need integers
a = np.round(a)  # transform them into integers
remainings = totals - np.sum(a, axis=0)  # check if there are corrections to be done
for j, r in enumerate(remainings):  # implement the correction
    step = 1 if r > 0 else -1
    while r != 0:
        i = np.random.randint(6)
        if a[i,j] + step >= 0:
            a[i, j] += step
            r -= step

Each column of a represents one of the lists you want. Hope this helps.

Share:
12,382

Related videos on Youtube

IndigoChild
Author by

IndigoChild

Updated on June 04, 2022

Comments

  • IndigoChild
    IndigoChild almost 2 years

    I have the following list:

    Sum=[54,1536,36,14,9,360]
    

    I need to generate 4 other lists, where each list will consist of 6 random numbers starting from 0, and the numbers will add upto the values in sum. For eg;

    l1=[a,b,c,d,e,f] where a+b+c+d+e+f=54
    l2=[g,h,i,j,k,l] where g+h+i+j+k+l=1536
    

    and so on upto l6. And I need to do this in python. Can it be done?

    • abarnert
      abarnert about 6 years
      Of course it can be done, but first you have to decide how you plan to choose the random numbers. Can I just do [random.randrange(2) for _ in range(5)] and then add 1536 - sum(those five numbers) for my last random number? If not, why not? What's the rule here?
    • KuboAndTwoStrings
      KuboAndTwoStrings about 6 years
      Is this a homework question? Please show us what you have tried so far in terms of code.
    • IndigoChild
      IndigoChild about 6 years
      @abertnet, no such rule, just that the numbers have to be random
    • Rao Sahab
      Rao Sahab about 6 years
      Is there any constraint like number should not be repeated, or something like that?
  • IndigoChild
    IndigoChild about 6 years
    hi, for a list of len 1000, I get this error: ValueError: low >= high
  • JahKnows
    JahKnows about 6 years
    Can you post your list so that I can test that please?
  • JahKnows
    JahKnows about 6 years
    The problem is the negative values in your list actually.
  • IndigoChild
    IndigoChild about 6 years
    Hi, I just checked, there are no negative values..
  • JahKnows
    JahKnows about 6 years
    Can you add print(i) right in the for loop to find out what value is causing the problem please.
  • IndigoChild
    IndigoChild about 6 years
    Hi ,print(i) returns 4
  • IndigoChild
    IndigoChild about 6 years
    I've edited the questionn to include the 5th ad 6th value
  • JahKnows
    JahKnows about 6 years
    Works for me with all these values. Please append the problematic values.
  • IndigoChild
    IndigoChild about 6 years
    I just realized, there are a few 1s and 2s here..could that be the problem?
  • JahKnows
    JahKnows about 6 years
    1s should be fine, however 0s will cause that error too. Are their 0s?
  • JahKnows
    JahKnows about 6 years
    I made an edit to account for 0s to remove that error.
  • IndigoChild
    IndigoChild about 6 years
    worked perfectly, thanks a lot buddy for the ffort :)
  • Richard
    Richard over 3 years
    This does not generate a uniform distribution
  • Richard
    Richard almost 3 years
    A multinomial distribution is a superior way to do this.