はじめに
※ この記事は 文言 / wenyan‑lang Advent Calendar 2020 4日目 の記事です。
はじめまして、@Mopepe51です。初めて文言関係の記事を書きます。
普段はそういう用途ではない言語などで競プロをやってみた記事を書いたりしています。
文言で乱数を得るためのライブラリは公式に提供されているのですが、公式WikiのCheatsheetに用法が記載されておらず、使用のあたっての注意点もいろいろあるので記事にまとめてみました。
「易經」ライブラリによる乱数生成
文言で最も簡単に乱数を得る方法は易經ライブラリを使用することです。
吾嘗觀『易經』之書。方悟「占」之義。
施「占」。書之。
これで0以上1未満の乱数が出力されます。
吾嘗觀『易經』之書。方悟「占」之義。
1で易經
ライブラリの占
関数をインポートし、
施「占」
で乱数を得ます。
ただし、この方法だとシード値が固定されている(※ 余談:乱数のシード)ため、占
の実行の度には値が変わるものの、何度実行しても毎回
二分二釐一毫九絲三忽六微五纖零五塵九埃八渺六漠
八分六釐七毫零七忽四微一纖九沙五塵二埃九渺六漠
四分五釐六毫六絲二忽四微一纖六沙九塵一埃三渺五漠
……
という数列が得られます2。
実行ごとにランダムな結果を得たい、というときはこれでは困るのでシード値を実行の度に変化する数値に指定してやる必要があります。一番ポピュラーなのが現在時刻です。
易經
と同様、時刻を取得するライブラリ曆法
の今何紀元時
で現在時刻のタイムスタンプを秒に変換したものが得られるのでこれを1000倍してミリ秒に戻したものをシード値に与えます。(取底
は切り捨ての関数。)
吾嘗觀『易經』之書。方悟「運」「占」之義。
吾嘗觀『曆法』之書。方悟「今何紀元時」之義。
吾嘗觀『算經』之書。方悟「取底」之義。
吾有一術。名之曰「隨機播放」。是術曰。
施「今何紀元時」。乘其以千。取一以施「取底」。取一以施「運」。噫。
是謂「隨機播放」之術也。
施「隨機播放」。噫。
施「占」。書之。
隨機播放
を実行すると3シード値がシャッフルされるので、これで今度こそ0以上1未満の乱数が得られます。
0以上10未満の乱整数がほしければ
……
施「占」。乘其以一十。取一以施「取底」。書之。
とすればよいです。
JavaScriptのrandom関数で乱数を得る
文言は変数名や関数名がJavaScriptのコードに直接展開されるためJavaScriptの関数を直接呼び出すことができます。
実質コードインジェクションですが公式のオンラインエディタで動くのでいいでしょう。(ただし、こういうことをすると当然PythonやRubyなど他の言語にトランスパイルした際に動かなくなるなど環境依存性が高くなるということに注意が必要です。)
夫「Math.random()」。取一以書之。
または
施「(() => Math.random())」。書之。
で乱数が出力できます4。
これだと自動で現在時刻にシードを設定してくれるので楽です。
Box-Muller法で正規乱数を発生させたりするのも1行で書けますね。
施「(() => Math.sqrt(-2 * Math.log(1 - Math.random())) * Math.cos(2 * Math.PI * Math.random()))」。書之。
ただし漢文レンダリングをすると悲しいことになります。半角英数字が縦書きに致命的に合わない……
JSのMath.random()
も線形合同法を使っているらしく、乱数としての品質は高くないので注意が必要です。
余談:乱数のシード
コンピューターにおける乱数とは殆どの場合5疑似乱数であり、ある値を入れると一定の決まった値を返す関数です。
普通の言語ではこの値(シード値)に現在時刻などを入力して予測がつきづらい乱数を発生させています。
とはいえ決定的な計算なので予測をつけることは可能なことが多いです。これを避けるために暗号論的擬似乱数というものも存在します。ただし、本当に暗号的に予測がつかない乱数を生成するアルゴリズムは重いので、セキュリティが重要視されない場面では線形合同法でいい感じのパラメータを選ぶかメルセンヌツイスタを使用していることが多いようです。
-
文言の文字列は
「「 」」
でクオートしますが、代わりに『 』
でも構いません。こっちのほうが格好がいいので好きです。 ↩ -
わかる人向けの解説ですが、乱数生成には線形合同法が使用されており、$A=22695477$,$B=1$,$M=2^{32}$です。なぜこのパラメーター……? ↩
-
噫
は変数スタックをリセットする制御構文です。関数の戻り値はすべて一旦スタックに積まれ、取一以施
などで関数に適用されます。適宜開放しないと使用されていない無名変数は書之
ですべて出力されることになります。 ↩ -
えっ?漢数字だと読みにくい?仕方がないですね。~~
施「(() => Math.random())」。取一以施「(銜 => console.log(銜.toString()))」。
でローマ数字のまま出力されます。~~コンパイラオプションに--no-outputHanzi
をつけましょう。 ↩ -
疑似ではない真の乱数を取得するには熱雑音を観測したり放射性物質の崩壊を観測するなどの専用のハードウェアが使用されます。 ↩