willpower2727
willpower2727

Reputation: 789

Generate random floats in a set of discontinuous ranges

I am looking to generate random floats to populate a virtual reality world with objects, using the floats to define the locations of the objects. Right now it is easy to uniformly fill a "box" with objects using random.uniform call.

import random

#generate random floating coordinates
for z in range(1000):
    x = random.uniform(-1,1)
    y = random.uniform(-1,1)
    z = random.uniform(-1,1)
    #shape = vizshape.addSphere() commented out because this is an uncommon module for VR but I wanted to show how floats are being used
    #shape.setPosition([x,y,z])

What I would like to do is pass in arguments to random.uniform() that specify more than 1 range to generate floats in, something like:

x = random.uniform([-1,1],[2,3])

Of course this will cause errors but I am looking for a way to get floats in multiple, size-varying, discontinuous ranges. I'd like to do it without writing another check to see if the generated floats are within my desired ranges and then deciding to keep or throw away. Any ideas?

Upvotes: 1

Views: 598

Answers (2)

Leandro Caniglia
Leandro Caniglia

Reputation: 14858

The idea would be to first peek a range at random and then a float inside the selected range. The problem, however, is that for this to be uniform across all ranges, their length should be taken into account. Otherwise, shorter ranges would get, in the long run, the same number of samples than longer ones, which is not a uniform behavior.

In order to address this problem we can map all ranges to, say, the interval [0,1] in such a way that relative lengths are preserved. Here is how:

  1. Sort the ranges in ascending order and get [a1, b1], ..., [an, bn]
  2. Let di = bi - ai be the length of the i-th range
  3. Let L be the sum of all di
  4. Map a1 to 0 and b1 to d1/L.
  5. For all i >= 2 map ai to the mapping of b_{i-1} and bi to that value plus di/L.
  6. For all i >= 1 let xi be the mapping of ai.

Now take a random number s uniformly sampled in [0,1] and proceed as follows

  1. Let i0 be the last index i such that xi <= s.
  2. Use the inverse of the i0-th mapping to get the answer. In other words, answer with

    f = a_{i0} + (s-xi) * L / di

    This number f is in the i0-th interval and has been chosen uniformly at random.

enter image description here

Upvotes: 2

inspectorG4dget
inspectorG4dget

Reputation: 113965

This is a bit of a hack, but it can be easily generalized to multiple discontinuous ranges:

import random

def getRandoms(n, ranges):
    answer = []
    for _ in xrange(n):
        low,high = random.choice(ranges)
        answer.append(random.uniform(low, high))

    return answer

Upvotes: 1

Related Questions