List of Random - text.Baldanders.info
tag:text.Baldanders.info,2024-03-09:/tags
2024-03-09T20:45:42+09:00
帰ってきた「しっぽのさきっちょ」
https://text.baldanders.info/images/avatar.jpg
https://text.baldanders.info/images/avatar.jpg
github.com/goark/mt/v2 をリリースした
tag:text.Baldanders.info,2024-03-09:/release/2024/03/goark-mt-v2/
2024-03-09T11:45:42+00:00
2024-03-09T11:45:46+00:00
Mersenne Twister 疑似乱数生成器を実装した拙作のパッケージを math/rand/v2 に対応することにした
Spiegel
https://baldanders.info/profile/
<p><a href="https://go.dev/">Go</a> 1.22 で <a href="https://pkg.go.dev/math/rand/v2" title="rand package - math/rand - pkg.go.dev"><code>math/rand/v2</code></a> パッケージが登場したため, <a href="http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/mt.html" title="Mersenne Twister: A random number generator (since 1997/10)">Mersenne Twister</a> 疑似乱数生成器を実装した拙作の <a href="https://github.com/goark/mt" title="goark/mt: Mersenne Twister; Pseudo Random Number Generator, Implemented by Golang"><code>github.com/goark/mt</code></a> パッケージも <a href="https://pkg.go.dev/math/rand/v2" title="rand package - math/rand - pkg.go.dev"><code>math/rand/v2</code></a> に対応することにした。</p>
<p>まずはバージョンを v2 に上げて,以下のインポート・パスに変更した。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">"github.com/goark/mt/v2"</span>
</span></span></code></pre></div><p>また <code>go.mod</code> も同様に</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">require github.com/goark/mt/v2 v2.0.1
</span></span></code></pre></div><p>とする。</p>
<p>例として <a href="https://github.com/goark/mt" title="goark/mt: Mersenne Twister; Pseudo Random Number Generator, Implemented by Golang"><code>github.com/goark/mt/v2</code></a> パッケージと <a href="https://pkg.go.dev/math/rand/v2" title="rand package - math/rand - pkg.go.dev"><code>math/rand/v2</code></a> パッケージを組み合わせて標準正規分布する値を1万個生成してみる。
こんな感じ。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"math"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"math/rand/v2"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/goark/mt/v2/mt19937"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rnd</span> <span class="o">:=</span> <span class="nx">rand</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="nx">mt19937</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="nx">rand</span><span class="p">.</span><span class="nf">Int64</span><span class="p">()))</span>
</span></span><span class="line"><span class="cl"> <span class="nx">points</span> <span class="o">:=</span> <span class="p">[]</span><span class="kt">float64</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">max</span> <span class="o">:=</span> <span class="mf">0.0</span>
</span></span><span class="line"><span class="cl"> <span class="nx">min</span> <span class="o">:=</span> <span class="mf">1.0</span>
</span></span><span class="line"><span class="cl"> <span class="nx">sum</span> <span class="o">:=</span> <span class="mf">0.0</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="k">range</span> <span class="mi">10000</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">point</span> <span class="o">:=</span> <span class="nx">rnd</span><span class="p">.</span><span class="nf">NormFloat64</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nx">points</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">points</span><span class="p">,</span> <span class="nx">point</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">min</span> <span class="p">=</span> <span class="nx">math</span><span class="p">.</span><span class="nf">Min</span><span class="p">(</span><span class="nx">min</span><span class="p">,</span> <span class="nx">point</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">max</span> <span class="p">=</span> <span class="nx">math</span><span class="p">.</span><span class="nf">Max</span><span class="p">(</span><span class="nx">max</span><span class="p">,</span> <span class="nx">point</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">sum</span> <span class="o">+=</span> <span class="nx">point</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">n</span> <span class="o">:=</span> <span class="nb">float64</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="nx">points</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="nx">ave</span> <span class="o">:=</span> <span class="nx">sum</span> <span class="o">/</span> <span class="nx">n</span>
</span></span><span class="line"><span class="cl"> <span class="nx">d2</span> <span class="o">:=</span> <span class="mf">0.0</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">p</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">points</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">d2</span> <span class="o">+=</span> <span class="p">(</span><span class="nx">p</span> <span class="o">-</span> <span class="nx">ave</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="nx">p</span> <span class="o">-</span> <span class="nx">ave</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">" minimum: "</span><span class="p">,</span> <span class="nx">min</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">" maximum: "</span><span class="p">,</span> <span class="nx">max</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">" average: "</span><span class="p">,</span> <span class="nx">ave</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">"standard deviation: "</span><span class="p">,</span> <span class="nx">math</span><span class="p">.</span><span class="nf">Sqrt</span><span class="p">(</span><span class="nx">d2</span><span class="o">/</span><span class="nx">n</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>これを実行するとこんな感じになる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go run sample.go
</span></span><span class="line"><span class="cl"> minimum: -4.465497509270884
</span></span><span class="line"><span class="cl"> maximum: 4.409945906326592
</span></span><span class="line"><span class="cl"> average: 0.010399867661332784
</span></span><span class="line"><span class="cl">standard deviation: 1.0027323703801945
</span></span></code></pre></div><p>まぁまぁ妥当な感じ?</p>
<p><a href="https://pkg.go.dev/math/rand" title="rand package - math/rand - Go Packages"><code>math/rand</code></a> および <a href="https://pkg.go.dev/math/rand/v2" title="rand package - math/rand - pkg.go.dev"><code>math/rand/v2</code></a> パッケージのトップレベル関数群の疑似乱数生成器が ChaCha8 になったおかげで seed を与えるのがめっちゃ楽になった。
これだけでもありがたい。
ともかくこれで,乱数生成周りの調査と対応は一通り完了かな。</p>
<h2>ブックマーク</h2>
<ul>
<li><a href="https://text.baldanders.info/release/mersenne-twister-by-golang/">疑似乱数生成器 goark/mt</a></li>
<li><a href="https://text.baldanders.info/golang/pseudo-random-number-generator-v2/">Go 1.22 における疑似乱数生成器</a></li>
<li><a href="https://zenn.dev/spiegel/articles/20240309-golang-math-rand-v2">Go 1.22 の math/rand/v2 を使ってみる</a></li>
</ul>
<h2>参考図書</h2>
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/416Stewy0NS._SL160_.jpg" width="123" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">プログラミング言語Go</a></dt>
<dd>アラン・ドノバン (著), ブライアン・カーニハン (著), 柴田芳樹 (著)</dd>
<dd>丸善出版 2016-06-20 (Release 2021-07-13)</dd>
<dd>Kindle版</dd>
<dd>B099928SJD (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">Kindle 版出た! 一部内容が古びてしまったが,この本は Go 言語の教科書と言ってもいいだろう。感想は<a href="https://text.baldanders.info/remark/2016/07/go-programming-language/" >こちら</a>。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2021-05-22">2021-05-22</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- プログラミング言語Go -->
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B0CFL1DK8Q?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/51BmDUG6D0L._SL160_.jpg" width="125" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B0CFL1DK8Q?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">Go言語 100Tips ありがちなミスを把握し、実装を最適化する impress top gearシリーズ</a></dt>
<dd>Teiva Harsanyi (著), 柴田 芳樹 (著)</dd>
<dd>インプレス 2023-08-18 (Release 2023-08-18)</dd>
<dd>Kindle版</dd>
<dd>B0CFL1DK8Q (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="4"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="far fa-star"></i></abbr></dd>
</dl>
<p class="description"><a href="https://book.impress.co.jp/books/1122101133">版元</a>で PDF 版を購入可能。事実上の Effective Go とも言える充実の内容。オリジナルは敢えてタイトルに “tips” という単語を入れるのを避けたのに邦題が「100 Tips」とかなっていて,原作者がお怒りとの噂(あくまで噂)</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2023-08-18">2023-08-18</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- Go言語 100Tips -->
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/4814400535?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/41+ew2wl2jL._SL160_.jpg" width="125" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/4814400535?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">効率的なGo ―データ指向によるGoアプリケーションの性能最適化</a></dt>
<dd>Bartłomiej Płotka (著), 山口 能迪 (翻訳)</dd>
<dd>オライリー・ジャパン 2024-02-24</dd>
<dd>単行本(ソフトカバー)</dd>
<dd>4814400535 (ASIN), 9784814400539 (EAN), 4814400535 (ISBN)</dd>
</dl>
<p class="description">ついに Effective Go のタイトルを冠した本の邦訳が出た。<a href="https://www.oreilly.co.jp/books/9784814400539/">版元</a>で Ebook を買える。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2024-02-22">2024-02-22</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- 効率的なGo : Effective Go -->
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B015643CPE?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/51t6yHHVwEL._SL160_.jpg" width="113" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B015643CPE?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">暗号技術入門 第3版 秘密の国のアリス</a></dt>
<dd>結城 浩 (著)</dd>
<dd>SBクリエイティブ 2015-08-25 (Release 2015-09-17)</dd>
<dd>Kindle版</dd>
<dd>B015643CPE (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">SHA-3 や Bitcoin/Blockchain など新しい知見や技術要素を大幅追加。暗号技術を使うだけならこれ1冊でとりあえず無問題。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2015-09-20">2015-09-20</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- 暗号技術入門 第3版 -->
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B00I8AT1FO?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/41353H+BzFL._SL160_.jpg" width="113" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B00I8AT1FO?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">数学ガール/乱択アルゴリズム</a></dt>
<dd>結城 浩 (著)</dd>
<dd>SBクリエイティブ 2011-02-25 (Release 2014-03-12)</dd>
<dd>Kindle版</dd>
<dd>B00I8AT1FO (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">工学ガール,リサちゃん登場!</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2015-04-19">2015-04-19</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- 数学ガール/乱択アルゴリズム -->
Go 1.22 における疑似乱数生成器
tag:text.Baldanders.info,2024-03-07:/golang/pseudo-random-number-generator-v2/
2024-03-07T13:20:40+00:00
2024-03-09T06:48:01+00:00
時代は math/rand/v2 かな
Spiegel
https://baldanders.info/profile/
<p><a href="https://go.dev/">Go</a> 1.22 の <a href="https://pkg.go.dev/math/rand" title="rand package - math/rand - Go Packages"><code>math/rand</code></a> パッケージと追加された <a href="https://pkg.go.dev/math/rand/v2" title="rand package - math/rand/v2 - Go Packages"><code>math/rand/v2</code></a> パッケージを眺めている。</p>
<p>おそらく <a href="https://go.dev/">Go</a> 1.22 における疑似乱数関連の最大のトピックは ChaCha8 がランタイムに組み込まれ,疑似乱数生成器の既定アルゴリズムになったことだろう。</p>
<ul>
<li><span><a href="https://cr.yp.to/snuffle/salsafamily-20071225.pdf">The Salsa20 family of stream ciphers <sup><i class="far fa-file-pdf"></i></sup></a></span> : これがベースになる論文かな</li>
<li><a href="https://en.wikipedia.org/wiki/Salsa20">Salsa20 - Wikipedia</a></li>
<li><a href="https://github.com/C2SP/C2SP/blob/main/chacha8rand.md">C2SP/chacha8rand.md at main · C2SP/C2SP · GitHub</a></li>
<li><a href="https://convto.hatenablog.com/entry/2024/02/26/121013">つくって理解するストリーム暗号 ChaCha20 - ちりもつもればミルキーウェイ</a> : <a href="https://go.dev/">Go</a> でサンプルコードを書いておられる。ありがたや</li>
</ul>
<p>ChaCha はストリーム暗号の一種で,簡単に言うと,疑似乱数を生成してそれを平文と XOR するというものらしい。
このうちの疑似乱数を生成する部分を切り出しているようだ。
ストリーム暗号に使うものなので,暗号技術的にセキュアでかつ速いというのが特徴になるだろうか。
ちなみに ChaCha の後ろについている 20 とか 8 とかはラウンド数を示しているそうな。</p>
<p>ChaCha は OpenSSL だか OpenSSH だかでも見かけたような(うろ覚え)。
もし結城浩さんが『<a href="https://www.amazon.co.jp/dp/B015643CPE?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1" title="Amazon.co.jp: 暗号技術入門 第3版 秘密の国のアリス eBook : 結城 浩: Kindleストア">暗号技術入門</a>』の第4版を出される機会があれば,付録でいいので是非 ChaCha にも言及して欲しい。</p>
<h2>ランタイムに ChaCha8 疑似乱数生成器を組み込む</h2>
<p>ChaCha8 疑似乱数生成器のアルゴリズムは <a href="https://pkg.go.dev/internal/chacha8rand" title="chacha8rand package - internal/chacha8rand - Go Packages"><code>internal/chacha8rand</code></a> パッケージに実装されている。
中身については割愛させてもらう。
Internal パッケージなので,サードパーティのパッケージからは直接参照できない。</p>
<p>まずは <a href="https://pkg.go.dev/runtime" title="runtime package - runtime - Go Packages"><code>runtime</code></a> パッケージに組み込んでいる部分を見てみる。
ちょっと長いけどご容赦。</p>
<figure lang="en">
<blockquote class="nobox"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// OS-specific startup can set startupRand if the OS passes
</span></span></span><span class="line"><span class="cl"><span class="c1">// random data to the process at startup time.
</span></span></span><span class="line"><span class="cl"><span class="c1">// For example Linux passes 16 bytes in the auxv vector.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">startupRand</span> <span class="p">[]</span><span class="kt">byte</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// globalRand holds the global random state.
</span></span></span><span class="line"><span class="cl"><span class="c1">// It is only used at startup and for creating new m's.
</span></span></span><span class="line"><span class="cl"><span class="c1">// Otherwise the per-m random state should be used
</span></span></span><span class="line"><span class="cl"><span class="c1">// by calling goodrand.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">globalRand</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">lock</span> <span class="nx">mutex</span>
</span></span><span class="line"><span class="cl"> <span class="nx">seed</span> <span class="p">[</span><span class="mi">32</span><span class="p">]</span><span class="kt">byte</span>
</span></span><span class="line"><span class="cl"> <span class="nx">state</span> <span class="nx">chacha8rand</span><span class="p">.</span><span class="nx">State</span>
</span></span><span class="line"><span class="cl"> <span class="nx">init</span> <span class="kt">bool</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">readRandomFailed</span> <span class="kt">bool</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// randinit initializes the global random state.
</span></span></span><span class="line"><span class="cl"><span class="c1">// It must be called before any use of grand.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">randinit</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nf">lock</span><span class="p">(</span><span class="o">&</span><span class="nx">globalRand</span><span class="p">.</span><span class="nx">lock</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">globalRand</span><span class="p">.</span><span class="nx">init</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nf">fatal</span><span class="p">(</span><span class="s">"randinit twice"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nx">seed</span> <span class="o">:=</span> <span class="o">&</span><span class="nx">globalRand</span><span class="p">.</span><span class="nx">seed</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">startupRand</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span><span class="p">,</span> <span class="nx">c</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">startupRand</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">seed</span><span class="p">[</span><span class="nx">i</span><span class="o">%</span><span class="nb">len</span><span class="p">(</span><span class="nx">seed</span><span class="p">)]</span> <span class="p">^=</span> <span class="nx">c</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nb">clear</span><span class="p">(</span><span class="nx">startupRand</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">startupRand</span> <span class="p">=</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nf">readRandom</span><span class="p">(</span><span class="nx">seed</span><span class="p">[:])</span> <span class="o">!=</span> <span class="nb">len</span><span class="p">(</span><span class="nx">seed</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// readRandom should never fail, but if it does we'd rather
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// not make Go binaries completely unusable, so make up
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// some random data based on the current time.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">readRandomFailed</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl"> <span class="nf">readTimeRandom</span><span class="p">(</span><span class="nx">seed</span><span class="p">[:])</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">globalRand</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nf">Init</span><span class="p">(</span><span class="o">*</span><span class="nx">seed</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nb">clear</span><span class="p">(</span><span class="nx">seed</span><span class="p">[:])</span>
</span></span><span class="line"><span class="cl"> <span class="nx">globalRand</span><span class="p">.</span><span class="nx">init</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl"> <span class="nf">unlock</span><span class="p">(</span><span class="o">&</span><span class="nx">globalRand</span><span class="p">.</span><span class="nx">lock</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// readTimeRandom stretches any entropy in the current time
</span></span></span><span class="line"><span class="cl"><span class="c1">// into entropy the length of r and XORs it into r.
</span></span></span><span class="line"><span class="cl"><span class="c1">// This is a fallback for when readRandom does not read
</span></span></span><span class="line"><span class="cl"><span class="c1">// the full requested amount.
</span></span></span><span class="line"><span class="cl"><span class="c1">// Whatever entropy r already contained is preserved.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">readTimeRandom</span><span class="p">(</span><span class="nx">r</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// Inspired by wyrand.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// An earlier version of this code used getg().m.procid as well,
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// but note that this is called so early in startup that procid
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// is not initialized yet.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">v</span> <span class="o">:=</span> <span class="nb">uint64</span><span class="p">(</span><span class="nf">nanotime</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nb">len</span><span class="p">(</span><span class="nx">r</span><span class="p">)</span> <span class="p">></span> <span class="mi">0</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">v</span> <span class="p">^=</span> <span class="mh">0xa0761d6478bd642f</span>
</span></span><span class="line"><span class="cl"> <span class="nx">v</span> <span class="o">*=</span> <span class="mh">0xe7037ed1a0b428db</span>
</span></span><span class="line"><span class="cl"> <span class="nx">size</span> <span class="o">:=</span> <span class="mi">8</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="nx">r</span><span class="p">)</span> <span class="p"><</span> <span class="mi">8</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">size</span> <span class="p">=</span> <span class="nb">len</span><span class="p">(</span><span class="nx">r</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">size</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">r</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="p">^=</span> <span class="nb">byte</span><span class="p">(</span><span class="nx">v</span> <span class="o">>></span> <span class="p">(</span><span class="mi">8</span> <span class="o">*</span> <span class="nx">i</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">r</span> <span class="p">=</span> <span class="nx">r</span><span class="p">[</span><span class="nx">size</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">v</span> <span class="p">=</span> <span class="nx">v</span><span class="o">>></span><span class="mi">32</span> <span class="p">|</span> <span class="nx">v</span><span class="o"><<</span><span class="mi">32</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></blockquote>
<figcaption><div>via <q><a href="https://pkg.go.dev/runtime">runtime/rand.go</a></q></div></figcaption>
</figure>
<p>これは疑似乱数生成器の状態(主に seed)を管理してる部分かな。
最初の seed は乱数デバイスから取ってるんだね。
これに失敗すると時刻から生成する,と。
ユーザ側は明示的に seed を指定する必要がなくなるということやね。</p>
<figure lang="en">
<blockquote class="nobox"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// readTimeRandom stretches any entropy in the current time
</span></span></span><span class="line"><span class="cl"><span class="c1">// into entropy the length of r and XORs it into r.
</span></span></span><span class="line"><span class="cl"><span class="c1">// This is a fallback for when readRandom does not read
</span></span></span><span class="line"><span class="cl"><span class="c1">// the full requested amount.
</span></span></span><span class="line"><span class="cl"><span class="c1">// Whatever entropy r already contained is preserved.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">readTimeRandom</span><span class="p">(</span><span class="nx">r</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// Inspired by wyrand.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// An earlier version of this code used getg().m.procid as well,
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// but note that this is called so early in startup that procid
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// is not initialized yet.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">v</span> <span class="o">:=</span> <span class="nb">uint64</span><span class="p">(</span><span class="nf">nanotime</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nb">len</span><span class="p">(</span><span class="nx">r</span><span class="p">)</span> <span class="p">></span> <span class="mi">0</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">v</span> <span class="p">^=</span> <span class="mh">0xa0761d6478bd642f</span>
</span></span><span class="line"><span class="cl"> <span class="nx">v</span> <span class="o">*=</span> <span class="mh">0xe7037ed1a0b428db</span>
</span></span><span class="line"><span class="cl"> <span class="nx">size</span> <span class="o">:=</span> <span class="mi">8</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="nx">r</span><span class="p">)</span> <span class="p"><</span> <span class="mi">8</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">size</span> <span class="p">=</span> <span class="nb">len</span><span class="p">(</span><span class="nx">r</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">size</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">r</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="p">^=</span> <span class="nb">byte</span><span class="p">(</span><span class="nx">v</span> <span class="o">>></span> <span class="p">(</span><span class="mi">8</span> <span class="o">*</span> <span class="nx">i</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">r</span> <span class="p">=</span> <span class="nx">r</span><span class="p">[</span><span class="nx">size</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">v</span> <span class="p">=</span> <span class="nx">v</span><span class="o">>></span><span class="mi">32</span> <span class="p">|</span> <span class="nx">v</span><span class="o"><<</span><span class="mi">32</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// bootstrapRand returns a random uint64 from the global random generator.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">bootstrapRand</span><span class="p">()</span> <span class="kt">uint64</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nf">lock</span><span class="p">(</span><span class="o">&</span><span class="nx">globalRand</span><span class="p">.</span><span class="nx">lock</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">!</span><span class="nx">globalRand</span><span class="p">.</span><span class="nx">init</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nf">fatal</span><span class="p">(</span><span class="s">"randinit missed"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">x</span><span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nx">globalRand</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nf">Next</span><span class="p">();</span> <span class="nx">ok</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nf">unlock</span><span class="p">(</span><span class="o">&</span><span class="nx">globalRand</span><span class="p">.</span><span class="nx">lock</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">x</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">globalRand</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nf">Refill</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// bootstrapRandReseed reseeds the bootstrap random number generator,
</span></span></span><span class="line"><span class="cl"><span class="c1">// clearing from memory any trace of previously returned random numbers.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">bootstrapRandReseed</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nf">lock</span><span class="p">(</span><span class="o">&</span><span class="nx">globalRand</span><span class="p">.</span><span class="nx">lock</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">!</span><span class="nx">globalRand</span><span class="p">.</span><span class="nx">init</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nf">fatal</span><span class="p">(</span><span class="s">"randinit missed"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">globalRand</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nf">Reseed</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nf">unlock</span><span class="p">(</span><span class="o">&</span><span class="nx">globalRand</span><span class="p">.</span><span class="nx">lock</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// rand32 is uint32(rand()), called from compiler-generated code.
</span></span></span><span class="line"><span class="cl"><span class="c1">//go:nosplit
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">rand32</span><span class="p">()</span> <span class="kt">uint32</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nb">uint32</span><span class="p">(</span><span class="nf">rand</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// rand returns a random uint64 from the per-m chacha8 state.
</span></span></span><span class="line"><span class="cl"><span class="c1">// Do not change signature: used via linkname from other packages.
</span></span></span><span class="line"><span class="cl"><span class="c1">//go:nosplit
</span></span></span><span class="line"><span class="cl"><span class="c1">//go:linkname rand
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">rand</span><span class="p">()</span> <span class="kt">uint64</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// Note: We avoid acquirem here so that in the fast path
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// there is just a getg, an inlined c.Next, and a return.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// The performance difference on a 16-core AMD is
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// 3.7ns/call this way versus 4.3ns/call with acquirem (+16%).
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">mp</span> <span class="o">:=</span> <span class="nf">getg</span><span class="p">().</span><span class="nx">m</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c</span> <span class="o">:=</span> <span class="o">&</span><span class="nx">mp</span><span class="p">.</span><span class="nx">chacha8</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// Note: c.Next is marked nosplit,
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// so we don't need to use mp.locks
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// on the fast path, which is that the
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// first attempt succeeds.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">x</span><span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nx">c</span><span class="p">.</span><span class="nf">Next</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">ok</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">x</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">mp</span><span class="p">.</span><span class="nx">locks</span><span class="o">++</span> <span class="c1">// hold m even though c.Refill may do stack split checks
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">c</span><span class="p">.</span><span class="nf">Refill</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nx">mp</span><span class="p">.</span><span class="nx">locks</span><span class="o">--</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// mrandinit initializes the random state of an m.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">mrandinit</span><span class="p">(</span><span class="nx">mp</span> <span class="o">*</span><span class="nx">m</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">seed</span> <span class="p">[</span><span class="mi">4</span><span class="p">]</span><span class="kt">uint64</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">seed</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">seed</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="p">=</span> <span class="nf">bootstrapRand</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nf">bootstrapRandReseed</span><span class="p">()</span> <span class="c1">// erase key we just extracted
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">mp</span><span class="p">.</span><span class="nx">chacha8</span><span class="p">.</span><span class="nf">Init64</span><span class="p">(</span><span class="nx">seed</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">mp</span><span class="p">.</span><span class="nx">cheaprand</span> <span class="p">=</span> <span class="nf">rand</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></blockquote>
<figcaption><div>via <q><a href="https://pkg.go.dev/runtime">runtime/rand.go</a></q></div></figcaption>
</figure>
<p><code>mrandinit()</code> 関数でランタイムを初期化して,それを使って実際に乱数を取得してるのが <code>rand()</code> 関数だね。
ふむふむ。</p>
<h2>math/rand パッケージのトップレベル関数群にランタイムの ChaCha8 を組み込む</h2>
<p>それじゃあ <a href="https://pkg.go.dev/math/rand" title="rand package - math/rand - Go Packages"><code>math/rand</code></a> パッケージの方を見てみよう。</p>
<figure lang="en">
<blockquote class="nobox"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">//go:linkname runtime_rand runtime.rand
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">runtime_rand</span><span class="p">()</span> <span class="kt">uint64</span>
</span></span></code></pre></div></blockquote>
<figcaption><div>via <q><a href="https://pkg.go.dev/math/rand">math/rand/rand.go</a></q></div></figcaption>
</figure>
<p><code>go:linkname</code> ディレクティブの説明は割愛する。
こうやってリンクしてるということで飲み込んでいただければ(笑) そうそう。
<code>go:linkname</code> ディレクティブは <a href="https://pkg.go.dev/unsafe" title="unsafe package - unsafe - Go Packages"><code>unsafe</code></a> パッケージを要求するので,真似するときは要注意だよ。</p>
<p>ともかく,この <code>runtime_rand()</code> 関数を使って <a href="https://pkg.go.dev/math/rand" title="rand package - math/rand - Go Packages"><code>rand</code></a><code>.Source</code> インタフェース互換の構造体 <code>runtimeSource</code> を定義している。</p>
<figure lang="en">
<blockquote class="nobox"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// runtimeSource is an implementation of Source64 that uses the runtime
</span></span></span><span class="line"><span class="cl"><span class="c1">// fastrand functions.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">runtimeSource</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// The mutex is used to avoid race conditions in Read.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">mu</span> <span class="nx">sync</span><span class="p">.</span><span class="nx">Mutex</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="o">*</span><span class="nx">runtimeSource</span><span class="p">)</span> <span class="nf">Int63</span><span class="p">()</span> <span class="kt">int64</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nb">int64</span><span class="p">(</span><span class="nf">runtime_rand</span><span class="p">()</span> <span class="o">&</span> <span class="nx">rngMask</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="o">*</span><span class="nx">runtimeSource</span><span class="p">)</span> <span class="nf">Seed</span><span class="p">(</span><span class="kt">int64</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nb">panic</span><span class="p">(</span><span class="s">"internal error: call to runtimeSource.Seed"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="o">*</span><span class="nx">runtimeSource</span><span class="p">)</span> <span class="nf">Uint64</span><span class="p">()</span> <span class="kt">uint64</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nf">runtime_rand</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></blockquote>
<figcaption><div>via <q><a href="https://pkg.go.dev/math/rand">math/rand/rand.go</a></q></div></figcaption>
</figure>
<p><code>Seed()</code> メソッドを呼び出したら panic 吐くとか容赦ないな(笑) <code>runtimeSource</code> はこんな風に使う。</p>
<figure lang="en">
<blockquote class="nobox"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// globalRandGenerator is the source of random numbers for the top-level
</span></span></span><span class="line"><span class="cl"><span class="c1">// convenience functions. When possible it uses the runtime fastrand64
</span></span></span><span class="line"><span class="cl"><span class="c1">// function to avoid locking. This is not possible if the user called Seed,
</span></span></span><span class="line"><span class="cl"><span class="c1">// either explicitly or implicitly via GODEBUG=randautoseed=0.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">globalRandGenerator</span> <span class="nx">atomic</span><span class="p">.</span><span class="nx">Pointer</span><span class="p">[</span><span class="nx">Rand</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">randautoseed</span> <span class="p">=</span> <span class="nx">godebug</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">"randautoseed"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// globalRand returns the generator to use for the top-level convenience
</span></span></span><span class="line"><span class="cl"><span class="c1">// functions.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">globalRand</span><span class="p">()</span> <span class="o">*</span><span class="nx">Rand</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">r</span> <span class="o">:=</span> <span class="nx">globalRandGenerator</span><span class="p">.</span><span class="nf">Load</span><span class="p">();</span> <span class="nx">r</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">r</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// This is the first call. Initialize based on GODEBUG.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kd">var</span> <span class="nx">r</span> <span class="o">*</span><span class="nx">Rand</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">randautoseed</span><span class="p">.</span><span class="nf">Value</span><span class="p">()</span> <span class="o">==</span> <span class="s">"0"</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">randautoseed</span><span class="p">.</span><span class="nf">IncNonDefault</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nx">r</span> <span class="p">=</span> <span class="nf">New</span><span class="p">(</span><span class="nb">new</span><span class="p">(</span><span class="nx">lockedSource</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="nx">r</span><span class="p">.</span><span class="nf">Seed</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">r</span> <span class="p">=</span> <span class="o">&</span><span class="nx">Rand</span><span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">src</span><span class="p">:</span> <span class="o">&</span><span class="nx">runtimeSource</span><span class="p">{},</span>
</span></span><span class="line"><span class="cl"> <span class="nx">s64</span><span class="p">:</span> <span class="o">&</span><span class="nx">runtimeSource</span><span class="p">{},</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">!</span><span class="nx">globalRandGenerator</span><span class="p">.</span><span class="nf">CompareAndSwap</span><span class="p">(</span><span class="kc">nil</span><span class="p">,</span> <span class="nx">r</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// Two different goroutines called some top-level
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// function at the same time. While the results in
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// that case are unpredictable, if we just use r here,
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// and we are using a seed, we will most likely return
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// the same value for both calls. That doesn't seem ideal.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// Just use the first one to get in.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">return</span> <span class="nx">globalRandGenerator</span><span class="p">.</span><span class="nf">Load</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">r</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></blockquote>
<figcaption><div>via <q><a href="https://pkg.go.dev/math/rand">math/rand/rand.go</a></q></div></figcaption>
</figure>
<figure lang="en">
<blockquote class="nobox"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// Seed uses the provided seed value to initialize the default Source to a
</span></span></span><span class="line"><span class="cl"><span class="c1">// deterministic state. Seed values that have the same remainder when
</span></span></span><span class="line"><span class="cl"><span class="c1">// divided by 2³¹-1 generate the same pseudo-random sequence.
</span></span></span><span class="line"><span class="cl"><span class="c1">// Seed, unlike the [Rand.Seed] method, is safe for concurrent use.
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// If Seed is not called, the generator is seeded randomly at program startup.
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// Prior to Go 1.20, the generator was seeded like Seed(1) at program startup.
</span></span></span><span class="line"><span class="cl"><span class="c1">// To force the old behavior, call Seed(1) at program startup.
</span></span></span><span class="line"><span class="cl"><span class="c1">// Alternately, set GODEBUG=randautoseed=0 in the environment
</span></span></span><span class="line"><span class="cl"><span class="c1">// before making any calls to functions in this package.
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// Deprecated: As of Go 1.20 there is no reason to call Seed with
</span></span></span><span class="line"><span class="cl"><span class="c1">// a random value. Programs that call Seed with a known value to get
</span></span></span><span class="line"><span class="cl"><span class="c1">// a specific sequence of results should use New(NewSource(seed)) to
</span></span></span><span class="line"><span class="cl"><span class="c1">// obtain a local random generator.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">Seed</span><span class="p">(</span><span class="nx">seed</span> <span class="kt">int64</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">orig</span> <span class="o">:=</span> <span class="nx">globalRandGenerator</span><span class="p">.</span><span class="nf">Load</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// If we are already using a lockedSource, we can just re-seed it.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="nx">orig</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nx">orig</span><span class="p">.</span><span class="nx">src</span><span class="p">.(</span><span class="o">*</span><span class="nx">lockedSource</span><span class="p">);</span> <span class="nx">ok</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">orig</span><span class="p">.</span><span class="nf">Seed</span><span class="p">(</span><span class="nx">seed</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Otherwise either
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// 1) orig == nil, which is the normal case when Seed is the first
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// top-level function to be called, or
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// 2) orig is already a runtimeSource, in which case we need to change
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// to a lockedSource.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// Either way we do the same thing.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"> <span class="nx">r</span> <span class="o">:=</span> <span class="nf">New</span><span class="p">(</span><span class="nb">new</span><span class="p">(</span><span class="nx">lockedSource</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="nx">r</span><span class="p">.</span><span class="nf">Seed</span><span class="p">(</span><span class="nx">seed</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">!</span><span class="nx">globalRandGenerator</span><span class="p">.</span><span class="nf">CompareAndSwap</span><span class="p">(</span><span class="nx">orig</span><span class="p">,</span> <span class="nx">r</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// Something changed underfoot. Retry to be safe.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">Seed</span><span class="p">(</span><span class="nx">seed</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></blockquote>
<figcaption><div>via <q><a href="https://pkg.go.dev/math/rand">math/rand/rand.go</a></q></div></figcaption>
</figure>
<p>つまり,環境変数 <code>GODEBUG</code> で明示的に指定(<code>randautoseed=0</code>)するか最初に <a href="https://pkg.go.dev/math/rand" title="rand package - math/rand - Go Packages"><code>rand</code></a><code>.Seed()</code> 関数を呼び出すかしない限りランタイムに組み込んだ ChaCha8 疑似乱数生成器が有効になるっちうわけだ。
ちなみに <code>lockedSource</code> は <a href="https://pkg.go.dev/math/rand" title="rand package - math/rand - Go Packages"><code>math/rand</code></a> パッケージに従来からある疑似乱数生成器で,名前の通り,ちゃんと mutex で排他処理している。</p>
<h2>math/rand/v2 パッケージにおける Source インタフェースの定義</h2>
<p>では,いよいよ <a href="https://go.dev/">Go</a> 1.22 で追加された <a href="https://pkg.go.dev/math/rand/v2" title="rand package - math/rand/v2 - Go Packages"><code>math/rand/v2</code></a> パッケージを見てみよう。</p>
<p><a href="https://pkg.go.dev/math/rand" title="rand package - math/rand - Go Packages"><code>math/rand</code></a> パッケージと <a href="https://pkg.go.dev/math/rand/v2" title="rand package - math/rand/v2 - Go Packages"><code>math/rand/v2</code></a> パッケージとの大きな違いは <code>rand.Source</code> インタフェースが非互換になっていることだろう。</p>
<p><a href="https://pkg.go.dev/math/rand" title="rand package - math/rand - Go Packages"><code>math/rand</code></a> パッケージの <code>Source</code> インタフェースの定義は以下の通り。</p>
<figure lang="en">
<blockquote class="nobox"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// A Source represents a source of uniformly-distributed
</span></span></span><span class="line"><span class="cl"><span class="c1">// pseudo-random int64 values in the range [0, 1<<63).
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// A Source is not safe for concurrent use by multiple goroutines.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">Source</span> <span class="kd">interface</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nf">Int63</span><span class="p">()</span> <span class="kt">int64</span>
</span></span><span class="line"><span class="cl"> <span class="nf">Seed</span><span class="p">(</span><span class="nx">seed</span> <span class="kt">int64</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// A Source64 is a [Source] that can also generate
</span></span></span><span class="line"><span class="cl"><span class="c1">// uniformly-distributed pseudo-random uint64 values in
</span></span></span><span class="line"><span class="cl"><span class="c1">// the range [0, 1<<64) directly.
</span></span></span><span class="line"><span class="cl"><span class="c1">// If a [Rand] r's underlying [Source] s implements Source64,
</span></span></span><span class="line"><span class="cl"><span class="c1">// then r.Uint64 returns the result of one call to s.Uint64
</span></span></span><span class="line"><span class="cl"><span class="c1">// instead of making two calls to s.Int63.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">Source64</span> <span class="kd">interface</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Source</span>
</span></span><span class="line"><span class="cl"> <span class="nf">Uint64</span><span class="p">()</span> <span class="kt">uint64</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></blockquote>
<figcaption><div>via <q><a href="https://pkg.go.dev/math/rand">math/rand/rand.go</a></q></div></figcaption>
</figure>
<p>これに対して <a href="https://pkg.go.dev/math/rand/v2" title="rand package - math/rand/v2 - Go Packages"><code>math/rand/v2</code></a> ではこう定義されている。</p>
<figure lang="en">
<blockquote class="nobox"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// A Source is a source of uniformly-distributed
</span></span></span><span class="line"><span class="cl"><span class="c1">// pseudo-random uint64 values in the range [0, 1<<64).
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// A Source is not safe for concurrent use by multiple goroutines.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">Source</span> <span class="kd">interface</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nf">Uint64</span><span class="p">()</span> <span class="kt">uint64</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></blockquote>
<figcaption><div>via <q><a href="https://pkg.go.dev/math/rand/v2">math/rand/v2/rand.go</a></q></div></figcaption>
</figure>
<p>どえらシンプル! <code>Seed()</code> メソッドがなくなったのは大きいね。
これによって <code>runtimeSource</code> やトップレベル関数群が参照する <code>globalRand</code> の定義もめっさシンプルになった。</p>
<figure lang="en">
<blockquote class="nobox"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// globalRand is the source of random numbers for the top-level
</span></span></span><span class="line"><span class="cl"><span class="c1">// convenience functions.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">globalRand</span> <span class="p">=</span> <span class="o">&</span><span class="nx">Rand</span><span class="p">{</span><span class="nx">src</span><span class="p">:</span> <span class="o">&</span><span class="nx">runtimeSource</span><span class="p">{}}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">//go:linkname runtime_rand runtime.rand
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">runtime_rand</span><span class="p">()</span> <span class="kt">uint64</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// runtimeSource is a Source that uses the runtime fastrand functions.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">runtimeSource</span> <span class="kd">struct</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="o">*</span><span class="nx">runtimeSource</span><span class="p">)</span> <span class="nf">Uint64</span><span class="p">()</span> <span class="kt">uint64</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nf">runtime_rand</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></blockquote>
<figcaption><div>via <q><a href="https://pkg.go.dev/math/rand/v2">math/rand/v2/rand.go</a></q></div></figcaption>
</figure>
<p>うんうん。
シンプルが一番だね。</p>
<h2>ChaCha8 を rand.Source にする</h2>
<p>ChaCha8 疑似乱数生成器を疑似乱数の <code>Source</code> として明示的に組み込む場合は, <a href="https://pkg.go.dev/math/rand/v2" title="rand package - math/rand/v2 - Go Packages"><code>rand</code></a><code>.NewChaCha8()</code> 関数を使って生成する。</p>
<figure lang="en">
<blockquote class="nobox"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">"internal/chacha8rand"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// A ChaCha8 is a ChaCha8-based cryptographically strong
</span></span></span><span class="line"><span class="cl"><span class="c1">// random number generator.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">ChaCha8</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">state</span> <span class="nx">chacha8rand</span><span class="p">.</span><span class="nx">State</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// NewChaCha8 returns a new ChaCha8 seeded with the given seed.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">NewChaCha8</span><span class="p">(</span><span class="nx">seed</span> <span class="p">[</span><span class="mi">32</span><span class="p">]</span><span class="kt">byte</span><span class="p">)</span> <span class="o">*</span><span class="nx">ChaCha8</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c</span> <span class="o">:=</span> <span class="nb">new</span><span class="p">(</span><span class="nx">ChaCha8</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nf">Init</span><span class="p">(</span><span class="nx">seed</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">c</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Seed resets the ChaCha8 to behave the same way as NewChaCha8(seed).
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">c</span> <span class="o">*</span><span class="nx">ChaCha8</span><span class="p">)</span> <span class="nf">Seed</span><span class="p">(</span><span class="nx">seed</span> <span class="p">[</span><span class="mi">32</span><span class="p">]</span><span class="kt">byte</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nf">Init</span><span class="p">(</span><span class="nx">seed</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Uint64 returns a uniformly distributed random uint64 value.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">c</span> <span class="o">*</span><span class="nx">ChaCha8</span><span class="p">)</span> <span class="nf">Uint64</span><span class="p">()</span> <span class="kt">uint64</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">x</span><span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nx">c</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nf">Next</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">ok</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">x</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nf">Refill</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></blockquote>
<figcaption><div>via <q><a href="https://pkg.go.dev/math/rand/v2">math/rand/v2/chacha8.go</a></q></div></figcaption>
</figure>
<p>ランタイムに組み込まれているものと違って,こちらは排他処理を行っていない。
並行的に安全(concurrency safe)ではないわけだ。
なので平行処理下で <a href="https://pkg.go.dev/math/rand/v2" title="rand package - math/rand/v2 - Go Packages"><code>math/rand/v2</code></a> の ChaCha8 を扱う場合は要注意である。
つか,平行処理下で ChaCha8 疑似乱数生成器を使うならトップレベル関数群を使うべきだろう。</p>
<h2>PCG を rand.Source にする</h2>
<p><a href="https://pkg.go.dev/math/rand/v2" title="rand package - math/rand/v2 - Go Packages"><code>math/rand/v2</code></a> にはもうひとつ疑似乱数生成器が用意されている。
PCG (Permuted Congruential Generator) というそうな。</p>
<figure lang="en">
<blockquote class="nobox"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// A PCG is a PCG generator with 128 bits of internal state.
</span></span></span><span class="line"><span class="cl"><span class="c1">// A zero PCG is equivalent to NewPCG(0, 0).
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">PCG</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">hi</span> <span class="kt">uint64</span>
</span></span><span class="line"><span class="cl"> <span class="nx">lo</span> <span class="kt">uint64</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// NewPCG returns a new PCG seeded with the given values.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">NewPCG</span><span class="p">(</span><span class="nx">seed1</span><span class="p">,</span> <span class="nx">seed2</span> <span class="kt">uint64</span><span class="p">)</span> <span class="o">*</span><span class="nx">PCG</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="o">&</span><span class="nx">PCG</span><span class="p">{</span><span class="nx">seed1</span><span class="p">,</span> <span class="nx">seed2</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></blockquote>
<figcaption><div>via <q><a href="https://pkg.go.dev/math/rand/v2">math/rand/v2/pcg.go</a></q></div></figcaption>
</figure>
<p>PCG は線形合同法(LCG)のバリエーションなんだそうで, LCG の統計学上の欠点を改善したものらしい。
2014年に発表された比較的新しいアルゴリズムのようだが,今のところは欠点のようなものは特に指摘されてないとか。
当然ながら暗号技術分野では使えない。</p>
<ul>
<li><a href="http://www.pcg-random.org/index.html">PCG, A Family of Better Random Number Generators | PCG, A Better Random Number Generator</a></li>
<li><a href="https://en.wikipedia.org/wiki/Permuted_congruential_generator">Permuted congruential generator - Wikipedia</a></li>
<li><a href="https://ja.wikipedia.org/wiki/Permuted_congruential_generator">Permuted congruential generator - Wikipedia</a> : 日本語</li>
</ul>
<p>こちらも並行的に安全ではないのでご注意を。</p>
<h2>これから math/rand を使う理由はないかな</h2>
<p>今までは <a href="https://pkg.go.dev/math/rand" title="rand package - math/rand - Go Packages"><code>math/rand</code></a> で用意されている疑似乱数生成器は暗号技術的にセキュアではない(要件である「予測困難性」を満たさない)ため使いどころを考えなければならなかったが <a href="https://go.dev/">Go</a> 1.22 から <a href="https://pkg.go.dev/math/rand" title="rand package - math/rand - Go Packages"><code>math/rand</code></a>, <a href="https://pkg.go.dev/math/rand/v2" title="rand package - math/rand/v2 - Go Packages"><code>math/rand/v2</code></a> ともに ChaCha8 が既定の疑似乱数生成器なったため用途を選ばずカジュアルに使えるようになるだろう。
そうなると,これから疑似乱数生成器を使おうというとききに,わざわざ <a href="https://pkg.go.dev/math/rand" title="rand package - math/rand - Go Packages"><code>math/rand</code></a> を使う理由はないかな。
<a href="https://pkg.go.dev/math/rand" title="rand package - math/rand - Go Packages"><code>math/rand</code></a> って無駄に複雑になってる感じだもんなぁ。</p>
<p>ちなみに <a href="https://pkg.go.dev/math/rand/v2" title="rand package - math/rand/v2 - Go Packages"><code>math/rand/v2</code></a> では <code>Read()</code> 関数もなくなっているが, <a href="https://pkg.go.dev/math/rand" title="rand package - math/rand - Go Packages"><code>math/rand</code></a> でも <a href="https://pkg.go.dev/math/rand" title="rand package - math/rand - Go Packages"><code>rand</code></a><code>.Read()</code> 関数は Deprecated になってるし,<a href="https://go.dev/doc/go1.22#math_rand_v2" title="Go 1.22 Release Notes - The Go Programming Language">リリースノート</a>を見ると, <code>Read()</code> 関数が使いたきゃ <a href="https://pkg.go.dev/crypto/rand" title="rand package - crypto/rand - Go Packages"><code>crypto/rand</code></a> パッケージを使え,みたいなことが書いてあるので,まぁそういうことなんだろう。</p>
<h2>ブックマーク</h2>
<ul>
<li><a href="https://go.dev/doc/go1.22">Go 1.22 Release Notes - The Go Programming Language</a></li>
<li><a href="https://tools.ietf.org/html/rfc4086">RFC 4086 - Randomness Requirements for Security</a> (<a href="https://www.nic.ad.jp/ja/tech/ipa/RFC4086JA.html" title="セキュリティのための乱雑性についての要件">日本語訳</a>)</li>
<li><a href="https://text.baldanders.info/golang/pseudo-random-number-generator/">Go の疑似乱数生成器は並行的に安全ではないらしい(追記あり)</a></li>
<li><a href="https://zenn.dev/spiegel/articles/20240309-golang-math-rand-v2">Go 1.22 の math/rand/v2 を使ってみる</a></li>
</ul>
<h2>参考図書</h2>
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/416Stewy0NS._SL160_.jpg" width="123" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">プログラミング言語Go</a></dt>
<dd>アラン・ドノバン (著), ブライアン・カーニハン (著), 柴田芳樹 (著)</dd>
<dd>丸善出版 2016-06-20 (Release 2021-07-13)</dd>
<dd>Kindle版</dd>
<dd>B099928SJD (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">Kindle 版出た! 一部内容が古びてしまったが,この本は Go 言語の教科書と言ってもいいだろう。感想は<a href="https://text.baldanders.info/remark/2016/07/go-programming-language/" >こちら</a>。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2021-05-22">2021-05-22</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- プログラミング言語Go -->
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B0CFL1DK8Q?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/51BmDUG6D0L._SL160_.jpg" width="125" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B0CFL1DK8Q?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">Go言語 100Tips ありがちなミスを把握し、実装を最適化する impress top gearシリーズ</a></dt>
<dd>Teiva Harsanyi (著), 柴田 芳樹 (著)</dd>
<dd>インプレス 2023-08-18 (Release 2023-08-18)</dd>
<dd>Kindle版</dd>
<dd>B0CFL1DK8Q (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="4"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="far fa-star"></i></abbr></dd>
</dl>
<p class="description"><a href="https://book.impress.co.jp/books/1122101133">版元</a>で PDF 版を購入可能。事実上の Effective Go とも言える充実の内容。オリジナルは敢えてタイトルに “tips” という単語を入れるのを避けたのに邦題が「100 Tips」とかなっていて,原作者がお怒りとの噂(あくまで噂)</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2023-08-18">2023-08-18</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- Go言語 100Tips -->
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/4873118468?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/51pUKQajnaL._SL160_.jpg" width="125" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/4873118468?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">Go言語による並行処理</a></dt>
<dd>Katherine Cox-Buday (著), 山口 能迪 (翻訳)</dd>
<dd>オライリージャパン 2018-10-26</dd>
<dd>単行本(ソフトカバー)</dd>
<dd>4873118468 (ASIN), 9784873118468 (EAN), 4873118468 (ISBN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description"><a href="https://www.oreilly.co.jp/books/9784873118468/">Eブック版もある</a>。感想は<a href="https://text.baldanders.info/remark/2018/11/concurrency-in-go/">こちら</a>。 Go 言語で並行処理を書くならこの本は必読書になるだろう。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2020-01-13">2020-01-13</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- Go言語による並行処理 -->
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/4814400535?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/41+ew2wl2jL._SL160_.jpg" width="125" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/4814400535?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">効率的なGo ―データ指向によるGoアプリケーションの性能最適化</a></dt>
<dd>Bartłomiej Płotka (著), 山口 能迪 (翻訳)</dd>
<dd>オライリー・ジャパン 2024-02-24</dd>
<dd>単行本(ソフトカバー)</dd>
<dd>4814400535 (ASIN), 9784814400539 (EAN), 4814400535 (ISBN)</dd>
</dl>
<p class="description">ついに Effective Go のタイトルを冠した本の邦訳が出た。<a href="https://www.oreilly.co.jp/books/9784814400539/">版元</a>で Ebook を買える。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2024-02-22">2024-02-22</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- 効率的なGo : Effective Go -->
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B015643CPE?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/51t6yHHVwEL._SL160_.jpg" width="113" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B015643CPE?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">暗号技術入門 第3版 秘密の国のアリス</a></dt>
<dd>結城 浩 (著)</dd>
<dd>SBクリエイティブ 2015-08-25 (Release 2015-09-17)</dd>
<dd>Kindle版</dd>
<dd>B015643CPE (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">SHA-3 や Bitcoin/Blockchain など新しい知見や技術要素を大幅追加。暗号技術を使うだけならこれ1冊でとりあえず無問題。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2015-09-20">2015-09-20</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- 暗号技術入門 第3版 -->
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B00I8ETG96?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/51bXS7crIjL._SL160_.jpg" width="117" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B00I8ETG96?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">赤ずきんチャチャ 1 (りぼんマスコットコミックスDIGITAL)</a></dt>
<dd>彩花みん (著)</dd>
<dd>集英社 1993-02-15 (Release 2014-02-25)</dd>
<dd>Kindle版</dd>
<dd>B00I8ETG96 (ASIN)</dd>
</dl>
<p class="description">そういえばコミックは読んでないかな。「リボン」は読まなかったからなぁ...</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2024-03-07">2024-03-07</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- 赤ずきんチャチャ -->
ランダムな文字列を生成する
tag:text.Baldanders.info,2020-10-16:/golang/random-string/
2020-10-16T05:24:30+00:00
2021-12-04T02:40:05+00:00
というわけで math/rand と crypto/rand はトレードオフの関係にある。
Spiegel
https://baldanders.info/profile/
<p>疑似乱数に関して面白い記事を見つけたので紹介しつつ,こちらでも試してみる。</p>
<ul>
<li><a href="https://zenn.dev/najeira/articles/2017-12-28-qiita-5974c6b2c59ecc02fad2">Go言語でランダムな文字列を作ってみる</a></li>
<li><a href="https://github.com/najeira/randstr">najeira/randstr: Generate random string using crypto/rand and math/rand for Go</a></li>
</ul>
<p>お題はこんな感じ:</p>
<ul>
<li>英数字62文字(<code>a-zA-Z0-9</code>)からランダムに1文字ずつとって指定の長さの文字列を作成する</li>
<li>同じ文字を何度使ってもよい</li>
</ul>
<p>また,この記事における前提として,以下の interface 型および定数が定義済みであるとする。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">randstr</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Random</span> <span class="kd">interface</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nf">String</span><span class="p">(</span><span class="kt">int</span><span class="p">)</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">const</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="nx">letterBytes</span> <span class="p">=</span> <span class="s">"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"</span>
</span></span><span class="line"><span class="cl"> <span class="nx">letterBytesLen</span> <span class="p">=</span> <span class="nb">len</span><span class="p">(</span><span class="nx">letterBytes</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">letterIdxBits</span> <span class="p">=</span> <span class="mi">6</span> <span class="c1">// 6 bits to represent a letter index
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">letterIdxMask</span> <span class="p">=</span> <span class="mi">1</span><span class="o"><<</span><span class="nx">letterIdxBits</span> <span class="o">-</span> <span class="mi">1</span> <span class="c1">// All 1-bits, as many as letterIdxBits
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">letterIdxMax</span> <span class="p">=</span> <span class="mi">63</span> <span class="o">/</span> <span class="nx">letterIdxBits</span> <span class="c1">// # of letter indices fitting in 63 bits
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">)</span>
</span></span></code></pre></div><p>じゃあ,いってみようか</p>
<h2><a href="https://golang.org/pkg/math/rand/" title="rand - The Go Programming Language">math/rand</a> パッケージによる実装</h2>
<p>まずは <a href="https://golang.org/pkg/math/rand/" title="rand - The Go Programming Language"><code>math/rand</code></a> パッケージを使った実装から。
これについては以下のページの議論が参考になる。</p>
<ul>
<li><a href="https://stackoverflow.com/questions/22892120/how-to-generate-a-random-string-of-a-fixed-length-in-go">How to generate a random string of a fixed length in Go? - Stack Overflow</a></li>
</ul>
<p>これを参照しつつ,過程をすっ飛ばして最終的に以下のコードにしてみた。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">randstr</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"math/rand"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"unsafe"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">MathRandom</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">src</span> <span class="nx">rand</span><span class="p">.</span><span class="nx">Source</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">NewMathRandom</span><span class="p">(</span><span class="nx">seed</span> <span class="kt">int64</span><span class="p">)</span> <span class="nx">Random</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="o">&</span><span class="nx">MathRandom</span><span class="p">{</span><span class="nx">src</span><span class="p">:</span> <span class="nx">rand</span><span class="p">.</span><span class="nf">NewSource</span><span class="p">(</span><span class="nx">seed</span><span class="p">)}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">mr</span> <span class="o">*</span><span class="nx">MathRandom</span><span class="p">)</span> <span class="nf">String</span><span class="p">(</span><span class="nx">len</span> <span class="kt">int</span><span class="p">)</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="nx">len</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span><span class="p">,</span> <span class="nx">cache</span><span class="p">,</span> <span class="nx">rest</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">mr</span><span class="p">.</span><span class="nx">src</span><span class="p">.</span><span class="nf">Int63</span><span class="p">(),</span> <span class="nx">letterIdxMax</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">len</span><span class="p">;</span> <span class="nx">rest</span><span class="o">--</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">rest</span> <span class="o"><=</span> <span class="mi">0</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">cache</span><span class="p">,</span> <span class="nx">rest</span> <span class="p">=</span> <span class="nx">mr</span><span class="p">.</span><span class="nx">src</span><span class="p">.</span><span class="nf">Int63</span><span class="p">(),</span> <span class="nx">letterIdxMax</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">idx</span> <span class="o">:=</span> <span class="nb">int</span><span class="p">(</span><span class="nx">cache</span> <span class="o">&</span> <span class="nx">letterIdxMask</span><span class="p">);</span> <span class="nx">idx</span> <span class="p"><</span> <span class="nx">letterBytesLen</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="p">=</span> <span class="nx">letterBytes</span><span class="p">[</span><span class="nx">idx</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">i</span><span class="o">++</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">cache</span> <span class="o">>>=</span> <span class="nx">letterIdxBits</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="o">*</span><span class="p">(</span><span class="o">*</span><span class="kt">string</span><span class="p">)(</span><span class="nx">unsafe</span><span class="p">.</span><span class="nf">Pointer</span><span class="p">(</span><span class="o">&</span><span class="nx">b</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>Int63()</code> メソッドで63ビット分の乱数を取って6ビットずつ切り出して使うイメージ。
ただし参照する <code>letterBytes</code> が62文字分なのに対し6ビット整数では <code>0-63</code> まであるので,値がはみ出る場合は取得した6ビット乱数を捨てている。
<code>letterBytes</code> に適当な記号を2文字足してやればロスは無くなるだろうが,お題から外れるので今回は割愛する。</p>
<p>最後の</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="k">return</span> <span class="o">*</span><span class="p">(</span><span class="o">*</span><span class="kt">string</span><span class="p">)(</span><span class="nx">unsafe</span><span class="p">.</span><span class="nf">Pointer</span><span class="p">(</span><span class="o">&</span><span class="nx">b</span><span class="p">))</span>
</span></span></code></pre></div><p>は少々トリッキーだが <code>[]byte</code> インスタンスをコピーなしに <code>string</code> 型にキャストするための「<ruby><rb>お呪い</rb><rp> (</rp><rt>おまじない</rt><rp>) </rp></ruby>」だと思えばいい<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>。</p>
<p>文字通り <a href="https://golang.org/pkg/unsafe/" title="unsafe - The Go Programming Language"><code>unsafe</code></a> な操作なので,乱用して「<ruby><rb>呪い</rb><rp> (</rp><rt>のろい</rt><rp>) </rp></ruby>」にならないようご注意を(笑)</p>
<h2><a href="https://golang.org/pkg/crypto/rand/" title="rand - The Go Programming Language">crypto/rand</a> パッケージによる実装</h2>
<p>次は <a href="https://golang.org/pkg/crypto/rand/" title="rand - The Go Programming Language"><code>crypto/rand</code></a> パッケージを使った実装。</p>
<p><a href="https://golang.org/pkg/crypto/rand/" title="rand - The Go Programming Language"><code>crypto/rand</code></a> パッケージでは乱数の生成に専用デバイスを使う。</p>
<figure lang="en">
<blockquote><q>On Linux and FreeBSD, Reader uses <code>getrandom(2)</code> if available, <code>/dev/urandom</code> otherwise. On OpenBSD, Reader uses <code>getentropy(2)</code>. On other Unix-like systems, Reader reads from <code>/dev/urandom</code>. On Windows systems, Reader uses the <code>CryptGenRandom</code> API. On Wasm, Reader uses the Web Crypto API</q>.</blockquote>
<figcaption><div>via <q><a href="https://golang.org/pkg/crypto/rand/">rand - The Go Programming Language</a></q></div></figcaption>
</figure>
<p>そのため <a href="https://golang.org/pkg/math/rand/" title="rand - The Go Programming Language"><code>math/rand</code></a> と比べてどうしても処理速度が遅くなる。
したがって <a href="https://golang.org/pkg/crypto/rand/" title="rand - The Go Programming Language"><code>rand</code></a><code>.Read()</code> 関数の呼び出し回数を抑えるよう実装するのがコツである。</p>
<p>最初に挙げた記事を参考にしつつ,こんな感じでどうだろう。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">randstr</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"crypto/rand"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"unsafe"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">CryptoRandom</span> <span class="kd">struct</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">NewCryptoRandom</span><span class="p">()</span> <span class="nx">Random</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="o">&</span><span class="nx">CryptoRandom</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">cr</span> <span class="o">*</span><span class="nx">CryptoRandom</span><span class="p">)</span> <span class="nf">String</span><span class="p">(</span><span class="nx">len</span> <span class="kt">int</span><span class="p">)</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="nx">len</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span><span class="p">,</span> <span class="nx">offset</span><span class="p">,</span> <span class="nx">size</span><span class="p">,</span> <span class="nx">rest</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">len</span><span class="p">;</span> <span class="nx">rest</span><span class="o">--</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">//fmt.Println(i, offset, size, rest)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="nx">rest</span> <span class="o"><=</span> <span class="mi">0</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">offset</span> <span class="p">=</span> <span class="nx">i</span>
</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">err</span> <span class="kt">error</span>
</span></span><span class="line"><span class="cl"> <span class="nx">size</span><span class="p">,</span> <span class="nx">err</span> <span class="p">=</span> <span class="nx">rand</span><span class="p">.</span><span class="nf">Read</span><span class="p">(</span><span class="nx">b</span><span class="p">[</span><span class="nx">offset</span><span class="p">:])</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="o">||</span> <span class="nx">size</span> <span class="o"><=</span> <span class="mi">0</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="s">""</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rest</span> <span class="p">=</span> <span class="nx">size</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">idx</span> <span class="o">:=</span> <span class="nb">int</span><span class="p">(</span><span class="nx">b</span><span class="p">[</span><span class="nx">offset</span><span class="o">+</span><span class="p">(</span><span class="nx">size</span><span class="o">-</span><span class="nx">rest</span><span class="p">)]</span> <span class="o">&</span> <span class="nx">letterIdxMask</span><span class="p">);</span> <span class="nx">idx</span> <span class="p"><</span> <span class="nx">letterBytesLen</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="p">=</span> <span class="nx">letterBytes</span><span class="p">[</span><span class="nx">idx</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">i</span><span class="o">++</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="o">*</span><span class="p">(</span><span class="o">*</span><span class="kt">string</span><span class="p">)(</span><span class="nx">unsafe</span><span class="p">.</span><span class="nf">Pointer</span><span class="p">(</span><span class="o">&</span><span class="nx">b</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>インタフェースを合わせるためにエラーハンドリングをサボっているが,ご容赦を。</p>
<p><a href="https://golang.org/pkg/crypto/rand/" title="rand - The Go Programming Language"><code>rand</code></a><code>.Read()</code> 関数で乱数をいったんバッファに展開し,その後文字に置き換えていく。
ただし <code>letterBytes</code> からはみ出る場合はその値を捨てて,捨てた分をまとめて <a href="https://golang.org/pkg/crypto/rand/" title="rand - The Go Programming Language"><code>rand</code></a><code>.Read()</code> 関数で再取得する,という動作を繰り返している。</p>
<h2>ベンチマークをとってみる</h2>
<p>んじゃあ,これらのコードを使ってベンチマークをとってみよう。
こんなテスト・コードでどうだろう。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">randstr_test</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"randstr"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"testing"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"time"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">const</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="nx">len64</span> <span class="p">=</span> <span class="mi">64</span>
</span></span><span class="line"><span class="cl"> <span class="nx">len128</span> <span class="p">=</span> <span class="mi">128</span>
</span></span><span class="line"><span class="cl"> <span class="nx">max512</span> <span class="p">=</span> <span class="nx">len64</span> <span class="o">*</span> <span class="mi">8</span>
</span></span><span class="line"><span class="cl"> <span class="nx">max1024</span> <span class="p">=</span> <span class="nx">len128</span> <span class="o">*</span> <span class="mi">8</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkRandomStringMath64t8</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">r</span> <span class="o">:=</span> <span class="nx">randstr</span><span class="p">.</span><span class="nf">NewMathRandom</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">().</span><span class="nf">UnixNano</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span><span class="p">.</span><span class="nf">ResetTimer</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">n</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">n</span> <span class="p"><</span> <span class="nx">max512</span><span class="o">/</span><span class="nx">len64</span><span class="p">;</span> <span class="nx">n</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">_</span> <span class="p">=</span> <span class="nx">r</span><span class="p">.</span><span class="nf">String</span><span class="p">(</span><span class="nx">len64</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkRandomStringMath512</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">r</span> <span class="o">:=</span> <span class="nx">randstr</span><span class="p">.</span><span class="nf">NewMathRandom</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">().</span><span class="nf">UnixNano</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span><span class="p">.</span><span class="nf">ResetTimer</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">_</span> <span class="p">=</span> <span class="nx">r</span><span class="p">.</span><span class="nf">String</span><span class="p">(</span><span class="nx">max512</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkRandomStringMath128t8</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">r</span> <span class="o">:=</span> <span class="nx">randstr</span><span class="p">.</span><span class="nf">NewMathRandom</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">().</span><span class="nf">UnixNano</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span><span class="p">.</span><span class="nf">ResetTimer</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">n</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">n</span> <span class="p"><</span> <span class="nx">max1024</span><span class="o">/</span><span class="nx">len128</span><span class="p">;</span> <span class="nx">n</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">_</span> <span class="p">=</span> <span class="nx">r</span><span class="p">.</span><span class="nf">String</span><span class="p">(</span><span class="nx">len128</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkRandomStringMath1024</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">r</span> <span class="o">:=</span> <span class="nx">randstr</span><span class="p">.</span><span class="nf">NewMathRandom</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">().</span><span class="nf">UnixNano</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span><span class="p">.</span><span class="nf">ResetTimer</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">_</span> <span class="p">=</span> <span class="nx">r</span><span class="p">.</span><span class="nf">String</span><span class="p">(</span><span class="nx">max1024</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkRandomStringCrypto64t8</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">r</span> <span class="o">:=</span> <span class="nx">randstr</span><span class="p">.</span><span class="nf">NewCryptoRandom</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span><span class="p">.</span><span class="nf">ResetTimer</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">n</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">n</span> <span class="p"><</span> <span class="nx">max512</span><span class="o">/</span><span class="nx">len64</span><span class="p">;</span> <span class="nx">n</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">_</span> <span class="p">=</span> <span class="nx">r</span><span class="p">.</span><span class="nf">String</span><span class="p">(</span><span class="nx">len64</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkRandomStringCrypto512</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">r</span> <span class="o">:=</span> <span class="nx">randstr</span><span class="p">.</span><span class="nf">NewCryptoRandom</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span><span class="p">.</span><span class="nf">ResetTimer</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">_</span> <span class="p">=</span> <span class="nx">r</span><span class="p">.</span><span class="nf">String</span><span class="p">(</span><span class="nx">max512</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkRandomStringCrypto128t8</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">r</span> <span class="o">:=</span> <span class="nx">randstr</span><span class="p">.</span><span class="nf">NewCryptoRandom</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span><span class="p">.</span><span class="nf">ResetTimer</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">n</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">n</span> <span class="p"><</span> <span class="nx">max1024</span><span class="o">/</span><span class="nx">len128</span><span class="p">;</span> <span class="nx">n</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">_</span> <span class="p">=</span> <span class="nx">r</span><span class="p">.</span><span class="nf">String</span><span class="p">(</span><span class="nx">len128</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkRandomStringCrypto1024</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">r</span> <span class="o">:=</span> <span class="nx">randstr</span><span class="p">.</span><span class="nf">NewCryptoRandom</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span><span class="p">.</span><span class="nf">ResetTimer</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">_</span> <span class="p">=</span> <span class="nx">r</span><span class="p">.</span><span class="nf">String</span><span class="p">(</span><span class="nx">max1024</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>処理内容を表にするとこんな感じ。</p>
<table>
<thead>
<tr>
<th>テスト名</th>
<th>使用パッケージ</th>
<th>処理内容</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>BenchmarkRandomStringMath64t8</code></td>
<td><a href="https://golang.org/pkg/math/rand/" title="rand - The Go Programming Language"><code>math/rand</code></a></td>
<td>64バイト文字列生成×8</td>
</tr>
<tr>
<td><code>BenchmarkRandomStringMath512</code></td>
<td><a href="https://golang.org/pkg/math/rand/" title="rand - The Go Programming Language"><code>math/rand</code></a></td>
<td>512バイト文字列生成</td>
</tr>
<tr>
<td><code>BenchmarkRandomStringMath128t8</code></td>
<td><a href="https://golang.org/pkg/math/rand/" title="rand - The Go Programming Language"><code>math/rand</code></a></td>
<td>128バイト文字列生成×8</td>
</tr>
<tr>
<td><code>BenchmarkRandomStringMath1024</code></td>
<td><a href="https://golang.org/pkg/math/rand/" title="rand - The Go Programming Language"><code>math/rand</code></a></td>
<td>1,024バイト文字列生成</td>
</tr>
<tr>
<td><code>BenchmarkRandomStringCrypto64t8</code></td>
<td><a href="https://golang.org/pkg/crypto/rand/" title="rand - The Go Programming Language"><code>crypto/rand</code></a></td>
<td>64バイト文字列生成×8</td>
</tr>
<tr>
<td><code>BenchmarkRandomStringCrypto512</code></td>
<td><a href="https://golang.org/pkg/crypto/rand/" title="rand - The Go Programming Language"><code>crypto/rand</code></a></td>
<td>512バイト文字列生成</td>
</tr>
<tr>
<td><code>BenchmarkRandomStringCrypto128t8</code></td>
<td><a href="https://golang.org/pkg/crypto/rand/" title="rand - The Go Programming Language"><code>crypto/rand</code></a></td>
<td>128バイト文字列生成×8</td>
</tr>
<tr>
<td><code>BenchmarkRandomStringCrypto1024</code></td>
<td><a href="https://golang.org/pkg/crypto/rand/" title="rand - The Go Programming Language"><code>crypto/rand</code></a></td>
<td>1,024バイト文字列生成</td>
</tr>
</tbody>
</table>
<p>では実際に動かしてみよう。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go test -bench RandomString -benchmem
</span></span><span class="line"><span class="cl">goos: linux
</span></span><span class="line"><span class="cl">goarch: amd64
</span></span><span class="line"><span class="cl">pkg: randstr
</span></span><span class="line"><span class="cl">BenchmarkRandomStringMath64t8-4 641556 1616 ns/op 512 B/op 8 allocs/op
</span></span><span class="line"><span class="cl">BenchmarkRandomStringMath512-4 899421 1386 ns/op 512 B/op 1 allocs/op
</span></span><span class="line"><span class="cl">BenchmarkRandomStringMath128t8-4 357760 3093 ns/op 1024 B/op 8 allocs/op
</span></span><span class="line"><span class="cl">BenchmarkRandomStringMath1024-4 407550 2820 ns/op 1024 B/op 1 allocs/op
</span></span><span class="line"><span class="cl">BenchmarkRandomStringCrypto64t8-4 81285 14320 ns/op 512 B/op 8 allocs/op
</span></span><span class="line"><span class="cl">BenchmarkRandomStringCrypto512-4 241180 4827 ns/op 512 B/op 1 allocs/op
</span></span><span class="line"><span class="cl">BenchmarkRandomStringCrypto128t8-4 64815 18358 ns/op 1024 B/op 8 allocs/op
</span></span><span class="line"><span class="cl">BenchmarkRandomStringCrypto1024-4 149160 8212 ns/op 1024 B/op 1 allocs/op
</span></span><span class="line"><span class="cl">PASS
</span></span><span class="line"><span class="cl">ok randstr 10.851s
</span></span></code></pre></div><p>これも表にまとめてみる。
処理回数でソートしているのでご注意を。</p>
<table>
<thead>
<tr>
<th style="text-align:right">使用パッケージ</th>
<th style="text-align:left">処理内容</th>
<th style="text-align:right">ns/op</th>
<th style="text-align:right">Alloc<br>Size</th>
<th style="text-align:right">Alloc<br>Count</th>
<th style="text-align:right">Ratio</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:right"><a href="https://golang.org/pkg/math/rand/" title="rand - The Go Programming Language"><code>math/rand</code></a></td>
<td style="text-align:left">64バイト文字列生成×8</td>
<td style="text-align:right">1,616</td>
<td style="text-align:right">512</td>
<td style="text-align:right">8</td>
<td style="text-align:right">1.0</td>
</tr>
<tr>
<td style="text-align:right"><a href="https://golang.org/pkg/math/rand/" title="rand - The Go Programming Language"><code>math/rand</code></a></td>
<td style="text-align:left">128バイト文字列生成×8</td>
<td style="text-align:right">3,093</td>
<td style="text-align:right">1024</td>
<td style="text-align:right">8</td>
<td style="text-align:right">1.9</td>
</tr>
<tr>
<td style="text-align:right"><a href="https://golang.org/pkg/crypto/rand/" title="rand - The Go Programming Language"><code>crypto/rand</code></a></td>
<td style="text-align:left">64バイト文字列生成×8</td>
<td style="text-align:right">14,329</td>
<td style="text-align:right">512</td>
<td style="text-align:right">8</td>
<td style="text-align:right">8.9</td>
</tr>
<tr>
<td style="text-align:right"><a href="https://golang.org/pkg/crypto/rand/" title="rand - The Go Programming Language"><code>crypto/rand</code></a></td>
<td style="text-align:left">128バイト文字列生成×8</td>
<td style="text-align:right">18,358</td>
<td style="text-align:right">1024</td>
<td style="text-align:right">8</td>
<td style="text-align:right">11.3</td>
</tr>
<tr>
<td style="text-align:right"></td>
<td style="text-align:left"></td>
<td style="text-align:right"></td>
<td style="text-align:right"></td>
<td style="text-align:right"></td>
<td style="text-align:right"></td>
</tr>
<tr>
<td style="text-align:right"><a href="https://golang.org/pkg/math/rand/" title="rand - The Go Programming Language"><code>math/rand</code></a></td>
<td style="text-align:left">512バイト文字列生成</td>
<td style="text-align:right">1,386</td>
<td style="text-align:right">512</td>
<td style="text-align:right">1</td>
<td style="text-align:right">1.0</td>
</tr>
<tr>
<td style="text-align:right"><a href="https://golang.org/pkg/math/rand/" title="rand - The Go Programming Language"><code>math/rand</code></a></td>
<td style="text-align:left">1,024バイト文字列生成</td>
<td style="text-align:right">2,820</td>
<td style="text-align:right">1024</td>
<td style="text-align:right">1</td>
<td style="text-align:right">2.0</td>
</tr>
<tr>
<td style="text-align:right"><a href="https://golang.org/pkg/crypto/rand/" title="rand - The Go Programming Language"><code>crypto/rand</code></a></td>
<td style="text-align:left">512バイト文字列生成</td>
<td style="text-align:right">4,827</td>
<td style="text-align:right">512</td>
<td style="text-align:right">1</td>
<td style="text-align:right">3.5</td>
</tr>
<tr>
<td style="text-align:right"><a href="https://golang.org/pkg/crypto/rand/" title="rand - The Go Programming Language"><code>crypto/rand</code></a></td>
<td style="text-align:left">1,024バイト文字列生成</td>
<td style="text-align:right">8,212</td>
<td style="text-align:right">1024</td>
<td style="text-align:right">1</td>
<td style="text-align:right">5.9</td>
</tr>
</tbody>
</table>
<p><a href="https://golang.org/pkg/math/rand/" title="rand - The Go Programming Language"><code>math/rand</code></a> パッケージを使った実装は分かりやすい。
文字列が長くなると処理時間が長くなり,アロケーション回数が多いと更に時間がかかる。</p>
<p><a href="https://golang.org/pkg/crypto/rand/" title="rand - The Go Programming Language"><code>crypto/rand</code></a> パッケージについては,やはりメソッドの呼び出し回数がボトルネックになっているようだ。
文字列の長さやアロケーション回数の影響を大きく上回っている。</p>
<div class="box"><p><a href="https://golang.org/pkg/math/rand/" title="rand - The Go Programming Language"><code>math/rand</code></a> パッケージを使った実装でも <code>Read()</code> メソッドでまとめて乱数を取得したほうが速くなるんじゃね? って思うよね。
私も思った。
ので,実際に試してベンチマークもとったのだが, <code>Int63()</code> メソッドで63ビットずつ取るほうが速いのよ,これが。</p>
<p>まぁ,中身を見れば分かるが,<a href="https://golang.org/pkg/math/rand/" title="rand - The Go Programming Language"><code>math/rand</code></a> パッケージの <code>Read()</code> メソッドは中で <code>Int63()</code> メソッドを呼び出して8ビットずつ切り分けているだけなので,そのオーバヘッド分だけ遅くなってしまうようだ。
残念!</p>
</div>
<h2>科学技術用の疑似乱数生成器と暗号技術用の乱数生成器</h2>
<p>科学技術用の疑似乱数生成器と暗号技術用の乱数生成器では求められる要件が異なる。</p>
<p>科学技術用の疑似乱数生成器で最重要なのは「高次元均等分布」な乱数を生成できることで,その次に重要なのは高速に乱数が生成できることである。</p>
<p>一方,暗号技術用の乱数生成器で最重要なのは「予測困難性」である。</p>
<p>たとえば,科学技術用の疑似乱数生成器の多くは,アルゴリズムで乱数を生成するため,起点となる seed が決まれば生成される値が確定してしまう。
これが科学技術用の疑似乱数生成器が暗号技術には向かないとされる理由だ。</p>
<p>しかし,現時点の技術で「予測困難」な乱数を作るためには何らかの方法で外乱要素(またはエントロピー源)を組み込む必要があるため<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>,どうしても乱数の生成速度が遅くなる。
大量の乱数を必要とする科学技術計算には向かないわけだ。</p>
<p>というわけで <a href="https://golang.org/pkg/math/rand/" title="rand - The Go Programming Language"><code>math/rand</code></a> と <a href="https://golang.org/pkg/crypto/rand/" title="rand - The Go Programming Language"><code>crypto/rand</code></a> はトレードオフの関係にある。
上手く使い分けて欲しい。</p>
<h2>ブックマーク</h2>
<ul>
<li><a href="https://text.baldanders.info/golang/estimate-of-pi-4-prng/">モンテカルロ法による円周率の推定(その4 PRNG)</a></li>
<li><a href="https://text.baldanders.info/golang/pseudo-random-number-generator/">Go の疑似乱数生成器は Goroutine-Safe ではないらしい(追記あり)</a></li>
<li><a href="https://text.baldanders.info/release/mersenne-twister-by-golang/">疑似乱数生成器 spiegel-im-spiegel/mt</a></li>
</ul>
<h2>参考図書</h2>
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/416Stewy0NS._SL160_.jpg" width="123" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">プログラミング言語Go</a></dt>
<dd>アラン・ドノバン (著), ブライアン・カーニハン (著), 柴田芳樹 (著)</dd>
<dd>丸善出版 2016-06-20 (Release 2021-07-13)</dd>
<dd>Kindle版</dd>
<dd>B099928SJD (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">Kindle 版出た! 一部内容が古びてしまったが,この本は Go 言語の教科書と言ってもいいだろう。感想は<a href="https://text.baldanders.info/remark/2016/07/go-programming-language/" >こちら</a>。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2021-05-22">2021-05-22</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- プログラミング言語Go -->
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/4908686033?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/51RKK5+6bpL._SL160_.jpg" width="112" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/4908686033?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">Goならわかるシステムプログラミング</a></dt>
<dd>渋川 よしき (著), ごっちん (イラスト)</dd>
<dd>ラムダノート 2017-10-23</dd>
<dd>単行本(ソフトカバー)</dd>
<dd>4908686033 (ASIN), 9784908686030 (EAN), 4908686033 (ISBN)</dd>
<dd>評価<abbr class="rating fa-sm" title="4"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="far fa-star"></i></abbr></dd>
</dl>
<p class="description"><a href="https://www.lambdanote.com/collections/go/products/go-ebook">PDF 版</a>あり。ファイルやソケットなどに代表される順次アクセスの汎化である io.Reader / io.Writer およびその派生・特化クラス,またプロセスやスレッドに関する解説が秀逸だと思う。さらに Docker コアの libcontainer についても解説がある。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2018-10-19">2018-10-19</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- Goならわかるシステムプログラミング -->
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B07VPSXF6N?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/51jif840ScL._SL160_.jpg" width="113" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B07VPSXF6N?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">改訂2版 みんなのGo言語</a></dt>
<dd>松木 雅幸 (著), mattn (著), 藤原 俊一郎 (著), 中島 大一 (著), 上田 拓也 (著), 牧 大輔 (著), 鈴木 健太 (著)</dd>
<dd>技術評論社 2019-08-01 (Release 2019-08-01)</dd>
<dd>Kindle版</dd>
<dd>B07VPSXF6N (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="4"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="far fa-star"></i></abbr></dd>
</dl>
<p class="description">改訂2版の目玉は7章の「データベースの扱い方」が追加されたことだろう。他の章では,大まかな構成は1版と同じだが細かい部分が変わっていて Go 1.12 への言及まであるのには驚いた。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2019-08-12">2019-08-12</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- 改訂2版 みんなのGo言語 -->
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><code>*(*string)(unsafe.Pointer(&b))</code> なキャストは <a href="https://golang.org/pkg/strings/" title="strings - The Go Programming Language"><code>strings</code></a><code>.Builder</code> の <code>String()</code> メソッドで使われている手法の丸パクリだったりする(笑) <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:2">
<p><code>/dev/urandom</code> はハードウェア・デバイスから十分なエントロピー源が得られない場合は内部で疑似乱数生成器を使用する。このため一時は <code>/dev/urandom</code> の脆弱性が疑われたが,現時点では事実上は問題ないとされている。一方で,スマートデバイスのような場合はハードウェア・デバイスからのエントロピー源だけでは外部から推測され易いため,性能のよい疑似乱数生成器を組み合わせるほうが有効になる場合もあるようだ。 <a href="#fnref:2" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
「今あなた以外に○○人が見ています」デモ
tag:text.Baldanders.info,2019-10-21:/remark/2019/10/javascript-random/
2019-10-21T13:58:57+00:00
2020-01-05T11:59:50+00:00
これ最初に考えた奴は天才だろ(笑)
Spiegel
https://baldanders.info/profile/
<p>ぶちウケた!</p>
<ul>
<li><a href="https://gigazine.net/news/20191021-one-travel-number-rundom/">旅行予約サイトの「今あなた以外に○○人が見ています」はウソだったことが判明 - GIGAZINE</a></li>
</ul>
<p>この記事によると</p>
<figure>
<blockquote><q>JavaScriptで以下のような部分を発見。完全に28から44までの数字がランダムに生成され、それが表示されているだけだったことが判明しました。つまり、One Travelの「○○人がこの搭乗券をチェックしています」という部分は全くのウソであり、ユーザーにフライトの予約を急がせるためのものだったというわけです。</q></blockquote>
<figcaption><div><q><a href="https://gigazine.net/news/20191021-one-travel-number-rundom/">旅行予約サイトの「今あなた以外に○○人が見ています」はウソだったことが判明</a></q>より</div></figcaption>
</figure>
<p>なんだって。</p>
<p>ちうわけで,戯れにデモ・コードを書いてみた。
こんな感じでどうだろう。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p"><</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">'demo'</span> <span class="na">class</span><span class="o">=</span><span class="s">'box'</span><span class="p">></</span><span class="nt">div</span><span class="p">></span>
</span></span><span class="line"><span class="cl"><span class="p"><</span><span class="nt">script</span><span class="p">></span>
</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">rn</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">floor</span><span class="p">(</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">random</span><span class="p">()</span> <span class="o">*</span> <span class="mi">17</span> <span class="p">)</span> <span class="o">+</span> <span class="mi">28</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">p</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s1">'p'</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="nx">p</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nb">document</span><span class="p">.</span><span class="nx">createTextNode</span><span class="p">(</span><span class="s1">'今あなた以外に'</span><span class="o">+</span><span class="nx">rn</span><span class="o">+</span><span class="s1">'人が見ています'</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"> <span class="nx">p</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s1">'class'</span><span class="p">,</span> <span class="s1">'center'</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">'demo'</span><span class="p">).</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">p</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p"></</span><span class="nt">script</span><span class="p">></span>
</span></span></code></pre></div><p>実際に試してみよう。</p>
<figure style='margin:0 auto;text-align:center;'>
<div id='demo' class='box'></div>
<script>
let rn = Math.floor( Math.random() * 17 ) + 28;
let p = document.createElement('p');
p.appendChild(document.createTextNode('今あなた以外に'+rn+'人が見ています'));
p.setAttribute('class', 'center');
document.getElementById('demo').appendChild(p);
</script>
<figcaption><div>「今あなた以外に○○人が見ています」デモ</div></figcaption>
</figure>
<p>おー。
できたできた。</p>
<p>最小の労力で最大の効果。
これ最初に考えた奴は天才だろ(笑)</p>
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/4873115531?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/51HQWyI1aUL._SL160_.jpg" width="113" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/4873115531?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">JavaScriptリファレンス 第6版</a></dt>
<dd>David Flanagan (著), 木下 哲也 (翻訳)</dd>
<dd>オライリージャパン 2012-08-10</dd>
<dd>単行本(ソフトカバー)</dd>
<dd>4873115531 (ASIN), 9784873115535 (EAN), 4873115531 (ISBN)</dd>
<dd>評価<abbr class="rating fa-sm" title="3"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="far fa-star"></i> <i class="far fa-star"></i></abbr></dd>
</dl>
<p class="description">最初に私が JavaScript を勉強したのって,これよりも更に古い版だったんだよなぁ。この版でも今となっては古すぎて使い物にならないけど(笑)</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2019-10-21">2019-10-21</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- JavaScriptリファレンス 第6版 -->
疑似乱数生成器 goark/mt
tag:text.Baldanders.info,2019-09-22:/release/mersenne-twister-by-golang/
2019-09-22T08:37:19+00:00
2024-03-09T11:45:46+00:00
goark/mt は64bit版 Mersenne Twister を元に pure Go で書き直したものである。
Spiegel
https://baldanders.info/profile/
<p><a href="http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/mt.html" title="Mersenne Twister: A random number generator (since 1997/10)">Mersenne Twister</a> とは<a href="http://www.math.sci.hiroshima-u.ac.jp/~m-mat/" title="Makoto Matsumoto Home Page">松本眞</a>・西村拓士両氏によって1996年に発表された擬似乱数生成アルゴリズムである。
他の擬似乱数生成アルゴリズムと比べて以下の特徴があるそうだ。</p>
<figure>
<blockquote><ul>
<li>従来の様々な生成法の欠点を考慮して設計されています</li>
<li>従来にない長周期, 高次元均等分布を持ちます(周期が $2^{19937}-1$ で、623次元超立方体の中に 均等に分布することが証明されています)</li>
<li>生成速度がかなり速い</li>
<li>メモリ効率が良い</li>
</ul>
</blockquote>
<figcaption><div><q><a href="http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/what-is-mt.html">Mersenne Twister とは?</a></q>より</div></figcaption>
</figure>
<p>特に2番目が重要で,モンテカルロ法などの科学技術計算に向いている。
Ruby などの一部のプログラミング言語では標準の疑似乱数生成器として組み込まれているらしい。</p>
<p><a href="https://github.com/goark/mt" title="goark/mt: Mersenne Twister; Pseudo Random Number Generator, Implemented by Golang"><code>github.com/goark/mt/v2</code></a> パッケージは <a href="http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/mt.html" title="Mersenne Twister: A random number generator (since 1997/10)">Mersenne Twister</a> のオリジナルコード(C/C++)を pure <a href="https://go.dev/">Go</a> で書き直したものである。</p>
<p><a href="https://github.com/goark/mt/actions"><img src="https://github.com/goark/mt/workflows/vulns/badge.svg" alt="check vulns"></a>
<a href="https://github.com/goark/mt/actions"><img src="https://github.com/goark/mt/workflows/lint/badge.svg" alt="lint status"></a>
<a href="https://raw.githubusercontent.com/goark/mt/master/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="GitHub license"></a>
<a href="https://github.com/goark/mt/releases/latest"><img src="https://img.shields.io/github/release/goark/mt.svg" alt="GitHub release"></a></p>
<p><a href="https://github.com/goark/mt" title="goark/mt: Mersenne Twister; Pseudo Random Number Generator, Implemented by Golang"><code>github.com/goark/mt/v2</code></a> パッケージの特徴は以下の通り。</p>
<ul>
<li><a href="https://pkg.go.dev/math/rand/v2" title="rand package - math/rand - pkg.go.dev"><code>math/rand/v2</code></a> パッケージ互換で <a href="https://pkg.go.dev/math/rand/v2" title="rand package - math/rand - pkg.go.dev"><code>rand</code></a><code>.Rand</code> のソースとして利用できる</li>
<li>並行的に安全(concurrency safe)な構成にできる(<a href="https://github.com/goark/mt" title="goark/mt: Mersenne Twister; Pseudo Random Number Generator, Implemented by Golang"><code>mt</code></a><code>.PRNG</code> 型を利用した場合)</li>
</ul>
<h2>github.com/goark/mt/v2/mt19937.Source の機能</h2>
<p><a href="https://github.com/goark/mt" title="goark/mt: Mersenne Twister; Pseudo Random Number Generator, Implemented by Golang"><code>github.com/goark/mt/v2</code></a><code>/mt19937</code> パッケージは <a href="http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/mt64.html">64bit版 Mersenne Twister</a> を元に pure <a href="https://go.dev/">Go</a> で書き直したものである。</p>
<p><code>mt19937.Source</code> はそのまま疑似乱数生成器として使える。
たとえば以下のように記述する。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/goark/mt/v2"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/goark/mt/v2/mt19937"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">mt</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="nx">mt19937</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="mi">19650218</span><span class="p">)).</span><span class="nf">Uint64</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"> <span class="c1">//Output:
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">//13735441942630277712
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p>提供するメソッドは以下の通り。</p>
<table>
<thead>
<tr>
<th>メソッド</th>
<th>機能</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Source.Seed(int64)</code></td>
<td>乱数のシードをセットする</td>
</tr>
<tr>
<td><code>Source.SeedArray([]uint64)</code></td>
<td>乱数のシード(配列)をセットする</td>
</tr>
<tr>
<td><code>Source.Uint64() uint64</code></td>
<td>乱数として範囲 $[0, 2^{64}-1]$ の整数値を生成する</td>
</tr>
<tr>
<td><code>Source.Real(int) float64</code></td>
<td>乱数として浮動小数点数値を生成する</td>
</tr>
</tbody>
</table>
<p><code>Source.Real()</code> 関数の引数による乱数の出力範囲は以下の通り。</p>
<table>
<thead>
<tr>
<th style="text-align:center">引数</th>
<th>生成範囲</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">1</td>
<td>範囲 $[0, 1)$ の浮動小数点数値</td>
</tr>
<tr>
<td style="text-align:center">2</td>
<td>範囲 $(0, 1)$ の浮動小数点数値</td>
</tr>
<tr>
<td style="text-align:center">上記以外</td>
<td>範囲 $[0, 1]$ の浮動小数点数値</td>
</tr>
</tbody>
</table>
<p>なお <code>mt19937.Source</code> は並行的に安全ではないので goroutine 間でインスタンスを共有できない。</p>
<h2>math/rand/v2 パッケージと組み合わせる</h2>
<p><code>mt19937.Source</code> を <a href="https://pkg.go.dev/math/rand/v2" title="rand package - math/rand - pkg.go.dev"><code>math/rand/v2</code></a> パッケージの <a href="https://pkg.go.dev/math/rand/v2" title="rand package - math/rand - pkg.go.dev"><code>rand</code></a><code>.Rand</code> のソースとして利用するには以下のように記述すればよい。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"math/rand/v2"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/goark/mt/v2/mt19937"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">rand</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="nx">mt19937</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="mi">19650218</span><span class="p">)).</span><span class="nf">Uint64</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"> <span class="c1">//Output:
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">//13735441942630277712
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p>これで <a href="https://pkg.go.dev/math/rand/v2" title="rand package - math/rand - pkg.go.dev"><code>rand</code></a><code>.Rand</code> が提供するメソッドはすべて使える。
ただし <a href="https://pkg.go.dev/math/rand/v2" title="rand package - math/rand - pkg.go.dev"><code>rand</code></a><code>.Rand</code> 自体も並行的に安全ではないので,取り扱いにはやはり注意が必要である。</p>
<h2>mt.PRNG と組み合わせる</h2>
<p><code>mt19937.Source</code> 型を <a href="https://github.com/goark/mt" title="goark/mt: Mersenne Twister; Pseudo Random Number Generator, Implemented by Golang"><code>mt</code></a><code>.PRNG</code> 型と組み合わせることで並行的に安全な構成にできる。
たとえばこんな感じに記述できる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"math/rand/v2"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"sync"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"time"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/goark/mt/v2"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/goark/mt/v2/mt19937"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">start</span> <span class="o">:=</span> <span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nx">wg</span> <span class="o">:=</span> <span class="nx">sync</span><span class="p">.</span><span class="nx">WaitGroup</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">prng</span> <span class="o">:=</span> <span class="nx">mt</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="nx">mt19937</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="nx">rand</span><span class="p">.</span><span class="nf">Int64</span><span class="p">()))</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="mi">1000</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">wg</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">go</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">defer</span> <span class="nx">wg</span><span class="p">.</span><span class="nf">Done</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="mi">10000</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">prng</span><span class="p">.</span><span class="nf">Uint64</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}()</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">wg</span><span class="p">.</span><span class="nf">Wait</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">"Time:"</span><span class="p">,</span> <span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">().</span><span class="nf">Sub</span><span class="p">(</span><span class="nx">start</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><a href="https://github.com/goark/mt" title="goark/mt: Mersenne Twister; Pseudo Random Number Generator, Implemented by Golang"><code>mt</code></a><code>.PRNG</code> 型は <a href="https://github.com/goark/mt" title="goark/mt: Mersenne Twister; Pseudo Random Number Generator, Implemented by Golang"><code>github.com/goark/mt/v2</code></a><code>/mt19937.Source</code> のラッパーになっている。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"math/rand/v2"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"sync"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Source represents a source of uniformly-distributed
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">Source</span> <span class="kd">interface</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rand</span><span class="p">.</span><span class="nx">Source</span>
</span></span><span class="line"><span class="cl"> <span class="nf">SeedArray</span><span class="p">([]</span><span class="kt">uint64</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nf">Real</span><span class="p">(</span><span class="kt">int</span><span class="p">)</span> <span class="kt">float64</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// PRNG is class of pseudo random number generator.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">PRNG</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">source</span> <span class="nx">Source</span>
</span></span><span class="line"><span class="cl"> <span class="nx">mutex</span> <span class="o">*</span><span class="nx">sync</span><span class="p">.</span><span class="nx">Mutex</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3>io.Reader 互換の疑似乱数生成器</h3>
<p><a href="https://github.com/goark/mt" title="goark/mt: Mersenne Twister; Pseudo Random Number Generator, Implemented by Golang"><code>mt</code></a><code>.PRNG</code> のインスタンスから <a href="https://github.com/goark/mt" title="goark/mt: Mersenne Twister; Pseudo Random Number Generator, Implemented by Golang"><code>mt</code></a><code>.Reader</code> 型のインスタンスを生成できる。
こんな感じに記述できる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"encoding/binary"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"math/rand/v2"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"sync"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/goark/mt/v2"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/goark/mt/v2/mt19937"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">wg</span> <span class="o">:=</span> <span class="nx">sync</span><span class="p">.</span><span class="nx">WaitGroup</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">prng</span> <span class="o">:=</span> <span class="nx">mt</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="nx">mt19937</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="nx">rand</span><span class="p">.</span><span class="nf">Int64</span><span class="p">()))</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="mi">1000</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">wg</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">go</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">defer</span> <span class="nx">wg</span><span class="p">.</span><span class="nf">Done</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nx">r</span> <span class="o">:=</span> <span class="nx">prng</span><span class="p">.</span><span class="nf">NewReader</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nx">buf</span> <span class="o">:=</span> <span class="p">[</span><span class="mi">8</span><span class="p">]</span><span class="kt">byte</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="mi">10000</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">ct</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">r</span><span class="p">.</span><span class="nf">Read</span><span class="p">(</span><span class="nx">buf</span><span class="p">[:])</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">binary</span><span class="p">.</span><span class="nx">LittleEndian</span><span class="p">.</span><span class="nf">Uint64</span><span class="p">(</span><span class="nx">buf</span><span class="p">[:</span><span class="nx">ct</span><span class="p">]))</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}()</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">wg</span><span class="p">.</span><span class="nf">Wait</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><a href="https://github.com/goark/mt" title="goark/mt: Mersenne Twister; Pseudo Random Number Generator, Implemented by Golang"><code>mt</code></a><code>.Reader</code> 型は <a href="https://golang.org/pkg/io/" title="io - The Go Programming Language"><code>io</code></a><code>.Reader</code> インタフェースと互換性がある。
また <a href="https://github.com/goark/mt" title="goark/mt: Mersenne Twister; Pseudo Random Number Generator, Implemented by Golang"><code>mt</code></a><code>.Reader</code> インスタンスも並行的に安全なので goroutine 間で共有可能である。</p>
<h2>ライセンスについて</h2>
<p><a href="https://github.com/goark/mt" title="goark/mt: Mersenne Twister; Pseudo Random Number Generator, Implemented by Golang"><code>github.com/goark/mt/v2</code></a> パッケージは MIT ライセンスで提供している。</p>
<p>オリジナルの <a href="http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/mt.html" title="Mersenne Twister: A random number generator (since 1997/10)">Mersenne Twister</a> コードは GPL または BSD ライセンスで提供されているが MIT ライセンスに書き換えてもいいらしい。</p>
<ul>
<li><a href="http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/MT2002/license.html">Mersenne Twisterの商業利用について</a></li>
</ul>
<p>というわけで <a href="https://github.com/goark/mt" title="goark/mt: Mersenne Twister; Pseudo Random Number Generator, Implemented by Golang"><code>github.com/goark/mt/v2</code></a> パッケージは MIT ライセンスで提供することにした。
ご利用はお気軽に。</p>
<h2>参考図書</h2>
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/416Stewy0NS._SL160_.jpg" width="123" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">プログラミング言語Go</a></dt>
<dd>アラン・ドノバン (著), ブライアン・カーニハン (著), 柴田芳樹 (著)</dd>
<dd>丸善出版 2016-06-20 (Release 2021-07-13)</dd>
<dd>Kindle版</dd>
<dd>B099928SJD (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">Kindle 版出た! 一部内容が古びてしまったが,この本は Go 言語の教科書と言ってもいいだろう。感想は<a href="https://text.baldanders.info/remark/2016/07/go-programming-language/" >こちら</a>。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2021-05-22">2021-05-22</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- プログラミング言語Go -->
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B00I8AT1FO?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/41353H+BzFL._SL160_.jpg" width="113" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B00I8AT1FO?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">数学ガール/乱択アルゴリズム</a></dt>
<dd>結城 浩 (著)</dd>
<dd>SBクリエイティブ 2011-02-25 (Release 2014-03-12)</dd>
<dd>Kindle版</dd>
<dd>B00I8AT1FO (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">工学ガール,リサちゃん登場!</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2015-04-19">2015-04-19</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- 数学ガール/乱択アルゴリズム -->
疑似乱数生成器 spiegel-im-spiegel/mt をリリースした
tag:text.Baldanders.info,2019-09-22:/release/2019/09/mersenne-twister-package-is-released/
2019-09-22T08:37:19+00:00
2024-03-09T11:45:46+00:00
ついカッとなって書いた。反省はしていない。
Spiegel
https://baldanders.info/profile/
<p>ついカッとなって書いた。
反省はしていない。</p>
<ul>
<li><a href="https://github.com/spiegel-im-spiegel/mt">spiegel-im-spiegel/mt: Mersenne Twister; Pseudo Random Number Generator, Implemented by Golang</a></li>
</ul>
<p><a href="http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/mt.html" title="Mersenne Twister: A random number generator (since 1997/10)">Mersenne Twister</a> の <a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>実装はいくつかあるのだが,やっぱ他人が作る道具は使いにくいよね,というわけで自分で書いてしまった(笑)
<a href="https://github.com/spiegel-im-spiegel/mt" title="spiegel-im-spiegel/mt: Mersenne Twister; Pseudo Random Number Generator, Implemented by Golang">spiegel-im-spiegel/mt</a> の特徴は以下の通り。</p>
<ul>
<li><a href="https://golang.org/pkg/math/rand/" title="rand - The Go Programming Language">math/rand</a> 互換で <a href="https://golang.org/pkg/math/rand/" title="rand - The Go Programming Language"><code>rand</code></a><code>.Rand</code> のソースとして利用できる</li>
<li>goroutine-safe な構成にできる(<a href="https://github.com/spiegel-im-spiegel/mt" title="spiegel-im-spiegel/mt: Mersenne Twister; Pseudo Random Number Generator, Implemented by Golang"><code>mt</code></a><code>.PRNG</code> 型を利用した場合)</li>
</ul>
<p>使い方は以下を参照のこと。</p>
<ul>
<li><a href="https://text.baldanders.info/release/mersenne-twister-by-golang/">疑似乱数生成器 spiegel-im-spiegel/mt</a></li>
</ul>
<p>一応,ベンチマークテストもしてみた。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"math/rand"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"testing"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"time"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/spiegel-im-spiegel/mt"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/spiegel-im-spiegel/mt/mt19937"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">const</span> <span class="nx">count</span> <span class="p">=</span> <span class="mi">10000000</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkRandomALFG</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rnd</span> <span class="o">:=</span> <span class="nx">rand</span><span class="p">.</span><span class="nf">NewSource</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">().</span><span class="nf">UnixNano</span><span class="p">()).(</span><span class="nx">rand</span><span class="p">.</span><span class="nx">Source64</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span><span class="p">.</span><span class="nf">ResetTimer</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">count</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rnd</span><span class="p">.</span><span class="nf">Uint64</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkRandomMT19917</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rnd</span> <span class="o">:=</span> <span class="nx">mt19937</span><span class="p">.</span><span class="nf">NewSource</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">().</span><span class="nf">UnixNano</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span><span class="p">.</span><span class="nf">ResetTimer</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">count</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rnd</span><span class="p">.</span><span class="nf">Uint64</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkRandomALFGRand</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rnd</span> <span class="o">:=</span> <span class="nx">rand</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="nx">rand</span><span class="p">.</span><span class="nf">NewSource</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">().</span><span class="nf">UnixNano</span><span class="p">()))</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span><span class="p">.</span><span class="nf">ResetTimer</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">count</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rnd</span><span class="p">.</span><span class="nf">Uint64</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkRandomMT19917Rand</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rnd</span> <span class="o">:=</span> <span class="nx">rand</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="nx">mt19937</span><span class="p">.</span><span class="nf">NewSource</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">().</span><span class="nf">UnixNano</span><span class="p">()))</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span><span class="p">.</span><span class="nf">ResetTimer</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">count</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rnd</span><span class="p">.</span><span class="nf">Uint64</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkRandomALFGLocked</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span><span class="p">.</span><span class="nf">ResetTimer</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">count</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rand</span><span class="p">.</span><span class="nf">Uint64</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkRandomMT19917Locked</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rnd</span> <span class="o">:=</span> <span class="nx">mt</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="nx">mt19937</span><span class="p">.</span><span class="nf">NewSource</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">().</span><span class="nf">UnixNano</span><span class="p">()))</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span><span class="p">.</span><span class="nf">ResetTimer</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">count</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rnd</span><span class="p">.</span><span class="nf">Uint64</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>テスト対象は以下の通り。</p>
<table>
<thead>
<tr>
<th>テスト名</th>
<th>対象</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>BenchmarkRandomALFG</code></td>
<td><a href="https://golang.org/pkg/math/rand/" title="rand - The Go Programming Language">math/rand</a> 標準アルゴリズム<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></td>
</tr>
<tr>
<td><code>BenchmarkRandomMT19917</code></td>
<td><a href="https://github.com/spiegel-im-spiegel/mt" title="spiegel-im-spiegel/mt: Mersenne Twister; Pseudo Random Number Generator, Implemented by Golang"><code>mt</code></a><code>/mt19937</code> パッケージ</td>
</tr>
<tr>
<td><code>BenchmarkRandomALFGRand</code></td>
<td><a href="https://golang.org/pkg/math/rand/" title="rand - The Go Programming Language">math/rand</a> (<a href="https://golang.org/pkg/math/rand/" title="rand - The Go Programming Language"><code>rand</code></a><code>.Rand</code> ラッパ)</td>
</tr>
<tr>
<td><code>BenchmarkRandomMT19917Rand</code></td>
<td><a href="https://github.com/spiegel-im-spiegel/mt" title="spiegel-im-spiegel/mt: Mersenne Twister; Pseudo Random Number Generator, Implemented by Golang"><code>mt</code></a><code>/mt19937</code> (<a href="https://golang.org/pkg/math/rand/" title="rand - The Go Programming Language"><code>rand</code></a><code>.Rand</code> ラッパ)</td>
</tr>
<tr>
<td><code>BenchmarkRandomALFGLocked</code></td>
<td><a href="https://golang.org/pkg/math/rand/" title="rand - The Go Programming Language">math/rand</a> Sync バージョン</td>
</tr>
<tr>
<td><code>BenchmarkRandomMT19917Locked</code></td>
<td><a href="https://github.com/spiegel-im-spiegel/mt" title="spiegel-im-spiegel/mt: Mersenne Twister; Pseudo Random Number Generator, Implemented by Golang"><code>mt</code></a><code>/mt19937</code> + <a href="https://github.com/spiegel-im-spiegel/mt" title="spiegel-im-spiegel/mt: Mersenne Twister; Pseudo Random Number Generator, Implemented by Golang"><code>mt</code></a><code>.PRNG</code></td>
</tr>
</tbody>
</table>
<p>このベンチマークテストの実行結果は以下の通り。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go test -bench Random -benchmem
</span></span><span class="line"><span class="cl">goos: linux
</span></span><span class="line"><span class="cl">goarch: amd64
</span></span><span class="line"><span class="cl">pkg: github.com/spiegel-im-spiegel/mt/benchmark
</span></span><span class="line"><span class="cl">BenchmarkRandomALFG-4 1000000000 0.0492 ns/op 0 B/op 0 allocs/op
</span></span><span class="line"><span class="cl">BenchmarkRandomMT19917-4 1000000000 0.0651 ns/op 0 B/op 0 allocs/op
</span></span><span class="line"><span class="cl">BenchmarkRandomALFGRand-4 1000000000 0.0749 ns/op 0 B/op 0 allocs/op
</span></span><span class="line"><span class="cl">BenchmarkRandomMT19917Rand-4 1000000000 0.0846 ns/op 0 B/op 0 allocs/op
</span></span><span class="line"><span class="cl">BenchmarkRandomALFGLocked-4 1000000000 0.176 ns/op 0 B/op 0 allocs/op
</span></span><span class="line"><span class="cl">BenchmarkRandomMT19917Locked-4 1000000000 0.192 ns/op 0 B/op 0 allocs/op
</span></span><span class="line"><span class="cl">PASS
</span></span><span class="line"><span class="cl">ok github.com/spiegel-im-spiegel/mt/benchmark 7.081s
</span></span></code></pre></div><p>というわけで <a href="https://golang.org/pkg/math/rand/" title="rand - The Go Programming Language">math/rand</a> のほうが若干速いかな。
乱数としての性能は別の機会に。</p>
<h2>参考図書</h2>
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/416Stewy0NS._SL160_.jpg" width="123" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">プログラミング言語Go</a></dt>
<dd>アラン・ドノバン (著), ブライアン・カーニハン (著), 柴田芳樹 (著)</dd>
<dd>丸善出版 2016-06-20 (Release 2021-07-13)</dd>
<dd>Kindle版</dd>
<dd>B099928SJD (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">Kindle 版出た! 一部内容が古びてしまったが,この本は Go 言語の教科書と言ってもいいだろう。感想は<a href="https://text.baldanders.info/remark/2016/07/go-programming-language/" >こちら</a>。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2021-05-22">2021-05-22</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- プログラミング言語Go -->
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B00I8AT1FO?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/41353H+BzFL._SL160_.jpg" width="113" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B00I8AT1FO?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">数学ガール/乱択アルゴリズム</a></dt>
<dd>結城 浩 (著)</dd>
<dd>SBクリエイティブ 2011-02-25 (Release 2014-03-12)</dd>
<dd>Kindle版</dd>
<dd>B00I8AT1FO (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">工学ガール,リサちゃん登場!</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2015-04-19">2015-04-19</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- 数学ガール/乱択アルゴリズム -->
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://golang.org/pkg/math/rand/" title="rand - The Go Programming Language">math/rand</a> パッケージに実装されている擬似乱数生成器は<a href="https://groups.google.com/forum/#!topic/golang-nuts/RZ1G3_cxMcM">ラグ付フィボナッチ法(Lagged Fibonacci Generator)のバリエーション</a>らしい。 <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Go の疑似乱数生成器は並行的に安全ではないらしい(追記あり)
tag:text.Baldanders.info,2019-09-17:/golang/pseudo-random-number-generator/
2019-09-17T14:27:18+00:00
2022-02-12T19:35:54+00:00
件の記事では解決方法が(具体的には)示されていないので,いくつか対策を考えてみよう。
Spiegel
https://baldanders.info/profile/
<p>面白い記事みっけ!</p>
<ul>
<li><a href="https://qiita.com/hiromichi_n/items/d0636b9444dca18ef357">【Go】rand.Sourceを並列で使いまわすなんて何事だ - Qiita</a></li>
</ul>
<p>折角なので便乗記事を書いてみる。</p>
<p>まぁ,内部状態を持つオブジェクトは,状態が変わらない(immutable)か操作が並行的に安全(concurrency safe)であることが仕様・設計として明確であるものでない限り,複数の goroutine 間でインスタンスを共有してはいけない,というのは基本中の基本である。</p>
<p>ましてや標準の <a href="https://golang.org/pkg/math/rand/" title="rand - The Go Programming Language">math/rand</a> パッケージは <a href="https://golang.org/pkg/math/rand/" title="rand - The Go Programming Language"><code>rand</code></a><code>.Source</code> インタフェースを満たすのであればユーザ側で任意のアルゴリズムを用意することもできるので,並行的に安全であることを期待するほうが間違っているとも言える。</p>
<p>まずは,<a href="https://qiita.com/hiromichi_n/items/d0636b9444dca18ef357" title="【Go】rand.Sourceを並列で使いまわすなんて何事だ - Qiita">件の記事</a>で書かれているコードを挙げておこう。
ただし動作に直接関係ないコードは極力省いている。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"math/rand"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"sync"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"time"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">randSource</span> <span class="p">=</span> <span class="nf">NewRandSource</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">NewRandSource</span><span class="p">()</span> <span class="o">*</span><span class="nx">rand</span><span class="p">.</span><span class="nx">Rand</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">rand</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="nx">rand</span><span class="p">.</span><span class="nf">NewSource</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">().</span><span class="nf">UnixNano</span><span class="p">()))</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">calcRand</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="mi">10000</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">randSource</span><span class="p">.</span><span class="nf">Intn</span><span class="p">(</span><span class="mi">1000</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">wg</span> <span class="o">:=</span> <span class="nx">sync</span><span class="p">.</span><span class="nx">WaitGroup</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="mi">1000</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">wg</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">go</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nf">calcRand</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nx">wg</span><span class="p">.</span><span class="nf">Done</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="p">}()</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">wg</span><span class="p">.</span><span class="nf">Wait</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>ポイントは <a href="https://golang.org/pkg/math/rand/" title="rand - The Go Programming Language"><code>rand</code></a><code>.Rand</code> インスタンスを初期化時にひとつだけ生成し,複数の goroutine で使い回している点である。
これを実行すると以下のように panic を吐く。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go run -trimpath sample.go
</span></span><span class="line"><span class="cl">panic: runtime error: index out of range [-1]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">goroutine 94 [running]:
</span></span><span class="line"><span class="cl">math/rand.(*rngSource).Uint64(...)
</span></span><span class="line"><span class="cl"> math/rand/rng.go:249
</span></span><span class="line"><span class="cl">math/rand.(*rngSource).Int63(0xc000083500, 0x50321535775976c1)
</span></span><span class="line"><span class="cl"> math/rand/rng.go:234 +0x93
</span></span><span class="line"><span class="cl">math/rand.(*Rand).Int63(...)
</span></span><span class="line"><span class="cl"> math/rand/rand.go:85
</span></span><span class="line"><span class="cl">math/rand.(*Rand).Int31(...)
</span></span><span class="line"><span class="cl"> math/rand/rand.go:99
</span></span><span class="line"><span class="cl">math/rand.(*Rand).Int31n(0xc000088090, 0x3e8, 0x1fd)
</span></span><span class="line"><span class="cl"> math/rand/rand.go:134 +0x5f
</span></span><span class="line"><span class="cl">math/rand.(*Rand).Intn(0xc000088090, 0x3e8, 0x1fd)
</span></span><span class="line"><span class="cl"> math/rand/rand.go:172 +0x45
</span></span><span class="line"><span class="cl">main.calcRand()
</span></span><span class="line"><span class="cl"> sample@/sample.go:17 +0x3f
</span></span><span class="line"><span class="cl">main.main.func1(0xc000098000)
</span></span><span class="line"><span class="cl"> sample@/sample.go:26 +0x22
</span></span><span class="line"><span class="cl">created by main.main
</span></span><span class="line"><span class="cl"> sample@/sample.go:25 +0x78
</span></span><span class="line"><span class="cl">exit status 2
</span></span></code></pre></div><p>panic が発生する仕組みは<a href="https://qiita.com/hiromichi_n/items/d0636b9444dca18ef357" title="【Go】rand.Sourceを並列で使いまわすなんて何事だ - Qiita">件の記事</a>に分かりやすく解説されているので参照のこと。</p>
<h2>goroutine ごとにインスタンスを生成する</h2>
<p><a href="https://qiita.com/hiromichi_n/items/d0636b9444dca18ef357" title="【Go】rand.Sourceを並列で使いまわすなんて何事だ - Qiita">件の記事</a>では解決方法が(具体的には)示されていないので,こちらでいくつか考えてみよう。</p>
<p>一番簡単なのは goroutine ごとに <a href="https://golang.org/pkg/math/rand/" title="rand - The Go Programming Language"><code>rand</code></a><code>.Rand</code> インスタンスを生成することだ。
こんな感じに変えたらどうだろう。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line hl"><span class="cl"><span class="kd">func</span> <span class="nf">calcRand</span><span class="p">(</span><span class="nx">rnd</span><span class="o">*</span> <span class="nx">rand</span><span class="p">.</span><span class="nx">Rand</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="mi">10000</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line hl"><span class="cl"> <span class="nx">rnd</span><span class="p">.</span><span class="nf">Intn</span><span class="p">(</span><span class="mi">1000</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">wg</span> <span class="o">:=</span> <span class="nx">sync</span><span class="p">.</span><span class="nx">WaitGroup</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="mi">1000</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">wg</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">go</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line hl"><span class="cl"> <span class="nf">calcRand</span><span class="p">(</span><span class="nf">NewRandSource</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"> <span class="nx">wg</span><span class="p">.</span><span class="nf">Done</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="p">}()</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">wg</span><span class="p">.</span><span class="nf">Wait</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div>
<p>これで必要十分に機能するし,少なくとも panic は起こらない。
このやり方の欠点は(goroutine ごとに <a href="https://golang.org/pkg/math/rand/" title="rand - The Go Programming Language"><code>rand</code></a><code>.Rand</code> インスタンスが生成されるため)元のコードより(僅かだが)高コストになることと,疑似乱数生成器の性能がアルゴリズムだけでなく seed の選び方にも依存する,というあたりだろうか。</p>
<p>まぁ <a href="https://golang.org/pkg/math/rand/" title="rand - The Go Programming Language">math/rand</a> の標準アルゴリズム<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> であれば性能に関してはさしたる問題にはならないだろう。</p>
<h2>Generator Pattern を使う</h2>
<p>今回の例ではあまりオススメではないのだが,並行処理の Generator Pattern を使う手もある。</p>
<p>まず <code>NewRandSource()</code> 関数を以下の関数で置き換える。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">NewGenerator</span><span class="p">()</span> <span class="o"><-</span><span class="kd">chan</span> <span class="kt">int</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rnd</span> <span class="o">:=</span> <span class="nx">rand</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="nx">rand</span><span class="p">.</span><span class="nf">NewSource</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">().</span><span class="nf">UnixNano</span><span class="p">()))</span>
</span></span><span class="line"><span class="cl"> <span class="nx">ch</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="kt">int</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">go</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">defer</span> <span class="nb">close</span><span class="p">(</span><span class="nx">ch</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">ch</span> <span class="o"><-</span> <span class="nx">rnd</span><span class="p">.</span><span class="nf">Intn</span><span class="p">(</span><span class="mi">1000</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}()</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">ch</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>こうすれば <a href="https://golang.org/pkg/math/rand/" title="rand - The Go Programming Language"><code>rand</code></a><code>.Rand</code> インスタンスはひとつで済むし(seed もひとつ),持ち回すインスタンスは channel のみなので並行的に安全にできる。
乱数の取り出し側はこう書き換えればよい。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line hl"><span class="cl"><span class="kd">func</span> <span class="nf">calcRand</span><span class="p">(</span><span class="nx">gen</span> <span class="o"><-</span><span class="kd">chan</span> <span class="kt">int</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="mi">10000</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line hl"><span class="cl"> <span class="k">if</span> <span class="nx">_</span> <span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="o"><-</span><span class="nx">gen</span><span class="p">;</span> <span class="p">!</span><span class="nx">ok</span> <span class="p">{</span>
</span></span><span class="line hl"><span class="cl"> <span class="k">return</span>
</span></span><span class="line hl"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">wg</span> <span class="o">:=</span> <span class="nx">sync</span><span class="p">.</span><span class="nx">WaitGroup</span><span class="p">{}</span>
</span></span><span class="line hl"><span class="cl"> <span class="nx">gen</span> <span class="o">:=</span> <span class="nf">NewGenerator</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="mi">1000</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">wg</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">go</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line hl"><span class="cl"> <span class="nf">calcRand</span><span class="p">(</span><span class="nx">gen</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">wg</span><span class="p">.</span><span class="nf">Done</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="p">}()</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">wg</span><span class="p">.</span><span class="nf">Wait</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div>
<p>このコードの欠点は「遅い」ことに尽きる。
まぁ channel の読み書きで同期を取る必要があるから遅くなって当たり前だけど。</p>
<p>今回のようなケースではなく,例えば generator がハードウェア制御を伴うものだったり <a href="https://text.baldanders.info/golang/singleton-pattern/">singleton</a> を含む処理だったり channel の読み書きにかかるコストに対して他の処理が相対的に大きくなったり …などなど,状況によっては Generator Pattern のほうが有利になる場合もあるだろう。</p>
<p>Generator Pattern は平行処理のデザインパターンの中では比較的単純なものだが応用範囲が広い。
<a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>の goroutine 自体は(OS スレッドなどと比べて)かなり安価で手軽に構成できるので,積極的に試してみるといいと思う。</p>
<h3>おまけの追記</h3>
<p>そうそう。
上の <code>NewGenerator()</code> 関数で生成・駆動される goroutine は自力で終了できない。
なので,以下のように</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">NewGenerator</span><span class="p">(</span><span class="nx">ctx</span> <span class="nx">context</span><span class="p">.</span><span class="nx">Context</span><span class="p">)</span> <span class="o"><-</span><span class="kd">chan</span> <span class="kt">int</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rnd</span> <span class="o">:=</span> <span class="nx">rand</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="nx">rand</span><span class="p">.</span><span class="nf">NewSource</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">().</span><span class="nf">UnixNano</span><span class="p">()))</span>
</span></span><span class="line"><span class="cl"> <span class="nx">ch</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="kt">int</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">go</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">defer</span> <span class="nb">close</span><span class="p">(</span><span class="nx">ch</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">select</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="o"><-</span><span class="nx">ctx</span><span class="p">.</span><span class="nf">Done</span><span class="p">():</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span>
</span></span><span class="line"><span class="cl"> <span class="k">default</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="nx">ch</span> <span class="o"><-</span> <span class="nx">rnd</span><span class="p">.</span><span class="nf">Intn</span><span class="p">(</span><span class="mi">1000</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}()</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">ch</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>外部からキャンセルイベントを流し込めるようにするといいかもしれない。</p>
<h2>【2019-09-20 追記】 実は標準で並行的に安全な疑似乱数生成器が用意されていた</h2>
<p>あれから <a href="https://golang.org/pkg/math/rand/" title="rand - The Go Programming Language">math/rand</a> のソースコードを眺めてて気がついたのだが,実は並行的に安全な疑似乱数生成器が標準で用意されていた。</p>
<p>たとえば <a href="https://golang.org/pkg/math/rand/" title="rand - The Go Programming Language"><code>rand</code></a><code>.Intn()</code> 関数を見ると</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// Intn returns, as an int, a non-negative pseudo-random number in [0,n)
</span></span></span><span class="line"><span class="cl"><span class="c1">// from the default Source.
</span></span></span><span class="line"><span class="cl"><span class="c1">// It panics if n <= 0.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">Intn</span><span class="p">(</span><span class="nx">n</span> <span class="kt">int</span><span class="p">)</span> <span class="kt">int</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">globalRand</span><span class="p">.</span><span class="nf">Intn</span><span class="p">(</span><span class="nx">n</span><span class="p">)</span> <span class="p">}</span>
</span></span></code></pre></div><p>とか書かれていて,じゃあ <code>globalRand</code> って何なん? と思って見てみたら</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">lockedSource</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">lk</span> <span class="nx">sync</span><span class="p">.</span><span class="nx">Mutex</span>
</span></span><span class="line"><span class="cl"> <span class="nx">src</span> <span class="nx">Source64</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">r</span> <span class="o">*</span><span class="nx">lockedSource</span><span class="p">)</span> <span class="nf">Int63</span><span class="p">()</span> <span class="p">(</span><span class="nx">n</span> <span class="kt">int64</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">r</span><span class="p">.</span><span class="nx">lk</span><span class="p">.</span><span class="nf">Lock</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nx">n</span> <span class="p">=</span> <span class="nx">r</span><span class="p">.</span><span class="nx">src</span><span class="p">.</span><span class="nf">Int63</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nx">r</span><span class="p">.</span><span class="nx">lk</span><span class="p">.</span><span class="nf">Unlock</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">r</span> <span class="o">*</span><span class="nx">lockedSource</span><span class="p">)</span> <span class="nf">Uint64</span><span class="p">()</span> <span class="p">(</span><span class="nx">n</span> <span class="kt">uint64</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">r</span><span class="p">.</span><span class="nx">lk</span><span class="p">.</span><span class="nf">Lock</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nx">n</span> <span class="p">=</span> <span class="nx">r</span><span class="p">.</span><span class="nx">src</span><span class="p">.</span><span class="nf">Uint64</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nx">r</span><span class="p">.</span><span class="nx">lk</span><span class="p">.</span><span class="nf">Unlock</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">r</span> <span class="o">*</span><span class="nx">lockedSource</span><span class="p">)</span> <span class="nf">Seed</span><span class="p">(</span><span class="nx">seed</span> <span class="kt">int64</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">r</span><span class="p">.</span><span class="nx">lk</span><span class="p">.</span><span class="nf">Lock</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nx">r</span><span class="p">.</span><span class="nx">src</span><span class="p">.</span><span class="nf">Seed</span><span class="p">(</span><span class="nx">seed</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">r</span><span class="p">.</span><span class="nx">lk</span><span class="p">.</span><span class="nf">Unlock</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">globalRand</span> <span class="p">=</span> <span class="nf">New</span><span class="p">(</span><span class="o">&</span><span class="nx">lockedSource</span><span class="p">{</span><span class="nx">src</span><span class="p">:</span> <span class="nf">NewSource</span><span class="p">(</span><span class="mi">1</span><span class="p">).(</span><span class="nx">Source64</span><span class="p">)})</span>
</span></span></code></pre></div><p>とか書かれているわけですよ。
なんだ,ちゃんと <a href="https://golang.org/pkg/sync/" title="sync - The Go Programming Language"><code>sync</code></a><code>.Mutex</code> で排他制御してるんぢゃん。</p>
<p>というわけで,最初のコードは</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"math/rand"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"sync"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"time"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">calcRnad</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="mi">10000</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rand</span><span class="p">.</span><span class="nf">Intn</span><span class="p">(</span><span class="mi">1000</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rand</span><span class="p">.</span><span class="nf">Seed</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">().</span><span class="nf">UnixNano</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"> <span class="nx">wg</span> <span class="o">:=</span> <span class="nx">sync</span><span class="p">.</span><span class="nx">WaitGroup</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="mi">1000</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">wg</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">go</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nf">calcRnad</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nx">wg</span><span class="p">.</span><span class="nf">Done</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="p">}()</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">wg</span><span class="p">.</span><span class="nf">Wait</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>と書けば panic を吐くことなくちゃんと終了する。
若干遅くはなるけど,それでも Generator Pattern を使うよりは全然速い。</p>
<h2>ブックマーク</h2>
<ul>
<li>
<p><a href="https://hori-ryota.com/blog/golang-channel-pattern/">Go の channel 処理パターン集</a></p>
</li>
<li>
<p><a href="https://text.baldanders.info/golang/estimate-of-pi-4-prng/">モンテカルロ法による円周率の推定(その4 PRNG)</a></p>
</li>
</ul>
<h2>参考図書</h2>
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/4873118468?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/51pUKQajnaL._SL160_.jpg" width="125" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/4873118468?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">Go言語による並行処理</a></dt>
<dd>Katherine Cox-Buday (著), 山口 能迪 (翻訳)</dd>
<dd>オライリージャパン 2018-10-26</dd>
<dd>単行本(ソフトカバー)</dd>
<dd>4873118468 (ASIN), 9784873118468 (EAN), 4873118468 (ISBN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description"><a href="https://www.oreilly.co.jp/books/9784873118468/">Eブック版もある</a>。感想は<a href="https://text.baldanders.info/remark/2018/11/concurrency-in-go/">こちら</a>。 Go 言語で並行処理を書くならこの本は必読書になるだろう。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2020-01-13">2020-01-13</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- Go言語による並行処理 -->
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/416Stewy0NS._SL160_.jpg" width="123" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">プログラミング言語Go</a></dt>
<dd>アラン・ドノバン (著), ブライアン・カーニハン (著), 柴田芳樹 (著)</dd>
<dd>丸善出版 2016-06-20 (Release 2021-07-13)</dd>
<dd>Kindle版</dd>
<dd>B099928SJD (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">Kindle 版出た! 一部内容が古びてしまったが,この本は Go 言語の教科書と言ってもいいだろう。感想は<a href="https://text.baldanders.info/remark/2016/07/go-programming-language/" >こちら</a>。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2021-05-22">2021-05-22</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- プログラミング言語Go -->
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://golang.org/pkg/math/rand/" title="rand - The Go Programming Language">math/rand</a> パッケージに実装されている擬似乱数生成器は<a href="https://groups.google.com/forum/#!topic/golang-nuts/RZ1G3_cxMcM">ラグ付フィボナッチ法(Lagged Fibonacci Generator)のバリエーション</a>らしい。 <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
「《命》に関わる確率」を疑似乱数を使って解いてみる
tag:text.Baldanders.info,2019-04-23:/remark/2019/04/mathgirl-note257/
2019-04-23T12:06:30+00:00
2021-08-12T21:22:05+00:00
結果だけを見れば直感に反するかも知れないが,こうやって実際にコードを書いてみると納得感が強まる。 やっぱプログラマはコードを書いてナンボだよね(笑)
Spiegel
https://baldanders.info/profile/
<p><a href="https://cakes.mu/series/339" title="数学ガールの秘密ノート|結城浩|cakes(ケイクス)">cakes で連載中の「数学ガールの秘密ノート」</a>。
今回の「<a href="https://cakes.mu/posts/25349" title="第257回 確率の冒険:《命》に関わる確率(前編)|数学ガールの秘密ノート|結城浩|cakes(ケイクス)">確率の冒険:《命》に関わる確率(前編)</a>」はなかなか面白かった。
確率や統計は直感に反する場合があって,そういう事例を考えるのは本当に楽しい。</p>
<p>今回のプロブレムを整理してみよう。</p>
<figure>
<blockquote><ul>
<li>《○○菌》に感染している人は《全人口の1%》である</li>
<li>○○菌に感染しているか否かを調べる《判定キット》がある。判定キットは感染しているか否かを《90%の確率》で正しく判定する
<ul>
<li>《感染している》という判定のことを《陽性》と呼ぶ</li>
<li>《感染していない》という判定のことを《陰性》と呼ぶ</li>
</ul>
</li>
<li>ある人を判定キットで調べたら《陽性》だった。この人が○○菌に実際に感染している確率を求めよ</li>
</ul>
</blockquote>
<figcaption><div><q><a href="https://cakes.mu/posts/25349">確率の冒険:《命》に関わる確率(前編)</a></q>より</div></figcaption>
</figure>
<p>きちんとした解法は本編を読んでいただくとして,この記事では疑似乱数を用いたシミュレーションで解いてみる。</p>
<h2>指定した確率で真偽を出力するクラス</h2>
<p>まずは指定した確率で真偽を出力するクラスを考えてみる。
<a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>でね(笑)</p>
<p>こんな感じでどうだろう。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">prob</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"math"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"math/rand"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"time"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">New</span><span class="p">(</span><span class="nx">p</span> <span class="kt">float64</span><span class="p">)</span> <span class="o"><-</span><span class="kd">chan</span> <span class="kt">bool</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">ch</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="kt">bool</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">go</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">defer</span> <span class="nb">close</span><span class="p">(</span><span class="nx">ch</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">max</span> <span class="o">:=</span> <span class="mi">1000000</span>
</span></span><span class="line"><span class="cl"> <span class="nx">limit</span> <span class="o">:=</span> <span class="nf">percent</span><span class="p">(</span><span class="nx">p</span><span class="p">,</span> <span class="nx">max</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rnd</span> <span class="o">:=</span> <span class="nx">rand</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="nx">rand</span><span class="p">.</span><span class="nf">NewSource</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">().</span><span class="nf">UnixNano</span><span class="p">()))</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">n</span> <span class="o">:=</span> <span class="nx">rnd</span><span class="p">.</span><span class="nf">Intn</span><span class="p">(</span><span class="nx">max</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"> <span class="nx">ch</span> <span class="o"><-</span> <span class="nx">n</span> <span class="p"><</span> <span class="nx">limit</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}()</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">ch</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">percent</span><span class="p">(</span><span class="nx">f</span> <span class="kt">float64</span><span class="p">,</span> <span class="nx">max</span> <span class="kt">int</span><span class="p">)</span> <span class="kt">int</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">f</span> <span class="p"><</span> <span class="mi">0</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">f</span> <span class="p">></span> <span class="mf">1.0</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">max</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nb">int</span><span class="p">(</span><span class="nx">math</span><span class="p">.</span><span class="nf">Floor</span><span class="p">(</span><span class="nx">f</span><span class="o">*</span><span class="nb">float64</span><span class="p">(</span><span class="nx">max</span><span class="p">)</span> <span class="o">+</span> <span class="mf">0.5</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>これで</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">ch</span> <span class="o">:=</span> <span class="nx">prob</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="mf">0.1</span><span class="p">)</span>
</span></span></code></pre></div><p>とすれば channel <code>ch</code> から10%の確率で true が出力される筈である。
サンプル・コードを書いて試してみよう。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">probability</span><span class="p">(</span><span class="nx">try</span> <span class="kt">int</span><span class="p">,</span> <span class="nx">ch</span> <span class="o"><-</span><span class="kd">chan</span> <span class="kt">bool</span><span class="p">)</span> <span class="kt">int</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">count</span> <span class="o">:=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">try</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="o"><-</span><span class="nx">ch</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">count</span><span class="o">++</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">count</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">ch</span> <span class="o">:=</span> <span class="nx">prob</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="mf">0.1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">min</span> <span class="o">:=</span> <span class="nb">float64</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">max</span> <span class="o">:=</span> <span class="nb">float64</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">sum</span> <span class="o">:=</span> <span class="nb">float64</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">sum2</span> <span class="o">:=</span> <span class="nb">float64</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">try</span> <span class="o">:=</span> <span class="mi">10000</span>
</span></span><span class="line"><span class="cl"> <span class="nx">tryf</span> <span class="o">:=</span> <span class="nb">float64</span><span class="p">(</span><span class="nx">try</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">ps</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">float64</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">try</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">try</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">count</span> <span class="o">:=</span> <span class="nf">probability</span><span class="p">(</span><span class="nx">try</span><span class="p">,</span> <span class="nx">ch</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">p</span> <span class="o">:=</span> <span class="nb">float64</span><span class="p">(</span><span class="nx">count</span><span class="p">)</span> <span class="o">/</span> <span class="nx">tryf</span>
</span></span><span class="line"><span class="cl"> <span class="nx">ps</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">ps</span><span class="p">,</span> <span class="nx">p</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">p</span> <span class="p"><</span> <span class="nx">min</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">min</span> <span class="p">=</span> <span class="nx">p</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">p</span> <span class="p">></span> <span class="nx">max</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">max</span> <span class="p">=</span> <span class="nx">p</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">sum</span> <span class="o">+=</span> <span class="nx">p</span>
</span></span><span class="line"><span class="cl"> <span class="nx">sum2</span> <span class="o">+=</span> <span class="nx">p</span> <span class="o">*</span> <span class="nx">p</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">"minimum value: %7.5f\n"</span><span class="p">,</span> <span class="nx">min</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">"maximum value: %7.5f\n"</span><span class="p">,</span> <span class="nx">max</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">ave</span> <span class="o">:=</span> <span class="nx">sum</span> <span class="o">/</span> <span class="nx">tryf</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">"average: %7.5f\n"</span><span class="p">,</span> <span class="nx">ave</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">devi</span> <span class="o">:=</span> <span class="nx">math</span><span class="p">.</span><span class="nf">Sqrt</span><span class="p">(</span><span class="nx">sum2</span><span class="o">/</span><span class="nx">tryf</span> <span class="o">-</span> <span class="nx">ave</span><span class="o">*</span><span class="nx">ave</span><span class="p">)</span> <span class="c1">//standard deviation
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">ct</span> <span class="o">:=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">p</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">ps</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">ave</span><span class="o">-</span><span class="nx">devi</span> <span class="o"><=</span> <span class="nx">p</span> <span class="o">&&</span> <span class="nx">p</span> <span class="o"><=</span> <span class="nx">ave</span><span class="o">+</span><span class="nx">devi</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">ct</span><span class="o">++</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">"standard deviation: %7.5f (%4.1f%%)\n"</span><span class="p">,</span> <span class="nx">devi</span><span class="p">,</span> <span class="nb">float64</span><span class="p">(</span><span class="nx">ct</span><span class="p">)</span><span class="o">*</span><span class="mf">100.0</span><span class="o">/</span><span class="nx">tryf</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>10,000回の試行で割合を求める処理をワンセットとしてこれを10,000セット繰り返し,最小値・最大値・平均値・標準偏差を求めている。</p>
<p>これを実行するとこんな感じになる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go run prob/sample/sample.go
</span></span><span class="line"><span class="cl">minimum value: 0.08930
</span></span><span class="line"><span class="cl">maximum value: 0.11160
</span></span><span class="line"><span class="cl">average: 0.09999
</span></span><span class="line"><span class="cl">standard deviation: 0.00299 (67.9%)
</span></span></code></pre></div><p>まぁこんなもんかな(笑)</p>
<h2>感染者と検査キットを定義する。</h2>
<p>ではこの <code>prob</code> パッケージを使って感染者と検査キットを定義してみる。</p>
<p>こんな感じでどうだろう。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">note257</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/spiegel-im-spiegel/mathgirl-problem/prob"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">People</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">infect</span> <span class="o"><-</span><span class="kd">chan</span> <span class="kt">bool</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Person</span> <span class="kt">bool</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">NewPeople</span><span class="p">()</span> <span class="o">*</span><span class="nx">People</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="o">&</span><span class="nx">People</span><span class="p">{</span><span class="nx">infect</span><span class="p">:</span> <span class="nx">prob</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="mf">0.01</span><span class="p">)}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">ppl</span> <span class="o">*</span><span class="nx">People</span><span class="p">)</span> <span class="nf">SelectPersion</span><span class="p">()</span> <span class="nx">Person</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nf">Person</span><span class="p">(</span><span class="o"><-</span><span class="nx">ppl</span><span class="p">.</span><span class="nx">infect</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">psn</span> <span class="nx">Person</span><span class="p">)</span> <span class="nf">Infection</span><span class="p">()</span> <span class="kt">bool</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nb">bool</span><span class="p">(</span><span class="nx">psn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">TestKit</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">probability</span> <span class="o"><-</span><span class="kd">chan</span> <span class="kt">bool</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">NewTestKit</span><span class="p">()</span> <span class="o">*</span><span class="nx">TestKit</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="o">&</span><span class="nx">TestKit</span><span class="p">{</span><span class="nx">probability</span><span class="p">:</span> <span class="nx">prob</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="mf">0.9</span><span class="p">)}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">tk</span> <span class="o">*</span><span class="nx">TestKit</span><span class="p">)</span> <span class="nf">Inspect</span><span class="p">(</span><span class="nx">psn</span> <span class="nx">Person</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">psn</span><span class="p">.</span><span class="nf">Infection</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="o"><-</span><span class="nx">tk</span><span class="p">.</span><span class="nx">probability</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">!</span><span class="o"><-</span><span class="nx">tk</span><span class="p">.</span><span class="nx">probability</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>まず <code>People</code> を定義し,この中から <code>People.SelectPersion()</code> でサンプル <code>Person</code> を選び出す。
このサンプルは1%の確率で感染している。
感染しているかどうかは <code>Person.Infection()</code> 関数で分かる。</p>
<p>判定キットは <code>TestKit</code> で定義され <code>TestKit.Inspect()</code> 関数で検査結果が出る。
このとき</p>
<ul>
<li>(全体の1%存在する)感染者は90%の確率で陽性(true)になる</li>
<li>(全体の99%存在する)非感染者は10%の確率で陽性(true)になる</li>
</ul>
<p>のがポイントである。</p>
<p>このパッケージを使って実際に検査を行ってみる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">probability</span><span class="p">(</span><span class="nx">ppl</span> <span class="o">*</span><span class="nx">note257</span><span class="p">.</span><span class="nx">People</span><span class="p">,</span> <span class="nx">tk</span> <span class="o">*</span><span class="nx">note257</span><span class="p">.</span><span class="nx">TestKit</span><span class="p">,</span> <span class="nx">try</span> <span class="kt">int</span><span class="p">)</span> <span class="kt">float64</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">total</span> <span class="o">:=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl"> <span class="nx">count</span> <span class="o">:=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">try</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">psn</span> <span class="o">:=</span> <span class="nx">ppl</span><span class="p">.</span><span class="nf">SelectPersion</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">tk</span><span class="p">.</span><span class="nf">Inspect</span><span class="p">(</span><span class="nx">psn</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">total</span><span class="o">++</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">psn</span><span class="p">.</span><span class="nf">Infection</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">count</span><span class="o">++</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nb">float64</span><span class="p">(</span><span class="nx">count</span><span class="p">)</span> <span class="o">/</span> <span class="nb">float64</span><span class="p">(</span><span class="nx">total</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">flag</span><span class="p">.</span><span class="nf">Parse</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nx">try</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">strconv</span><span class="p">.</span><span class="nf">Atoi</span><span class="p">(</span><span class="nx">flag</span><span class="p">.</span><span class="nf">Arg</span><span class="p">(</span><span class="mi">0</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Fprintln</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nx">ppl</span> <span class="o">:=</span> <span class="nx">note257</span><span class="p">.</span><span class="nf">NewPeople</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nx">tk</span> <span class="o">:=</span> <span class="nx">note257</span><span class="p">.</span><span class="nf">NewTestKit</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nx">min</span> <span class="o">:=</span> <span class="nb">float64</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">max</span> <span class="o">:=</span> <span class="nb">float64</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">sum</span> <span class="o">:=</span> <span class="nb">float64</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">sum2</span> <span class="o">:=</span> <span class="nb">float64</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">tryf</span> <span class="o">:=</span> <span class="nb">float64</span><span class="p">(</span><span class="nx">try</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">ps</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">float64</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">try</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">try</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">p</span> <span class="o">:=</span> <span class="nf">probability</span><span class="p">(</span><span class="nx">ppl</span><span class="p">,</span> <span class="nx">tk</span><span class="p">,</span> <span class="nx">try</span><span class="o">*</span><span class="mi">10</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">ps</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">ps</span><span class="p">,</span> <span class="nx">p</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">p</span> <span class="p"><</span> <span class="nx">min</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">min</span> <span class="p">=</span> <span class="nx">p</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">p</span> <span class="p">></span> <span class="nx">max</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">max</span> <span class="p">=</span> <span class="nx">p</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">sum</span> <span class="o">+=</span> <span class="nx">p</span>
</span></span><span class="line"><span class="cl"> <span class="nx">sum2</span> <span class="o">+=</span> <span class="nx">p</span> <span class="o">*</span> <span class="nx">p</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">"minimum value: %7.5f\n"</span><span class="p">,</span> <span class="nx">min</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">"maximum value: %7.5f\n"</span><span class="p">,</span> <span class="nx">max</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">ave</span> <span class="o">:=</span> <span class="nx">sum</span> <span class="o">/</span> <span class="nx">tryf</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">"average: %7.5f\n"</span><span class="p">,</span> <span class="nx">ave</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">devi</span> <span class="o">:=</span> <span class="nx">math</span><span class="p">.</span><span class="nf">Sqrt</span><span class="p">(</span><span class="nx">sum2</span><span class="o">/</span><span class="nx">tryf</span> <span class="o">-</span> <span class="nx">ave</span><span class="o">*</span><span class="nx">ave</span><span class="p">)</span> <span class="c1">//standard deviation
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">ct</span> <span class="o">:=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">p</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">ps</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">ave</span><span class="o">-</span><span class="nx">devi</span> <span class="o"><=</span> <span class="nx">p</span> <span class="o">&&</span> <span class="nx">p</span> <span class="o"><=</span> <span class="nx">ave</span><span class="o">+</span><span class="nx">devi</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">ct</span><span class="o">++</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">"standard deviation: %7.5f (%4.1f%%)\n"</span><span class="p">,</span> <span class="nx">devi</span><span class="p">,</span> <span class="nb">float64</span><span class="p">(</span><span class="nx">ct</span><span class="p">)</span><span class="o">*</span><span class="mf">100.0</span><span class="o">/</span><span class="nx">tryf</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>probability()</code> 関数で判定キットの結果が陽性だった人の中で実際に感染している人の割合を返している。</p>
<p>たとえば</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go run note257/sample/sample.go 10000
</span></span></code></pre></div><p>とすれば <code>probability()</code> 関数による試行を10,000回繰り返すわけだ。
私のマシンは遅いので,これを実行するとめっさ時間がかかるのだが,まぁやってみよう。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go run note257/sample/sample.go 10000
</span></span><span class="line"><span class="cl">minimum value: 0.08061
</span></span><span class="line"><span class="cl">maximum value: 0.10279
</span></span><span class="line"><span class="cl">average: 0.09092
</span></span><span class="line"><span class="cl">standard deviation: 0.00272 (68.5%)
</span></span></code></pre></div><p>平均値が少し高めに出たけど,こんなものかな。</p>
<p>結果だけを見れば直感に反するかも知れないが,こうやって実際にコードを書いてみると納得感が強まる。
やっぱプログラマはコードを書いてナンボだよね(笑)</p>
<h2>ブックマーク</h2>
<ul>
<li>
<p><a href="https://cakes.mu/posts/25349">第257回 確率の冒険:《命》に関わる確率(前編)|数学ガールの秘密ノート|結城浩|cakes(ケイクス)</a></p>
<ul>
<li><a href="https://cakes.mu/posts/25465">第258回 確率の冒険:《命》に関わる確率(後編)|数学ガールの秘密ノート|結城浩|cakes(ケイクス)</a></li>
</ul>
</li>
<li>
<p><a href="https://text.baldanders.info/golang/estimate-of-pi-3-gaussian/">モンテカルロ法による円周率の推定(その3 Gaussian)</a></p>
</li>
<li>
<p><a href="https://text.baldanders.info/remark/2018/04/monty-hall-problem/">モンティ・ホール問題で遊ぶ</a></p>
</li>
</ul>
<h2>参考図書</h2>
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B079JLW5YN?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/51QDhrqqEtL._SL160_.jpg" width="113" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B079JLW5YN?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">プログラマの数学 第2版</a></dt>
<dd>結城 浩 (著)</dd>
<dd>SBクリエイティブ 2018-01-16 (Release 2018-02-08)</dd>
<dd>Kindle版</dd>
<dd>B079JLW5YN (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">タイトル通りプログラマ必読書。第2版では機械学習に関する章が付録に追加された。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2018-03-19">2018-03-19</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- プログラマの数学 第2版 -->
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/4621045938?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/51XGP8AFX2L._SL160_.jpg" width="112" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/4621045938?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">いかにして問題をとくか</a></dt>
<dd>G. ポリア (著), Polya,G. (原著), 賢信, 柿内 (翻訳)</dd>
<dd>丸善 1975-04-01</dd>
<dd>単行本</dd>
<dd>4621045938 (ASIN), 9784621045930 (EAN), 4621045938 (ISBN)</dd>
<dd>評価<abbr class="rating fa-sm" title="4"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="far fa-star"></i></abbr></dd>
</dl>
<p class="description">数学書。というか問いの立てかたやものの考え方についての指南書。のようなものかな。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2014-09-26">2014-09-26</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- いかにして問題をとくか -->
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/416Stewy0NS._SL160_.jpg" width="123" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">プログラミング言語Go</a></dt>
<dd>アラン・ドノバン (著), ブライアン・カーニハン (著), 柴田芳樹 (著)</dd>
<dd>丸善出版 2016-06-20 (Release 2021-07-13)</dd>
<dd>Kindle版</dd>
<dd>B099928SJD (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">Kindle 版出た! 一部内容が古びてしまったが,この本は Go 言語の教科書と言ってもいいだろう。感想は<a href="https://text.baldanders.info/remark/2016/07/go-programming-language/" >こちら</a>。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2021-05-22">2021-05-22</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- プログラミング言語Go -->
モンテカルロ法による円周率の推定(その4 PRNG)
tag:text.Baldanders.info,2016-11-20:/golang/estimate-of-pi-4-prng/
2016-11-20T14:33:55+00:00
2024-03-09T11:45:46+00:00
math/rand パッケージでは rand.Source interface を持つ別の擬似乱数生成器を使うことができる。
Spiegel
https://baldanders.info/profile/
<ol>
<li><a href="https://text.baldanders.info/golang/estimate-of-pi/">モンテカルロ法による円周率の推定(その1)</a></li>
<li><a href="https://text.baldanders.info/golang/estimate-of-pi-2-cli/">モンテカルロ法による円周率の推定(その2 CLI)</a></li>
<li><a href="https://text.baldanders.info/golang/estimate-of-pi-3-gaussian/">モンテカルロ法による円周率の推定(その3 Gaussian)</a></li>
<li><a href="https://text.baldanders.info/golang/estimate-of-pi-4-prng/">モンテカルロ法による円周率の推定(その4 PRNG)</a> ← イマココ</li>
</ol>
<p>「モンテカルロ法による円周率の推定」もひととおり終わったので,今回は擬似乱数生成器(pseudo random number generator)の話。</p>
<h2>math/rand の擬似乱数生成器</h2>
<p><a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>では <a href="https://golang.org/pkg/math/rand/" title="rand - The Go Programming Language"><code>math/rand</code></a> パッケージで擬似乱数を取り扱えることは「<a href="https://text.baldanders.info/golang/estimate-of-pi/" title="モンテカルロ法による円周率の推定(その1)">その1</a>」で紹介した通り。
<a href="https://golang.org/pkg/math/rand/" title="rand - The Go Programming Language"><code>math/rand</code></a> パッケージに実装されている擬似乱数生成器はラグ付フィボナッチ法(Lagged Fibonacci Generator)のバリエーションらしい。</p>
<figure lang="en">
<blockquote>
<q>If I am not mistaken again, the generator is an ALFG (Additive Lagged Fibonacci Generator, thats what Wikipedia calls it). Knuth describes the algorithm in Volume 2 of The art of computer programming in section 3.2.2 (around equation 7). Both Wikipedia and Knuth state the parameter combination 607,273 as possible combination with a period length of 2^(e-1) * (2^607-1) where e is the length of the random number in bits.<br>
I actually found a few references examining its properties and it seems to be a good rng so faar, but there is still seems to be a lack of mathematical background and it is fairly easy to get into trouble by not seeding properly.</q>
</blockquote>
<figcaption><div>via <q><a href="https://groups.google.com/forum/#!topic/golang-nuts/RZ1G3_cxMcM">[Announce] A rand package for high quality 64bit random numbers (possibly go2)</a></q></div></figcaption>
</figure>
<p>ラグ付フィボナッチ法は<a href="#lcg">線形合同法(後述)</a>を改善することを目的としたものでフィボナッチ数の生成法を元にしている。</p>
<ul>
<li><a href="https://en.wikipedia.org/wiki/Lagged_Fibonacci_generator">Lagged Fibonacci generator - Wikipedia</a></li>
</ul>
<p>ラグ付フィボナッチ法は以下の式で表される。</p>
<figure>
<blockquote>
\begin{alignat*}{2}
S_{n} \equiv S_{n-j} * S_{n-k} \pmod{m}, & \; & 0 \lt j \lt k
\end{alignat*}
</blockquote></figure>
<p>ラグ付フィボナッチ法は $ * $ 演算子によってバリエーションがあるが <a href="https://golang.org/pkg/math/rand/" title="rand - The Go Programming Language"><code>math/rand</code></a> パッケージの実装では加算を使うため “<strong>Additive</strong> Lagged Fibonacci Generator” ということらしい。
ソースコードで言うとこの部分かな。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// Int63 returns a non-negative pseudo-random 63-bit integer as an int64.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">rng</span> <span class="o">*</span><span class="nx">rngSource</span><span class="p">)</span> <span class="nf">Int63</span><span class="p">()</span> <span class="kt">int64</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rng</span><span class="p">.</span><span class="nx">tap</span><span class="o">--</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">rng</span><span class="p">.</span><span class="nx">tap</span> <span class="p"><</span> <span class="mi">0</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rng</span><span class="p">.</span><span class="nx">tap</span> <span class="o">+=</span> <span class="nx">_LEN</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nx">rng</span><span class="p">.</span><span class="nx">feed</span><span class="o">--</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">rng</span><span class="p">.</span><span class="nx">feed</span> <span class="p"><</span> <span class="mi">0</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rng</span><span class="p">.</span><span class="nx">feed</span> <span class="o">+=</span> <span class="nx">_LEN</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nx">x</span> <span class="o">:=</span> <span class="p">(</span><span class="nx">rng</span><span class="p">.</span><span class="nx">vec</span><span class="p">[</span><span class="nx">rng</span><span class="p">.</span><span class="nx">feed</span><span class="p">]</span> <span class="o">+</span> <span class="nx">rng</span><span class="p">.</span><span class="nx">vec</span><span class="p">[</span><span class="nx">rng</span><span class="p">.</span><span class="nx">tap</span><span class="p">])</span> <span class="o">&</span> <span class="nx">_MASK</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rng</span><span class="p">.</span><span class="nx">vec</span><span class="p">[</span><span class="nx">rng</span><span class="p">.</span><span class="nx">feed</span><span class="p">]</span> <span class="p">=</span> <span class="nx">x</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">x</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2>擬似乱数生成器のバリエーション</h2>
<p><a href="https://golang.org/pkg/math/rand/" title="rand - The Go Programming Language"><code>math/rand</code></a> パッケージでは以下の <a href="https://golang.org/doc/effective_go.html#interfaces_and_types" title="Effective Go - The Go Programming Language">interface</a> を持つ別の擬似乱数生成器を使うことができる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// A Source represents a source of uniformly-distributed
</span></span></span><span class="line"><span class="cl"><span class="c1">// pseudo-random int64 values in the range [0, 1<<63).
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">Source</span> <span class="kd">interface</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nf">Int63</span><span class="p">()</span> <span class="kt">int64</span>
</span></span><span class="line"><span class="cl"> <span class="nf">Seed</span><span class="p">(</span><span class="nx">seed</span> <span class="kt">int64</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>以下に2つほど紹介する。</p>
<h3 id="lcg">線形合同法</h3>
<p>線形合同法(Linear Congruential Generator)は昔の擬似乱数ライブラリでよく使われていたアルゴリズムで,以下の式で表される。</p>
<figure>
<blockquote>
\[
X_{n+1} = (A \times X_{n} + B) \bmod M
\]
</blockquote></figure>
<p>定数 $A$ および $B$ の与え方により幾つかバリエーションがある。</p>
<p>線形合同法のメリットは実装サイズが小さく計算量も少ない点だろうか。
一方デメリットとしては,多次元で疎に分布する性質があり,周期も小さいため乱数を大量に発生させる必要がある科学技術シミュレーションなどには向かないと言われている。
このためメモリサイズが限られるマイクロ・コントローラのようなものでもない限り線形合同法が使われることはなくなった。</p>
<p><a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>でわざわざ線形合同法を実装しているパッケージは少ないのだが,たとえば以下のパッケージがある<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>。</p>
<ul>
<li><a href="https://github.com/davidminor/gorand">davidminor/gorand: Basic golang implementation of a permuted congruential generator for pseudorandom number generation</a></li>
</ul>
<h3 id="mt">Mersenne Twister</h3>
<p><a href="http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/mt.html" title="Mersenne Twister: A random number generator (since 1997/10)">Mersenne Twister</a> とは<a href="http://www.math.sci.hiroshima-u.ac.jp/~m-mat/" title="Makoto Matsumoto Home Page">松本眞</a>・西村拓士両氏によって1996年に発表された擬似乱数生成アルゴリズムである。
他の擬似乱数生成アルゴリズムと比べて以下の特徴があるそうだ。
(「<a href="http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/what-is-mt.html" title="What & how is MT?">Mersenne Twister とは?</a>」より)</p>
<ul>
<li>従来の様々な生成法の欠点を考慮して設計されています。</li>
<li>従来にない長周期,高次元均等分布を持ちます。(周期が $2^{19937}-1$ で<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>、623次元超立方体の中に 均等に分布することが証明されています。)</li>
<li>生成速度がかなり速い。(処理系にもよりますが、パイプライン処理やキャッシュメモリ のあるシステムでは、Cの標準ライブラリの <code>rand()</code> より高速なこと もあります。なお、開発当時には cokus 版は <code>rand()</code> より4倍程度高速でしたが、その後 ANSI-C の <code>rand()</code> が LCG 法から lagged-fibonacci に 変更されたこともあり、2002年現在 rand と MT の速度差はあまりありません。)</li>
<li>メモリ効率が良い。(32ビット以上のマシン用に設計された <code>mt19937.c</code> は、 624ワードのワーキングメモリを消費するだけです。 1ワードは32ビット長とします。</li>
</ul>
<p>ちなみに <a href="http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/mt.html" title="Mersenne Twister: A random number generator (since 1997/10)">Mersenne Twister</a> の<a href="http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/MT2002/license.html" title="Mersenne Twisterの商用について">オリジナル・コードは BSD ライセンスで提供</a>されている。</p>
<p><a href="https://github.com/MersenneTwister-Lab" title="MersenneTwister-Lab">公式のリポジトリ</a>には C/C++ による実装のみのようだが, <a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>で実装している人もいるようである。</p>
<ul>
<li><a href="https://github.com/seehuhn/mt19937">seehuhn/mt19937: An implementation of Takuji Nishimura’s and Makoto Matsumoto’s Mersenne Twister pseudo random number generator in Go.</a></li>
<li><a href="https://github.com/nutterts/randgen">nutterts/randgen: Pseudo Random Number Generators implementing the Go(lang) math/rand.Source Interface</a></li>
<li><a href="https://github.com/farces/mt19937_64">farces/mt19937_64: Mersenne Twister (int64) for Go</a></li>
<li><a href="https://github.com/cuixin/goalg">cuixin/goalg: golang algorithms</a></li>
<li><a href="https://github.com/cpmech/gosl">cpmech/gosl: Go scientific library</a> : <a href="http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/" title="SIMD-oriented Fast Mersenne Twister">SFMT</a> や <a href="http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/TINYMT/" title="Tiny Mersenne Twister">TinyMT</a> に対応。オリジナルのコードを <a href="https://golang.org/cmd/cgo/" title="cgo - The Go Programming Language">cgo</a> で結合しているのでクロス環境では注意</li>
</ul>
<h2>擬似乱数生成器を組み込む</h2>
<p>では,先ほど紹介した擬似乱数生成器を今回のコードに組み込んでみることにしよう。
こんな感じ。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">gencmplx</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"math/rand"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/davidminor/gorand/lcg"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/seehuhn/mt19937"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">//RNGs is kind of RNG
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">RNGs</span> <span class="kt">int</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">const</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="nx">NULL</span> <span class="nx">RNGs</span> <span class="p">=</span> <span class="kc">iota</span>
</span></span><span class="line"><span class="cl"> <span class="nx">GO</span>
</span></span><span class="line"><span class="cl"> <span class="nx">LCG</span>
</span></span><span class="line"><span class="cl"> <span class="nx">MT</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">//NewRndSource returns Source of random numbers
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">NewRndSource</span><span class="p">(</span><span class="nx">rng</span> <span class="nx">RNGs</span><span class="p">,</span> <span class="nx">seed</span> <span class="kt">int64</span><span class="p">)</span> <span class="nx">rand</span><span class="p">.</span><span class="nx">Source</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">switch</span> <span class="nx">rng</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nx">LCG</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">lcg</span><span class="p">.</span><span class="nf">NewLcg64</span><span class="p">(</span><span class="nb">uint64</span><span class="p">(</span><span class="nx">seed</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nx">MT</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="nx">mt</span> <span class="o">:=</span> <span class="nx">mt19937</span><span class="p">.</span><span class="nf">New</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nx">mt</span><span class="p">.</span><span class="nf">Seed</span><span class="p">(</span><span class="nx">seed</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">mt</span>
</span></span><span class="line"><span class="cl"> <span class="k">default</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">rand</span><span class="p">.</span><span class="nf">NewSource</span><span class="p">(</span><span class="nx">seed</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>gencmplx.NewRndSource()</code> 関数で <code>rand.Source</code> オブジェクトを生成する<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>。
これを「<a href="https://text.baldanders.info/golang/estimate-of-pi/" title="モンテカルロ法による円周率の推定(その1)">その1</a>」で作った <code>gencmplx.New()</code> 関数に渡せばよい。</p>
<p>CLI は以下のように調整してみた。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go run main.go estmt --help
</span></span><span class="line"><span class="cl">Estimate of Pi with Monte Carlo method.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Usage:
</span></span><span class="line"><span class="cl"> pi estmt [flags]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Flags:
</span></span><span class="line"><span class="cl"> -e, --ecount int Count of estimate (default 100)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Global Flags:
</span></span><span class="line"><span class="cl"> -p, --pcount int Count of points (default 10000)
</span></span><span class="line"><span class="cl"> -r, --rsource string Source of RNG (GO/LCG/MT) (default "GO")
</span></span></code></pre></div><p>で,それぞれの擬似乱数生成器で評価を行ってみようと思ったのだが,今回のケースに限ってはあまり違いが出ないようである。</p>
<p>まずは<a href="#lcg">線形合同法</a>の場合。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go run main.go estmt -e 10000 -p 100000 -r LCG > estmt100k-lcg.dat
</span></span><span class="line"><span class="cl">random number generator: LCG
</span></span><span class="line"><span class="cl">minimum value: 3.12204
</span></span><span class="line"><span class="cl">maximum value: 3.16224
</span></span><span class="line"><span class="cl">average value: 3.14164
</span></span><span class="line"><span class="cl">standard deviation: 0.00524 (68.3%)
</span></span></code></pre></div><figure style='margin:0 auto;text-align:center;'><a href="./histogram-lcg.png"><img src="./histogram-lcg.png" srcset="./histogram-lcg.png 611w" sizes="(min-width:600px) 500px, 80vw" alt="" loading="lazy"></a></figure>
<figure style='margin:0 auto;text-align:center;'><a href="./qq100k-plot-lcg.png"><img src="./qq100k-plot-lcg.png" srcset="./qq100k-plot-lcg.png 611w" sizes="(min-width:600px) 500px, 80vw" alt="" loading="lazy"></a></figure>
<p>次は <a href="#mt">Mersenne Twister</a> の場合。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go run main.go estmt -e 10000 -p 100000 -r MT > estmt100k-mt.dat
</span></span><span class="line"><span class="cl">random number generator: MT
</span></span><span class="line"><span class="cl">minimum value: 3.12380
</span></span><span class="line"><span class="cl">maximum value: 3.16140
</span></span><span class="line"><span class="cl">average value: 3.14165
</span></span><span class="line"><span class="cl">standard deviation: 0.00517 (67.8%)
</span></span></code></pre></div><figure style='margin:0 auto;text-align:center;'><a href="./histogram-mt.png"><img src="./histogram-mt.png" srcset="./histogram-mt.png 611w" sizes="(min-width:600px) 500px, 80vw" alt="" loading="lazy"></a></figure>
<figure style='margin:0 auto;text-align:center;'><a href="./qq100k-plot-mt.png"><img src="./qq100k-plot-mt.png" srcset="./qq100k-plot-mt.png 611w" sizes="(min-width:600px) 500px, 80vw" alt="" loading="lazy"></a></figure>
<p>もっと多次元だったりすると変わってくるのかなぁ。</p>
<h2>暗号技術用途の乱数生成器</h2>
<p><a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>では暗号技術用途の乱数として <a href="https://golang.org/pkg/crypto/rand/" title="rand - The Go Programming Language"><code>crypto/rand</code></a> パッケージが用意されている。
これは <a href="https://golang.org/pkg/math/rand/" title="rand - The Go Programming Language"><code>math/rand</code></a> とは互換性がない。</p>
<p>具体的には,UNIX 系のプラットフォームでは乱数生成に <code>/dev/urandom</code> デバイスを参照している<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup>。
また Windows プラットフォームでは <a href="https://technet.microsoft.com/ja-jp/library/cc734124.aspx">CryptoAPI 2.0</a> の <a href="https://msdn.microsoft.com/ja-jp/library/windows/desktop/aa379942.aspx" title="CryptGenRandom function"><code>CryptGenRandom</code></a> 関数を使っている<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup>。</p>
<p>そもそも暗号技術用途の乱数生成器は科学技術シミュレーションやゲームで使う擬似乱数生成器とは要件が異なる。</p>
<ul>
<li><a href="https://tools.ietf.org/html/rfc4086">RFC 4086 - Randomness Requirements for Security</a> (<a href="https://www.ipa.go.jp/security/rfc/RFC4086JA.html" title="セキュリティのための乱雑性についての要件">IPA による日本語訳</a>)</li>
<li><span><a href="http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-90Ar1.pdf">NIST Special Publication 800-90A Revision 1: Recommendation for Random Number Generation Using Deterministic Random Bit Generators <sup><i class="far fa-file-pdf"></i></sup></a></span></li>
</ul>
<figure>
<blockquote>
<q>従前の観点から統計的にテストされた乱雑性は、セキュリティ用途に要求される予測困難性と同等では<strong>ありません</strong>。<br>
例えば、(CRC Standard Mathematical Tables からのランダムテーブルのような)広く利用可能な一定のシーケンスの利用は、攻撃者に対して非常に弱いです。これを学習したり、推測する攻撃者は、容易に(過去・未来を問わず)そのシーケンス [CRC] に基づいて、すべてのセキュリティを破ることができます。他の例として、AES を 1, 2, 3 ... のような連続した整数を暗号化する一定の鍵と共に使うことは、優れた統計的乱雑性をもつが予測可能な出力を作り出します。他方、6 面のサイコロを連続して転がして、その結果の値を ASCII にエンコードすることは、実質的に予測困難なコンポーネントをもちながらも「統計的に貧弱な出力」を作り出します。それゆえ、「統計的テストの合否は、『何かが予測不可能であるか否か、あるいは、予測可能であるか否か』を表さないこと」に注意してください。</q>
</blockquote>
<figcaption><div><q><a href="https://www.ipa.go.jp/security/rfc/RFC4086JA.html">RFC 4086 - セキュリティのための乱雑性についての要件</a></q>より</div></figcaption>
</figure>
<p>暗号技術用途の乱数生成器は,暗号分野においては中核技術のひとつであるが,一度に大量の乱数を生成させる必要のある科学技術シミュレーションなどの用途には向かない。
上手く使い分けてほしい。</p>
<h2>ブックマーク</h2>
<ul>
<li><a href="https://text.baldanders.info/remark/2016/03/mersenne-twister/">Mersenne Twister に関する覚え書き</a></li>
<li><a href="http://www.pcg-random.org/">PCG, A Family of Better Random Number Generators | PCG, A Better Random Number Generator</a></li>
<li><a href="http://news.mynavi.jp/news/2014/03/11/037/">/dev/randomではなく/dev/urandomを使うべき理由? | マイナビニュース</a></li>
<li><a href="https://qiita.com/po3rin/items/f28a8ce322cb1bd95175">与えられた重みに従ってランダムに値を返す「Weighted Random Selection」をGoで実装する! - Qiita</a></li>
<li><a href="https://zenn.dev/yoheimuta/articles/6de5816f5d721c">ある範囲に収まる乱数を得るために剰余(モジュロ)演算を書くとき、レビューするときに意識すること</a></li>
</ul>
<p><a href="https://text.baldanders.info/golang/bookmark/">Go 言語に関するブックマーク集はこちら</a>。</p>
<h2>参考図書</h2>
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/416Stewy0NS._SL160_.jpg" width="123" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">プログラミング言語Go</a></dt>
<dd>アラン・ドノバン (著), ブライアン・カーニハン (著), 柴田芳樹 (著)</dd>
<dd>丸善出版 2016-06-20 (Release 2021-07-13)</dd>
<dd>Kindle版</dd>
<dd>B099928SJD (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">Kindle 版出た! 一部内容が古びてしまったが,この本は Go 言語の教科書と言ってもいいだろう。感想は<a href="https://text.baldanders.info/remark/2016/07/go-programming-language/" >こちら</a>。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2021-05-22">2021-05-22</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- プログラミング言語Go -->
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B015643CPE?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/51t6yHHVwEL._SL160_.jpg" width="113" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B015643CPE?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">暗号技術入門 第3版 秘密の国のアリス</a></dt>
<dd>結城 浩 (著)</dd>
<dd>SBクリエイティブ 2015-08-25 (Release 2015-09-17)</dd>
<dd>Kindle版</dd>
<dd>B015643CPE (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">SHA-3 や Bitcoin/Blockchain など新しい知見や技術要素を大幅追加。暗号技術を使うだけならこれ1冊でとりあえず無問題。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2015-09-20">2015-09-20</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- 暗号技術入門 第3版 -->
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>ただし <a href="https://github.com/davidminor/gorand" title="davidminor/gorand: Basic golang implementation of a permuted congruential generator for pseudorandom number generation"><code>github.com/davidminor/gorand</code></a><code>/lcg</code> には不具合があって <code>Int63()</code> 関数で負の値を出力する場合がある。とりあえず fork 版の <a href="https://github.com/davidminor/gorand" title="spiegel-im-spiegel/gorand: Basic golang implementation of a permuted congruential generator for pseudorandom number generation"><code>github.com/spiegel-im-spiegel/gorand</code></a><code>/lcg</code> で修正している。 Pull request も出したけど,古いコードだし,もうメンテしてないかなぁ。 <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:2">
<p>$2^{19937}-1$ はメルセンヌ素数で <a href="http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/mt.html" title="Mersenne Twister: A random number generator (since 1997/10)">Mersenne Twister</a> の名前の由来になっている。 <a href="http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/mt.html" title="Mersenne Twister: A random number generator (since 1997/10)">Mersenne Twister</a> では周期サイズごとに複数の実装があるが, $2^{19937}-1$ がポピュラーな実装として広く使われているようだ。 <a href="#fnref:2" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:3">
<p><a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>におけるオブジェクトの多態性については「<a href="https://text.baldanders.info/golang/object-oriented-programming/">Go 言語における「オブジェクト」</a>」を参考にどうぞ。 <a href="#fnref:3" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:4">
<p><code>/dev/urandom</code> はハードウェア・デバイスから十分なエントロピー源が得られない場合は内部で疑似乱数生成器を使用する。このため一時は <code>/dev/urandom</code> の脆弱性が疑われたが,現時点では事実上は問題ないとされている。一方で,スマートデバイスのような場合はハードウェア・デバイスからのエントロピー源だけでは外部から推測され易いため,性能のよい疑似乱数生成器を組み合わせるほうが有効になる場合もあるようだ。 <a href="#fnref:4" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:5">
<p><a href="https://msdn.microsoft.com/ja-jp/library/windows/desktop/aa379942.aspx" title="CryptGenRandom function"><code>CryptGenRandom</code></a> 関数の内部実装は公開されていないが,やはりキーボードやマウス等のデバイスの挙動をエントロピー源とし, NIST の SP800-90 勧告に従った実装をしているようである。余談だが SP800-90 は乱数生成の一部のアルゴリズムで脆弱性が発見され(これがまた NSA 絡みだったものだから大騒ぎになった),現在は修正版の <a href="http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-90Ar1.pdf" title="Recommendation for Random Number Generation Using Deterministic Random Bit Generators">SP800-90A Revision 1</a>が発行されている。(参考:<a href="http://www.cryptrec.go.jp/topics/cryptrec_20131106_dual_ec_drbg.html">擬似乱数生成アルゴリズム Dual_EC_DRBG について</a>) <a href="#fnref:5" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
モンテカルロ法による円周率の推定(その3 Gaussian)
tag:text.Baldanders.info,2016-11-14:/golang/estimate-of-pi-3-gaussian/
2016-11-14T11:50:56+00:00
2021-08-12T21:22:05+00:00
さて,前回書いたコードを利用していよいよ円周率の推定結果を評価してみる。
Spiegel
https://baldanders.info/profile/
<ol>
<li><a href="https://text.baldanders.info/golang/estimate-of-pi/">モンテカルロ法による円周率の推定(その1)</a></li>
<li><a href="https://text.baldanders.info/golang/estimate-of-pi-2-cli/">モンテカルロ法による円周率の推定(その2 CLI)</a></li>
<li><a href="https://text.baldanders.info/golang/estimate-of-pi-3-gaussian/">モンテカルロ法による円周率の推定(その3 Gaussian)</a> ← イマココ</li>
<li><a href="https://text.baldanders.info/golang/estimate-of-pi-4-prng/">モンテカルロ法による円周率の推定(その4 PRNG)</a></li>
</ol>
<h2>推定結果の分布</h2>
<p>さて,<a href="https://text.baldanders.info/golang/estimate-of-pi-2-cli/" title="モンテカルロ法による円周率の推定(その2 CLI)">前回</a>書いたコードを利用して,いよいよ円周率の推定結果を評価してみる。</p>
<ul>
<li><a href="https://github.com/spiegel-im-spiegel/pi">spiegel-im-spiegel/pi: Estimate of Pi with Monte Carlo method.</a></li>
</ul>
<p>CLI はこんな感じになった。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go run main.go estmt --help
</span></span><span class="line"><span class="cl">Estimate of Pi with Monte Carlo method.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Usage:
</span></span><span class="line"><span class="cl"> pi estmt [flags]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Flags:
</span></span><span class="line"><span class="cl"> -e, --ecount int Count of estimate (default 100)
</span></span><span class="line"><span class="cl"> -p, --pcount int Count of points (default 10000)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Global Flags:
</span></span><span class="line"><span class="cl"> --config string config file (default is $HOME/.pi.yaml)
</span></span></code></pre></div><p>まずは円周率の推定処理を10,000回繰り返してみる。
また推定処理のためのランダムな点の数 $n$ を1,000個,10,000個,100,000個と変えて実行してみようか。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go run main.go estmt -e 10000 -p 1000 > estmt1k.dat
</span></span><span class="line"><span class="cl">minimum value: 2.94800
</span></span><span class="line"><span class="cl">maximum value: 3.31600
</span></span><span class="line"><span class="cl">average value: 3.14199
</span></span><span class="line"><span class="cl">standard deviation: 0.05104 (69.9%)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">$ go run main.go estmt -e 10000 -p 10000 > estmt10k.dat
</span></span><span class="line"><span class="cl">minimum value: 3.07240
</span></span><span class="line"><span class="cl">maximum value: 3.20360
</span></span><span class="line"><span class="cl">average value: 3.14178
</span></span><span class="line"><span class="cl">standard deviation: 0.01654 (68.0%)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">$ go run main.go estmt -e 10000 -p 100000 > estmt100k.dat
</span></span><span class="line"><span class="cl">minimum value: 3.12360
</span></span><span class="line"><span class="cl">maximum value: 3.16184
</span></span><span class="line"><span class="cl">average value: 3.14163
</span></span><span class="line"><span class="cl">standard deviation: 0.00518 (68.3%)
</span></span></code></pre></div><p>最後のはさすがに時間がかかるので,お茶でも飲みながら優雅に待ちましょう(笑)</p>
<p>標準エラー出力に最小値,最大値,平均値($E$),標準偏差($\sigma$)を出力してみた。
標準偏差の後ろの括弧は $\left[ E-\sigma, E+\sigma \right]$ の範囲にある推定値の割合を示したものだ。</p>
<p>円周率の推定処理の試行回数が十分大きいなら推定値の分布は正規分布(またはガウス分布)に近似できる筈である。
(以下の<a href="https://commons.wikimedia.org/wiki/File:Standard_deviation_diagram.svg" title="File:Standard deviation diagram.svg - Wikimedia Commons">図は Wikimedia Commons のもの</a>を拝借した。 <a href="https://creativecommons.org/licenses/by/2.5/" title="Creative Commons — Attribution 2.5 Generic — CC BY 2.5">CC-BY-2.5 Generic</a> で公開されている)
<figure style='margin:0 auto;text-align:center;'><a href="https://commons.wikimedia.org/wiki/File:Standard_deviation_diagram.svg"><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/8/8c/Standard_deviation_diagram.svg/640px-Standard_deviation_diagram.svg.png" srcset="https://upload.wikimedia.org/wikipedia/commons/thumb/8/8c/Standard_deviation_diagram.svg/640px-Standard_deviation_diagram.svg.png 640w" sizes="(min-width:600px) 500px, 80vw" alt="normal distribution from Wikimedia" loading="lazy"></a><figcaption><div><a href="https://commons.wikimedia.org/wiki/File:Standard_deviation_diagram.svg">normal distribution from Wikimedia</a></div></figcaption>
</figure></p>
<p>そこで $n=100,000$ のときの推定結果についてヒストグラムを描いてみる。
幸いなことに <a href="http://www.gnuplot.info/" title="gnuplot homepage">gnuplot</a> では簡単にヒストグラムを作図できる。
こんな感じ(階級幅を0.001としている)。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">gnuplot> filter(x,y)=int(x/y)*y
</span></span><span class="line"><span class="cl">gnuplot> plot "estmt100k.dat" u (filter($1,0.001)):(1) smooth frequency with boxes lc rgb "black"
</span></span></code></pre></div><p><a href="http://www.gnuplot.info/" title="gnuplot homepage">gnuplot</a> の出力結果はこんな感じ。</p>
<figure style='margin:0 auto;text-align:center;'><a href="./histogram.png"><img src="./histogram.png" srcset="./histogram.png 640w" sizes="(min-width:600px) 500px, 80vw" alt="" loading="lazy"></a></figure>
<p>んー。
まぁ正規分布っぽい?</p>
<p>もうひとつ,正規確率の分布を調べてみよう。
これも <a href="http://www.gnuplot.info/" title="gnuplot homepage">gnuplot</a> で描こうと思ったけど,少し面倒そうなので,ズルして以下を参考に <a href="https://www.libreoffice.org/" title="Home | LibreOffice - Free Office Suite - Fun Project - Fantastic People">LibreOffice</a> の Calc で描くことにした<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>。</p>
<ul>
<li><a href="http://hitorimarketing.net/tools/normal-quantile-quantile-plot.html">正規確率プロット(Normal Q-Q Plot)の作成 with Excel</a></li>
</ul>
<p>とりあえず結果だけ。</p>
<figure style='margin:0 auto;text-align:center;'><a href="./qq-plot.png"><img src="./qq-plot.png" srcset="./qq-plot.png 653w" sizes="(min-width:600px) 500px, 80vw" alt="" loading="lazy"></a></figure>
<p>プロットが直線状に並んでいれば正規分布であると言える。
図から見る限り,概ね正規分布になっているようである。</p>
<p>しまった。
ここまで <a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>が全然出てこなかった。
まぁ,いいや。
多分あと1回続きます。</p>
<h2>おまけ:誤差評価</h2>
<p>モンテカルロ法を使ってどの程度の精度で円周率が求まるかの考察については以下が参考になる。</p>
<ul>
<li><a href="http://ruby.kyoto-wu.ac.jp/info-com/NumericalModels/RandomProcess/estimateMCmodel.html">モンテカルロ法の誤差を考える</a></li>
</ul>
<p>これも横着して結果だけを拝借すると, $n=100,000$ で推定を行った場合の値の分布は,中央値を $\pi$,99%信頼区間を $\frac{4.230}{\sqrt{100,000}} = 0.013$ として, $\left[ \pi - 0.013, \pi + 0.013 \right]$ の範囲になるようだ。</p>
<h2>おまけの追記: 正規確率の分布図について</h2>
<p>正規確率の分布図(Q-Q プロット)を描くのに毎回 Excel や Calc を使うのもどうかという気がしたので,こちらのプログラム側であらかじめ計算して,結果のプロット・データを <a href="http://www.gnuplot.info/" title="gnuplot homepage">gnuplot</a> に食わせるよう考えてみる。</p>
<p>まず <code>qq</code> サブコマンドを追加し,この <code>qq</code> サブコマンド時にデータファイルを読み込んで Q-Q プロットの計算を行うように CLI を変更する。(ついでに他のオプションも整理した)</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go run main.go qq --help
</span></span><span class="line"><span class="cl">make Q-Q plot data.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Usage:
</span></span><span class="line"><span class="cl"> pi qq [data file] [flags]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Global Flags:
</span></span><span class="line"><span class="cl"> -p, --pcount int Count of points (default 10000)
</span></span></code></pre></div><p>実際の処理部分はこんな感じ。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">//Execute output Q-Q plot data.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">Execute</span><span class="p">(</span><span class="nx">cxt</span> <span class="o">*</span><span class="nx">Context</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">scanner</span> <span class="o">:=</span> <span class="nx">bufio</span><span class="p">.</span><span class="nf">NewScanner</span><span class="p">(</span><span class="nx">cxt</span><span class="p">.</span><span class="nx">ui</span><span class="p">.</span><span class="nf">Reader</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"> <span class="nx">pis</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">float64</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">scanner</span><span class="p">.</span><span class="nf">Scan</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">pi</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">strconv</span><span class="p">.</span><span class="nf">ParseFloat</span><span class="p">(</span><span class="nx">scanner</span><span class="p">.</span><span class="nf">Text</span><span class="p">(),</span> <span class="mi">64</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">Wrap</span><span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="s">"invalid data"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">pis</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">pis</span><span class="p">,</span> <span class="nx">pi</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">ecf</span> <span class="o">:=</span> <span class="nb">float64</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="nx">pis</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nx">sort</span><span class="p">.</span><span class="nf">Float64s</span><span class="p">(</span><span class="nx">pis</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span><span class="p">,</span> <span class="nx">pi</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">pis</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rank</span> <span class="o">:=</span> <span class="p">(</span><span class="nb">float64</span><span class="p">(</span><span class="nx">i</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span> <span class="o">-</span> <span class="mf">0.5</span><span class="p">)</span> <span class="o">/</span> <span class="nx">ecf</span>
</span></span><span class="line"><span class="cl"> <span class="nx">cxt</span><span class="p">.</span><span class="nx">ui</span><span class="p">.</span><span class="nf">Outputln</span><span class="p">(</span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">"%v\t%v"</span><span class="p">,</span> <span class="nf">qnorm</span><span class="p">(</span><span class="nx">rank</span><span class="p">),</span> <span class="nx">pi</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>qnorm()</code> 関数は標準正規分布に対する累積分布関数の逆関数の値を返すのだが(Excel の <code>NORM.S.INV</code> 関数相当),<a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>で書かれた適当なパッケージが見つからなかったので(もしあれば誰か教えて)以下のページのコードを <a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>用に書き直した。</p>
<ul>
<li><a href="http://rangevoting.org/Qnorm.html">RangeVoting.org - Java normal distribution calculator II</a></li>
</ul>
<p>実際にはこんな感じ<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">//qnorm function refers to http://rangevoting.org/Qnorm.html
</span></span></span><span class="line"><span class="cl"><span class="c1">// This function is licensed under GNU GPL version 3 or later.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">qnorm</span><span class="p">(</span><span class="nx">p</span> <span class="kt">float64</span><span class="p">)</span> <span class="kt">float64</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kd">const</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="nx">split</span> <span class="p">=</span> <span class="mf">0.42</span>
</span></span><span class="line"><span class="cl"> <span class="nx">a0</span> <span class="p">=</span> <span class="mf">2.50662823884</span>
</span></span><span class="line"><span class="cl"> <span class="nx">a1</span> <span class="p">=</span> <span class="o">-</span><span class="mf">18.61500062529</span>
</span></span><span class="line"><span class="cl"> <span class="nx">a2</span> <span class="p">=</span> <span class="mf">41.39119773534</span>
</span></span><span class="line"><span class="cl"> <span class="nx">a3</span> <span class="p">=</span> <span class="o">-</span><span class="mf">25.44106049637</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b1</span> <span class="p">=</span> <span class="o">-</span><span class="mf">8.47351093090</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b2</span> <span class="p">=</span> <span class="mf">23.08336743743</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b3</span> <span class="p">=</span> <span class="o">-</span><span class="mf">21.06224101826</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b4</span> <span class="p">=</span> <span class="mf">3.13082909833</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c0</span> <span class="p">=</span> <span class="o">-</span><span class="mf">2.78718931138</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c1</span> <span class="p">=</span> <span class="o">-</span><span class="mf">2.29796479134</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c2</span> <span class="p">=</span> <span class="mf">4.85014127135</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c3</span> <span class="p">=</span> <span class="mf">2.32121276858</span>
</span></span><span class="line"><span class="cl"> <span class="nx">d1</span> <span class="p">=</span> <span class="mf">3.54388924762</span>
</span></span><span class="line"><span class="cl"> <span class="nx">d2</span> <span class="p">=</span> <span class="mf">1.63706781897</span>
</span></span><span class="line"><span class="cl"> <span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nx">q</span> <span class="o">:=</span> <span class="nx">p</span> <span class="o">-</span> <span class="mf">0.5</span>
</span></span><span class="line"><span class="cl"> <span class="nx">ppnd</span> <span class="o">:=</span> <span class="nb">float64</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">math</span><span class="p">.</span><span class="nf">Abs</span><span class="p">(</span><span class="nx">q</span><span class="p">)</span> <span class="o"><=</span> <span class="nx">split</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">r</span> <span class="o">:=</span> <span class="nx">q</span> <span class="o">*</span> <span class="nx">q</span>
</span></span><span class="line"><span class="cl"> <span class="nx">ppnd</span> <span class="p">=</span> <span class="nx">q</span> <span class="o">*</span> <span class="p">(((</span><span class="nx">a3</span><span class="o">*</span><span class="nx">r</span><span class="o">+</span><span class="nx">a2</span><span class="p">)</span><span class="o">*</span><span class="nx">r</span><span class="o">+</span><span class="nx">a1</span><span class="p">)</span><span class="o">*</span><span class="nx">r</span> <span class="o">+</span> <span class="nx">a0</span><span class="p">)</span> <span class="o">/</span> <span class="p">((((</span><span class="nx">b4</span><span class="o">*</span><span class="nx">r</span><span class="o">+</span><span class="nx">b3</span><span class="p">)</span><span class="o">*</span><span class="nx">r</span><span class="o">+</span><span class="nx">b2</span><span class="p">)</span><span class="o">*</span><span class="nx">r</span><span class="o">+</span><span class="nx">b1</span><span class="p">)</span><span class="o">*</span><span class="nx">r</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">r</span> <span class="o">:=</span> <span class="nx">p</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">q</span> <span class="p">></span> <span class="mi">0</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">r</span> <span class="p">=</span> <span class="mi">1</span> <span class="o">-</span> <span class="nx">p</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">r</span> <span class="p">></span> <span class="mi">0</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">r</span> <span class="p">=</span> <span class="nx">math</span><span class="p">.</span><span class="nf">Sqrt</span><span class="p">(</span><span class="o">-</span><span class="nx">math</span><span class="p">.</span><span class="nf">Log</span><span class="p">(</span><span class="nx">r</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="nx">ppnd</span> <span class="p">=</span> <span class="p">(((</span><span class="nx">c3</span><span class="o">*</span><span class="nx">r</span><span class="o">+</span><span class="nx">c2</span><span class="p">)</span><span class="o">*</span><span class="nx">r</span><span class="o">+</span><span class="nx">c1</span><span class="p">)</span><span class="o">*</span><span class="nx">r</span> <span class="o">+</span> <span class="nx">c0</span><span class="p">)</span> <span class="o">/</span> <span class="p">((</span><span class="nx">d2</span><span class="o">*</span><span class="nx">r</span><span class="o">+</span><span class="nx">d1</span><span class="p">)</span><span class="o">*</span><span class="nx">r</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">q</span> <span class="p"><</span> <span class="mi">0</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">ppnd</span> <span class="p">=</span> <span class="o">-</span><span class="nx">ppnd</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">ppnd</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>では早速動かしてみよう。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go run main.go qq estmt100k.dat > qq100k.dat
</span></span></code></pre></div><p>生成した <a href="./qq100k.dat"><code>qq100k.dat</code></a> ファイルを <a href="http://www.gnuplot.info/" title="gnuplot homepage">gnuplot</a> に食わせる。
こんな感じでいいだろう。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">gnuplot> unset key
</span></span><span class="line"><span class="cl">gnuplot> set style line 1 pointsize 0.1 pointtype 7 linecolor rgb "black"
</span></span><span class="line"><span class="cl">gnuplot> plot "qq100k.dat" linestyle 1
</span></span></code></pre></div><p>結果はこんな感じ。</p>
<figure style='margin:0 auto;text-align:center;'><a href="./qq-plot2.png"><img src="./qq-plot2.png" srcset="./qq-plot2.png 640w" sizes="(min-width:600px) 500px, 80vw" alt="" loading="lazy"></a></figure>
<p>ついでにこの分布図にフィットする直線 $y=ax+b$ の $a, b$ 値を調べてみる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">gnuplot> f(x)=a*x+b
</span></span><span class="line"><span class="cl">gnuplot> fit f(x) "qq100k.dat" via a,b
</span></span><span class="line"><span class="cl">iter chisq delta/lim lambda a b
</span></span><span class="line"><span class="cl"> 0 5.5761271099e+04 0.00e+00 1.00e+00 1.000000e+00 1.000000e+00
</span></span><span class="line"><span class="cl"> 1 6.0252530865e-04 -9.25e+12 1.00e-01 5.277253e-03 3.141418e+00
</span></span><span class="line"><span class="cl"> 2 4.5071534394e-05 -1.24e+06 1.00e-02 5.177775e-03 3.141632e+00
</span></span><span class="line"><span class="cl"> 3 4.5071534393e-05 -1.23e-06 1.00e-03 5.177775e-03 3.141632e+00
</span></span><span class="line"><span class="cl">iter chisq delta/lim lambda a b
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">After 3 iterations the fit converged.
</span></span><span class="line"><span class="cl">final sum of squares of residuals : 4.50715e-005
</span></span><span class="line"><span class="cl">rel. change during last iteration : -1.23364e-011
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">degrees of freedom (FIT_NDF) : 9998
</span></span><span class="line"><span class="cl">rms of residuals (FIT_STDFIT) = sqrt(WSSR/ndf) : 6.71421e-005
</span></span><span class="line"><span class="cl">variance of residuals (reduced chisquare) = WSSR/ndf : 4.50806e-009
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Final set of parameters Asymptotic Standard Error
</span></span><span class="line"><span class="cl">======================= ==========================
</span></span><span class="line"><span class="cl">a = 0.00517777 +/- 6.715e-007 (0.01297%)
</span></span><span class="line"><span class="cl">b = 3.14163 +/- 6.714e-007 (2.137e-005%)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">correlation matrix of the fit parameters:
</span></span><span class="line"><span class="cl"> a b
</span></span><span class="line"><span class="cl">a 1.000
</span></span><span class="line"><span class="cl">b 0.000 1.000
</span></span></code></pre></div><p>ここで $a$ が標準偏差, $b$ が平均値にマッチしている点に注目。
分布図と上の直線を重ねあわせるとこうなる。</p>
<figure style='margin:0 auto;text-align:center;'><a href="./qq-plot2b.png"><img src="./qq-plot2b.png" srcset="./qq-plot2b.png 640w" sizes="(min-width:600px) 500px, 80vw" alt="" loading="lazy"></a></figure>
<p>んー。
こんなもんかな。</p>
<p>そうそう。
<code>qq</code> サブコマンドは,フィルタとしても機能するので</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go run main.go estmt -e 100 -p 10000 | go run main.go qq > qq.dat
</span></span><span class="line"><span class="cl">minimum value: 3.09640
</span></span><span class="line"><span class="cl">maximum value: 3.18600
</span></span><span class="line"><span class="cl">average value: 3.14158
</span></span><span class="line"><span class="cl">standard deviation: 0.01654 (68.0%)
</span></span></code></pre></div><p>といった感じにパイプでつなぐこともできる。</p>
<h2>ブックマーク</h2>
<ul>
<li><a href="http://qiita.com/iwiwi/items/4c7635d4c84bc785e47a">gnuplot でヒストグラム(頻度分布図)を描画する - Qiita</a></li>
<li><a href="http://qiita.com/kenmatsu4/items/59605dc745707e8701e0">【統計学】Q-Qプロットの仕組みをアニメーションで理解する。 - Qiita</a></li>
<li><a href="http://dsl4.eee.u-ryukyu.ac.jp/DOCS/gnuplot/node180.html">簡単な例題</a>:最小2乗法でデータに曲線や曲面をあてはめる</li>
<li><a href="http://www.biwako.shiga-u.ac.jp/sensei/mnaka/ut/normdistinvtab.html">付表:正規分布表 ( P から z を求める表) - 中川雅央(滋賀大学)</a></li>
</ul>
<p><a href="https://text.baldanders.info/golang/bookmark/">Go 言語に関するブックマーク集はこちら</a>。</p>
<h2>参考図書</h2>
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/416Stewy0NS._SL160_.jpg" width="123" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">プログラミング言語Go</a></dt>
<dd>アラン・ドノバン (著), ブライアン・カーニハン (著), 柴田芳樹 (著)</dd>
<dd>丸善出版 2016-06-20 (Release 2021-07-13)</dd>
<dd>Kindle版</dd>
<dd>B099928SJD (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">Kindle 版出た! 一部内容が古びてしまったが,この本は Go 言語の教科書と言ってもいいだろう。感想は<a href="https://text.baldanders.info/remark/2016/07/go-programming-language/" >こちら</a>。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2021-05-22">2021-05-22</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- プログラミング言語Go -->
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B00I8AT1FO?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/41353H+BzFL._SL160_.jpg" width="113" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B00I8AT1FO?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">数学ガール/乱択アルゴリズム</a></dt>
<dd>結城 浩 (著)</dd>
<dd>SBクリエイティブ 2011-02-25 (Release 2014-03-12)</dd>
<dd>Kindle版</dd>
<dd>B00I8AT1FO (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">工学ガール,リサちゃん登場!</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2015-04-19">2015-04-19</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- 数学ガール/乱択アルゴリズム -->
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B01MSJMKMW?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/41-A4q7tckL._SL160_.jpg" width="111" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B01MSJMKMW?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">数学ガールの秘密ノート/やさしい統計</a></dt>
<dd>結城 浩 (著)</dd>
<dd>SBクリエイティブ 2016-10-28 (Release 2016-11-10)</dd>
<dd>Kindle版</dd>
<dd>B01MSJMKMW (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">統計の本当に基礎の部分から。学業成績でよく聞く「偏差値」とは何を表していて何を意味しているのか。なんてなあたりから。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2016-12-11">2016-12-11</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- 数学ガールの秘密ノート/やさしい統計 -->
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Calc でも Excel の関数がそのまま使えるようだ。助かる。 <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:2">
<p>余談だが, <a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>では untyped な定数を設定できる。型が評価されるのは,処理の中でその定数が使われた時点となる。数値の精度も使用時点で評価されるため,定義では大きい桁数の値を設定しても問題ない。(参考: <a href="http://qiita.com/hkurokawa/items/a4d402d3182dff387674">Go の定数の話 - Qiita</a>) <a href="#fnref:2" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>