2次元平面に一様分布する乱数の生成

ある2次元平面内（正方形、円形）に一様分布する乱数（座標）を`Rust`で生成する。

# 前準備

## 必要な`crate`の用意
```toml:Cargo.toml
[package]
(省略)

[dependencies]
rand = "0.7.2"
```

## 散布図プロット用のスクリプト

今回はある意味デ・ファクト・スタンダードな`matplotlib`を使用したが、
良いプロット用の`crate`があればそっちを利用して全体を`Rust`で統一したい。

```python:graph.py
import numpy as np
import matplotlib as mpl
mpl.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.ticker as tick
import pandas as pd

plt.ioff()
plt.rcParams["font.family"] = "Times New Roman"
plt.rcParams["xtick.direction"] = "in"
plt.rcParams["ytick.direction"] = "in"
plt.rcParams["xtick.minor.visible"] = True
plt.rcParams["ytick.minor.visible"] = True
plt.rcParams["xtick.major.width"] = 1.5
plt.rcParams["ytick.major.width"] = 1.5
plt.rcParams["xtick.minor.width"] = 1.0
plt.rcParams["ytick.minor.width"] = 1.0
plt.rcParams["xtick.major.size"] = 10
plt.rcParams["ytick.major.size"] = 10
plt.rcParams["xtick.minor.size"] = 5
plt.rcParams["ytick.minor.size"] = 5
plt.rcParams["font.size"] = 20
plt.rcParams["axes.linewidth"] = 1.5
plt.rcParams["grid.linestyle"] = "--"
plt.rcParams["grid.color"] = "#808080"
plt.rcParams["mathtext.fontset"] = "stix"

df = pd.read_csv('./plots.csv')
ax = df.plot.scatter(x='x', y='y', grid=True, lw=1, figsize=(10,10), color='0.5')
plt.savefig('distribution.eps',  figsize=(5, 8), bbox_inches="tight", pad_inches=0.05, dpi=300, transparent=True)
```

大量の`plt.rcParams[]`は学術向けに図を作成したときのパラメータ調整の名残。
また、`pandas`を使用しているのはたまたま手元にあったものを流用しているためである。


# 生成とプロット

題名にある乱数の生成を行い、実際に散布図をプロットすることで結果を確かめる。
また、[集合体恐怖症（トライポフォビア）](https://ja.wikipedia.org/wiki/トライポフォビア)の方のためにグラフを折り畳んだ。

## 正方形

```rust:src/main.rs
use rand::Rng;

const N: u64 = 10000;  // サンプル数
const S: f64 = 1000.0; // 正方形の一辺の長さ

fn main() {
    let mut rng = rand::thread_rng();

    println!("x,y");
    for _ in 0..N {
       let x = rng.gen_range(0., S);
       let y = rng.gen_range(0., S);
       println!("{:+3.8},{:+3.8}", x, y);
    }
}
```

### プロット

```sh
$ cargo run >| plots.csv
$ python graph.py
```

<details><summary>グラフを表示</summary><div>

![square.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183420/35c93307-8cc1-8df4-fc64-ed032d537156.png)
</div></details>


#### 中心を原点にする

```rust
       let x = rng.gen_range(0., S);
       let y = rng.gen_range(0., S);
```

上記を下記で書き換える。

```rust
       let x = rng.gen_range( -0.5 * S, 0.5 * S);
       let y = rng.gen_range( -0.5 * S, 0.5 * S);
```

<details><summary>グラフを表示</summary><div>

![square_shift.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183420/56ddfa15-af48-470e-1dff-0220ab5a1f8e.png)
</div></details>

## 円形
[参考](https://teramonagi.hatenablog.com/entry/20141113/1415839321)

```rust:src/main.rs
use rand::Rng;
use std::f64::consts::PI;

const N: u64 = 10000;  // サンプル数
const R: f64 = 1000.0; // 半径

fn main() {
    let mut rng = rand::thread_rng();

    println!("x,y");
    for _ in 0..N {
        let theta = rng.gen_range(0., 2.0 * PI);
        let r = (2.0 * rng.gen_range(0., 0.5 * R.powf(2.0))).sqrt();
        let x = r * theta.cos();
        let y = r * theta.sin();
        println!("{:+3.8},{:+3.8}", x, y);
    }
}
```

### プロット

```sh
$ cargo run >| plots.csv
$ python graph.py
```
<details><summary>グラフを表示</summary><div>

![circle.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183420/930d23cb-53e8-d6af-c0b1-b3d132912729.png)
</div></details>

# Appendix

上の3つを合体させたものを下記に示す。

```rust:src/main.rs
use rand::Rng;
use std::f64::consts::PI;

const N: u64 = 10000;   // サンプル数
const S: f64 = 1000.0;  // サイズ

// 正方形
fn square_rand(rng: &mut rand::rngs::ThreadRng) -> (f64, f64) {
    (rng.gen_range(0., S), rng.gen_range(0., S))
}

// 正方形 (中心を原点に)
fn square_shift_rand(rng: &mut rand::rngs::ThreadRng) -> (f64, f64) {
    (rng.gen_range( -0.5 * S, 0.5 * S), rng.gen_range( -0.5 * S, 0.5 * S))
}

// 円形
fn circular_rand(rng: &mut rand::rngs::ThreadRng) -> (f64, f64) {
    let theta = rng.gen_range(0., 2.0 * PI);
    let r = (2.0 * rng.gen_range(0., 0.5 * S.powf(2.0))).sqrt();
    (r * theta.cos(), r * theta.sin())
}

fn main() {
    let mut rng = rand::thread_rng();

    println!("x,y");
    for _ in 0..N {
        let (x, y) = square_rand(&mut rng);
        // let (x, y) = square_rand(&mut rng);
        // let (x, y) = circular_rand(&mut rng);
        println!("{:+3.8},{:+3.8}", x, y);
    }
}
```

このように「`rng`を`&mut`で渡して使う」[^1]のは推奨されているのかはさておき、
奇を衒わずともAppendix以前に紹介した実装を使った方が良さそうではある。

[^1]: 一応コンパイルは通るしコードは正しく動作（しているようにみえる）。