How to create an array of specific length populated by random uniform values within a range

#1

Hello, beginner Julia user here.

This is a continuation of a previous post where I wanted to create a random uniform distribution between 0 (which is excluded) and 10 (included). I am making a second post but keeping the range general in the hope it will help someone one day.

So for example:
I would like an array of length 8
In this array I would like to randomly generate 8 values
These values fall uniformly within the ranges of 2 and 7.

I guess this could also be phrased as,
How to generate 8 random uniform values between 2 and 7, and place them within an array?

0 Likes

#2
5 .* rand(8) .+ 2

(broadcasting the multiplication as well makes it a tiny bit faster)

Here is the documentation on broadcasting btw.

0 Likes

#3

Thank you for your reply.

What does the 5 and +2 represent?

0 Likes

#4

Another way of thinking of uniformly distributed random values between 2 and 7 is uniformly distributed random values between 0 and 5 and then adding 2 to those. The rand function generates values in [0, 1], so if you multiply that by 5, you get values in [0, 5].

0 Likes

#5

You could also do [5rand()+2 for i=1:8] to eliminate the temporary rand(8) array.

Alternatively, using the Distributions.jl package, you can do

using Distributions
rand(Uniform(2,7), 8)

which is maybe a bit clearer, and generalizes better to other types of distributions if you need that flexibility.

6 Likes

#6

This is how you should think. (pardon me english)

  1. The range of value is from 2 to 7 therefore the span is 7 - 2 = 5.

  2. The offset is 2 because the smallest number is 2.

  3. Create a random number generator with span 5 and offset 2

julia> function myrng()
       return 2.0 + 5.0 * rand()
       end
myrng (generic function with 1 method)
  1. finally create your array
julia> a = [ myrng() for n in 1:8 ]
8-element Array{Float64,1}:
 4.636049027494998
 5.061366873363691
 6.420185007452647
 6.955145662491416
 2.708159366136055
 6.223899415297151
 6.682556947797874
 4.927220645178398

or you can use

julia> 5.0 .* rand(8) .+ 2.0
8-element Array{Float64,1}:
 2.2677134636706024
 6.9686960119949095
 5.713372377688906 
 4.253700066196062 
 3.483397341546835 
 4.291678326242685 
 3.094738077434428 
 6.920131322028918

Any in case you are curious, this could come out with the exact number 2.0 but will NEVER come out with the exact number 7.0

If you want to include both 2.0 and 7.0 then replace rand() with rand_ZeroAndOneIncluded()

julia> function rand_ZeroAndOneIncluded()
       return rand()/prevfloat(1.0)
       end
rand_ZeroAndOneIncluded (generic function with 1 method)
1 Like

#7

If this is a common problem, then perhaps the best way to help others is to make a small PR to Distributions with a variant of Uniform that does this. Or even generalizing the idea to other (half)bounded distributions.

0 Likes

#8

I recall from your previous question that there was a big discussion on your requirement that you want a random number in the range (F1,F2], in other words that F1 is excluded and F2 included. The “problem” is that rand() defaults to producing numbers in the range [0,1), i.e., lower bound included and upper bound excluded. In that thread, someone suggested that 1-rand() produces numbers in the range (0,1]. Thus, I think you can do:

function my_rand(F1,F2,dims...)
    return F1 .+ (F2-F1)*(1 .- rand(dims...))
end

If you want a function that returns a scalar when dims is skipped, you can add the method:

function my_rand(F1,F2)
    return F1 + (F2-F1)*(1-rand())
end

A couple of examples (assuming the functions have been defined):

julia> my_rand(2,7,8)
8-element Array{Float64,1}:
 6.826654827094337
 6.4343778115292025
 2.078598954398351
 5.963089683352704
 6.729404550779482
 6.2861095743137465
 5.70630650452843
 3.127727841177605

julia> my_rand(2,7,2,3)
2×3 Array{Float64,2}:
 4.41794  5.86907  2.04626
 6.27201  3.7892   2.90218

julia> my_rand(2,7)
5.92523692894661

If you want efficient code, some of the other suggestions may be better.

0 Likes

#9

Actually, as the last bit of a value produced by rand is 0, you need to call rand() / prevfloat(1.0, 2) in order to have 1.0 included in the possible outcomes.

0 Likes

#10

Stepping back a bit from the immediate problem, the best approach may not be designing a function that can ensure random draws \in (a, b], but an algorithm that can deal with =a robustly.

It is common to have modeling problems where eg some x \in D holds for an open subset of \mathbb{R}^n. Then for continuous functions f_i, it of course follows that f_1(x) \in f_1(D), f_2(f_1(x)) \in f_2(f_1(D)), etc.

But for floating point, numerical error will inevitably creep in, especially for complex transformations. At that point the algorithm should be able to cope with x \in D but f_n(\dots) being “invalid”, eg

  1. by returning a -Inf log likelihood/posterior for likelihood-based inference,
  2. by not recording a simulated sample and trying again for MC simulation,
  3. “regularizing” the value so that it is valid (eg for x < a, replace with a+\epsilon, this is sometimes not innocuous though).
0 Likes