ある2次元平面内(正方形、円形)に一様分布する乱数(座標)をRust
で生成する。
前準備
必要なcrate
の用意
Cargo.toml
[package]
(省略)
[dependencies]
rand = "0.7.2"
散布図プロット用のスクリプト
今回はある意味デ・ファクト・スタンダードなmatplotlib
を使用したが、
良いプロット用のcrate
があればそっちを利用して全体をRust
で統一したい。
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
を使用しているのはたまたま手元にあったものを流用しているためである。
生成とプロット
題名にある乱数の生成を行い、実際に散布図をプロットすることで結果を確かめる。
また、集合体恐怖症(トライポフォビア)の方のためにグラフを折り畳んだ。
正方形
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);
}
}
プロット
$ cargo run >| plots.csv
$ python graph.py
中心を原点にする
let x = rng.gen_range(0., S);
let y = rng.gen_range(0., S);
上記を下記で書き換える。
let x = rng.gen_range( -0.5 * S, 0.5 * S);
let y = rng.gen_range( -0.5 * S, 0.5 * S);
円形
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);
}
}
プロット
$ cargo run >| plots.csv
$ python graph.py
Appendix
上の3つを合体させたものを下記に示す。
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以前に紹介した実装を使った方が良さそうではある。
-
一応コンパイルは通るしコードは正しく動作(しているようにみえる)。 ↩