Random numbers, why is it necessary to multiply by rand()

Hi I am working on the pi example in JuliaBox. When I am generating random numbers the only way I do not get 0 as the value of pi is if I multiply by rand() (as it did in the solution to the exercise). It looks like multiplying rand() makes x and y decimals. I was wondering how multiplying rand() was used or what it specifically does?

function calculate_pi(N,r)
Acircle = 0

for i in 1:N    
    x, y = rand([-1, 1])*rand(), rand([-1, 1])*rand()

    dist = sqrt(x^2 + y^2)

    dist < r ? Acircle += 1 : Acircle += 0 
end
return 4*(Acircle/N)

end

N = 10000
r = 1
calculate_pi(N, r)

for what i see, rand(Vector) gives an random element of the vector, and rand([-1,1]) is either -1 or 1, so that operation is to give the x and y coordinates a random sign

1 Like

You could also express something comparable as 2*rand()-1 — it’s just extending the range of generated values such that it covers the entire unit circle instead of just the upper right quadrant.

That said, the formula works just as well if you use x, y = rand(), rand()! That just looks at the upper right quadrant, but it works because you’re symmetrically reducing the areas of the circle and the square by the same fraction. :slight_smile:

4 Likes

If you care about performance, you should absolutely drop the rand([-1, 1]). It makes no difference to the result, and it makes your code almost 10x slower. Most of that is due to the vectors [-1, 1] you keep creating over and over, if you keep the rands but replace them with rand((-1, 1)) instead, you get a very large speed-up, since tuples are so efficient.

But, even so, just drop them entirely.

You can also get some improvement if you realize that

sqrt(x^2 + y^2) < r

is equivalent to

x^2 + y^2 < r^2  # and here you could just pre-calculate r^2

No need to calculate the sqrt.

Furthermore, you can add a Bool to an Int. Doesn’t seem to matter much to performance, but it’s more elegant, imo:

Acircle += (x^2 + y^2 < r^2)
3 Likes

If you care even more about performance, there’s no need to actually store x and y.

function calc_pi(N, r)
    Acirc = 0
    for i in 1:N
        Acirc += rand()^2 + rand()^2 < r^2
    end
    4Acirc / N
end

Running @btime on those:

julia> @btime calculate_pi(10^4, 1)
  972.581 μs (20000 allocations: 1.83 MiB)
3.136

julia> @btime calc_pi(10^4, 1)
  53.120 μs (0 allocations: 0 bytes)
3.144
1 Like

Assigning something to a local variable is just giving it a name. It has no effect on how/where the value is stored or on performance.

5 Likes

@stevengj Eh… I checked again and you’re right. Good to know!

I guess it saves a few keystrokes though :slight_smile:

1 Like