Pi calculation, failed to resolve: maybe a missing crate `rand`?

New to Rust. Tried suggestions I googled, but still not getting rand crate. What am I doing wrong?

Robin

// pi_calc.rs

use rand::Rng;
use rand::distributions::{Distribution, Uniform};
use std::f64::consts::PI;

fn main()
{       let mut rng = rand::thread_rng();
    let dist = rand::distributions::Uniform::new_inclusive(0.0f64..1.0f64);
        let mut inside = 0f64;
        let mut i = 0f64;
    loop
        {       let x = dist.sample(&mut rng);
                let y = dist.sample(&mut rng);
        let sum_sq_xy = x*x + y*y;
                let sqrt = &sum_sq_xy.sqrt();
                if sqrt <= 1.0f64
                {       inside += 1.0f64;
                }
        i += 1.0f64;
                let pi_est = 4.0f64 * inside/i;
                print!("{}: Pi = {}",i,pi_est);
                if pi_est == std::f64::consts::PI
                {       break;
        }
    }
}

error[E0433]: failed to resolve: maybe a missing crate `rand`?
 --> pi_calc.rs:4:5
  |
4 | use rand::distributions::{Distribution, Uniform};
  |     ^^^^ maybe a missing crate `rand`?
  |
  = help: consider adding `extern crate rand` to use the `rand` crate

What's in your Cargo.toml? How are you compiling this code?

rand is not built in. Run cargo add rand or modify Cargo.toml

Thank you! I wasn't calling cargo correctly.

Next issue, when I run my program the Monte Carlo calculation doesn't converge to Pi, so I think something is off in how I'm using rand or maybe my formula for Pi. I have it stop after a million iterations. What have I got wrong in the math below?

I tried new_inclusive() with <= on compare. That doesn't work either.


$ cargo run
   Compiling pi_calc v0.1.0 (C:\code\rust\cargo\pi_calc)
    Finished dev [unoptimized + debuginfo] target(s) in 0.42s
     Running `target\debug\pi_calc.exe`
Pi = 3.141592653589793
Estimate = 3.142324 (1000000) stopped)

rower@P330 MINGW64 /c/code/rust/cargo/pi_calc (master)
$ cat src/main.rs
// pi_calc.rs

use rand::distributions::{Distribution, Uniform};

fn main()
{       let mut rng = rand::thread_rng();
    let dist = Uniform::new(0.0f64,1.0f64);
        let mut inside = 0f64;
        let mut i = 0f64;
        println!("Pi = {}", std::f64::consts::PI);
    loop
        {       let x = dist.sample(&mut rng);
                let y = dist.sample(&mut rng);
        let sum_sq_xy = x*x + y*y;
                let sqrt = &sum_sq_xy.sqrt();
                if sqrt < &1.0f64
                {       inside += 1.0f64;
                }
        i += 1.0f64;
                let pi_est = 4.0f64 * inside/i;
                print!("\rEstimate = {} ({})",pi_est,i);
                if i >= 1000000f64
                {       println!(" stopped");
                        break;
                }
                if pi_est == std::f64::consts::PI
                {       break;
        }
    }
}

How fast it is expected to converge? From the naive point of view, your estimate is relatively close to the true value (I mean, it's definitely not diverging and not converging to something obviously different).

Note that comparing float values for equality isn't generally a good idea, since this comparison will give many false negatives.

I changed the code back to new_inclusive and using <= for the compare. Says...

$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.02s
     Running `target\debug\pi_calc.exe`
Pi = 3.141592653589793
Estimate = 3.14191532 (100000000 iterations) stopped

While watching it run it is easy to see it isn't converging toward the correct 4th decimal place. 100M iterations is not reasonable. Good to 3 decimal places is not reasonable. Logically, either I'm using rand incorrectly (most likely), or my Pi formula is wrong, or rand is not a good random number generator, or Monte Carlo is not a good algorithm for calculating Pi.

The standard deviation of your estimate for n iterations is sqrt(π * (4 - π) / n), which for n = 100M comes out to 0.000164. So 3 decimals of accuracy is what you should expect. Every time you multiply n by 100 you'll get 1 more digit.

The estimate 3.141915 is 1.9 standard deviations above the true value -- slightly unlucky, but not too unusual. You expect to be more than 2 standard deviations off about 5% of the time.

4 Likes

I'd assume the latter - what you're doing is essentially relying on empirics which must eventually work for big numbers, but there is no such thing as deterministic "converging".

Relevant articles on the topic (https://academo.org/demos/estimating-pi-monte-carlo/, https://www.geeksforgeeks.org/estimating-value-pi-using-monte-carlo/, https://idrack.org/blog/2018/02/03/python-monte-carlo-simulation-calculate-pi/) also don't seem to get much farther than you, if not worse.

The rand crate's algorithm is ChaCha12, which passes several pseudo-randomness tests and is definitely not the cause of the issue. Your pi formula ist right, as is your usage of rand (as far as I can tell) - else your perceived error wouldn't be so subtle. You may improve your code by only using floats where necessary and using integers for the index and count, see a slightly better version of the code here: Rust Playground

There are much better algorithms known today, but you beat Archimedes! With some hard work he was able to prove that π is between 3 + 10/71 and 3 + 1/7, so between 3.1408 and 3.1429.

Your Monte Carlo estimate is not proof, but still, 3 digits isn't that bad for such a simple method.

3 Likes