Skip to content Skip to sidebar Skip to footer

How To Create L Lists Of N Non-zero Random Decimals Where Each List Sums To 1.0?

Looking for a quick way to create L amount of lists of n amount of decimals whose sum is 1. Each number should be >= 0.01 Desired Output: where L = 200, n = 6 [0.20, 0.22, 0.10,

Solution 1:

This should be very fast, as it uses numpy.

It will automatically repeat the randomization if it gets any 0.0's, but that is unlikely. The while loop was written before the OP adjusted the non-zero requirement to be above 0.01. To fix this you can modify the while block to include the entire subsequent code, and calculate the number of violations of any desired constraint at the end in a manner similar to what is shown for detecting zeros. But that may get slow when L is large compared to the probability of violating the constraint. In some sense it is easiest to comply with the original requirement of >0.0.

After the while loop, each element of an L x n matrix is uniformly distributed on (0.0,1.0) without any 0s or 1s. Each row is summed and used to form a scale matrix, that is then matrix multiplied by the random matrix to obtain rows that automatically sum to 1.0

 import numpy as np
 def random_proportions(L,n):
      zeros = 1
      while zeros>0:
          x = np.random.random(size=(L,n))
          zeros = np.sum(x==0.0)
      sums = x.sum(axis=1)
      scale = np.diag(1.0/sums)
      return np.dot(scale, x)

EDIT: The above produces an LxL matrix for scale, which is memory-inefficient. It will OOM before L=10**6. We can fix that by using the broadcasting normalization procedure suggested by this answer

import numpy as np
def random_proportions(L,n):
      zeros = 1
      while zeros>0:
          x = np.random.random(size=(L,n))
          zeros = np.sum(x==0.0)
      sums = x.sum(axis=1).reshape(L,1) # reshape for "broadcasting" effect
      return x/sums

This 2nd version will calculate 1 millions lists of size 10 in about 1/3 of a second on an AMD FX-8150 with 16GB ram:

%timeit l = random_proportions(1000000,10)
1 loops, best of 3: 347 ms per loop

Solution 2:

Here's how you get n numbers that add up to one: Select n random numbers in an arbitrary range of your choice (for example, from 1 to 10), then divide them all by their sum.


Solution 3:

This should do the trick:

import random


def floatPartition(n, total):
    answer = []
    for _ in range(n-1):
        num = random.uniform(0, total)
        answer.append(num)
        total -= num
    answer.append(total)
    return answer


def paritions(n,L):
    return [floatPartition(n, 1) for _ in range(L)]


if __name__ == "__main__":
    answer = paritions(6,200)

Solution 4:

I didn't check against the others for speed, but this algorthim produces 1,000,000 lists of length 10 with elements 0.01 - 0.99 in increments of 0.01 in 20 seconds:

import random
def rand_list(n):

    sub_list = []
    max_val  = 100 - n + 1  # max needs to be n-1 less than 100

    for repetition in xrange(n-1):

        sub_list += [random.randrange(1, max_val)]
        max_val  -= (sub_list[-1] - 1)  # decrease the range by the latest element added - 1

    sub_list += [max_val]  # use the remainder for the last value, this way it adds to 100

    return [round(x/100.0, 2) for x in sub_list]  # convert to 0.01 - 0.99 with list comprehension

Post a Comment for "How To Create L Lists Of N Non-zero Random Decimals Where Each List Sums To 1.0?"