LoginSignup
3
0

More than 3 years have passed since last update.

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

Last updated at Posted at 2019-12-14

ある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

グラフを表示

square.png

中心を原点にする

       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);

グラフを表示

square_shift.png

円形

参考

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

グラフを表示

circle.png

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以前に紹介した実装を使った方が良さそうではある。


  1. 一応コンパイルは通るしコードは正しく動作(しているようにみえる)。 

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0