MATLAB ユーザーコミュニティー

MATLAB & Simulink ユーザーコミュニティー向け日本語ブログ

[6 3 7 8 5 1 2 4 9 10] – ”乱数”にまつわるストーリー

※この投稿は 2022 年 6 月 7 日に The MATLAB blog (Mike Croucher) に投稿されたものの抄訳です。

3月下旬、 MATLAB の乱数処理についてTom Rhys Marshall さんが何か気が付いたようです。

randperm はランダムな整数を返す関数で、例えば randperm(10) は 1 から 10 までのすべての整数をランダムな順番で返します。新たに立ち上げた MATLAB で実行するとこの「ランダムな」順番は常に同じです。
randperm(10)
ans = 1×10
6 3 7 8 5 1 2 4 9 10
Tom さんなどが指摘されているように、世の中には、「そらそうでしょう」という人と「まじか・・知らなかった」と心配している人に分かれる様です。

コンピュータの作る乱数はランダムではない・・

MATLAB や Python、R における乱数は、純粋な意味での乱数ではなく完全に決定論的です。ただ、これらの「疑似乱数」アルゴリズムが返す数値は、デザイン上本当の乱数と同じ統計量を持っているので、注意さえしていればモンテカルロ・シミュレーションなどを、すべてが本当にランダムであるかのように考えることができます。
大事なことなのでもう一度言いますよ。最近のプログラミング言語やシミュレーション・プラットフォームで使われているすべての「乱数」ジェネレータは、完全に決定論的です。もし、乱数を使って何かしようとしているなら、遅かれ早かれぶつかるポイントですね。
長い間、MATLAB の乱数生成器は Mersenne Twister と呼ばれるアルゴリズムをデフォルトとして使用してきました。MathWorks のこの実装は、他の多くの実装と同様に、設定された「シード」に応じて異なる乱数セットを出力します。シードは整数で、rng() 関数を使用して設定できますが、MATLAB を起動したときはデフォルトでシードは 0 に設定されています。
rng(0)
randperm(10)
ans = 1×10
6 3 7 8 5 1 2 4 9 10
もちろん違うシードを設定すれば結果も違います。
rng(1)
randperm(10)
ans = 1×10
3 6 5 7 4 8 9 1 10 2
「よりランダム」な数字を得るためにはシードに何か凝ったことをしないといけない・・と思われるかもしれませんが、単純な乱数の使い方であれば、どんな整数でも大丈夫です(既定でクライアントとワーカーは異なる乱数発生器を使用するので、並列シミュレーションを扱う場合はややこしくなりますが・・詳細は最後まとめた関連ページを参照してください。)

A design choice: MATLAB 立ち上げ時のシードはなにであるべきか?

Tom さんのツイートへの返信を見ると、他の言語では MATLAB とは違う動作をすることが指摘されています。例えば、Python のデフォルトのジェネレータは、起動するたびに(おそらく)異なる乱数を返すとか。これは MATLAB よりもランダムというわけではなく、シードを設定する方法が異なるからです。
起動時のシード設定方法として、システム時刻を利用する方法があります。起動時の時刻を利用して整数を生成し、これをシードとして利用します。そうするとシステムを起動するたびに、異なるシード、つまり異なる乱数が作られます。MATLAB でも rng(“shuffle”) を使えばできます。
rng(“shuffle”) % Use the system clock to set the seed
randperm(10)
ans = 1×10
7 4 5 6 1 3 9 2 10 8
Twitter でも多くの人がそう主張している通り、この方法の方が起動時に常に同じシードを使用する方法(MATLAB のデフォルト設定)よりも優れているという議論も可能です。例えば・・
  • よりランダムである(確かに毎回違う。これは役に立つかもしれないし、立たないかもしれない)
  • 2 回実行してそれぞれ「独立した」結果を得ることができる (たぶん。実際にはこの方法で作られた2つの配列が統計的に独立であるという数学的な保証はありませんが、おそらくうまくいくでしょう)
過去のある時点で MathWorks では、もろもろの懸念事項よりも「再現性を重視する」設計上の決定がなされました。乱数生成器のシードにシステムクロックを使用することは(実際に使用されたシードを記録できず)再現性には嬉しくありません。もし必要であれば rng(“shuffle”) を使えばいいので。

Bug report: 「乱数」が違う・・

2008 年に MATLAB で使用するデフォルトのアルゴリズムが変更されることがありました。多くのユーザーがテストに失敗・・多くのバグレポートが寄せられました。一部のユーザーは、アルゴリズムを正確に再現可能な乱数で検証していたので乱数の変更は大問題でした。もちろん、アルゴリズムやシードを調整することは可能でしたが、多くの人はデフォルトの設定を使っていますからね。
もちろん何百万人ものユーザーのことを考えると、デフォルトの動作の変更は軽々しくできることではありません。ただ、古い乱数アルゴリズムに根本的な問題があったため、デフォルトの変更を余儀なくされました。Mersenne Twister はその問題を修正するために設計されました。2022 年現在、Mersenne Twister は依然として多くの作業において優れた選択肢であり、MathWorks はユーザーが異なるものを必要とする場合に備えて、いくつかの追加アルゴリズムをオプションで追加しました。

My story: 時刻をシードにすることが必ずしも良いアイデアではない理由

その昔、コンピュータの乱数処理について知る前のこと。マンチェスター大学のギーク集団の一員だった私は、Condor という技術を使って大学のすべてのデスクトップマシンをその場限りのスーパーコンピュータに変えていました。当時の基準では、ピーク時に約 5000 の CPU コアが利用可能なかなり大規模なものでした。そのユーザーを探していました。
そんな時ある研究者が完璧なアプリケーションを持って接触してきました。それは MATLAB ではない何かでプログラムされたモンテカルロ・シミュレーションで、彼は週末に数十年分の CPU 時間分の計算を実行しました。彼はその結果にとても満足していました。結果を吟味し始めるまでは・・。
デスクトップ PC では、予想通りシミュレーションを実行するたびに異なる結果が得られていました。ただ、私たちの作ったシステムでは、同じ結果が何度も出てきていました。同じ結果が数個出ることもあれば、数百個出ることもあり、パターンもないように見えます。当時の私たちは乱数の仕組みがよくわからなかったので大混乱。
私たちが使っていたコンピュータのシステムクロックは、インターネットを使って同期していました。結果として、多くの(全てではありませんが)ジョブは全く同じ時刻に起動し、全く同じシード、つまり同じ乱数ストリームを持つことになりました。
このブログの前半で、「最近のプログラミング言語やシミュレーション・プラットフォームで使われている『乱数』発生器はすべて完全に決定論的である」「乱数を使って何かしようとしているなら、遅かれ早かれぶつかるポイントですね」と書きました。これが私がぶつかった時の経験です。

もっと深掘りするには

乱数は人気のあるトピックで、MATLAB の公式ドキュメントはもちろんのこと、何年にもわたっていくつかのブログ記事があります。もっと深く掘り下げたい方にお勧めのものをいくつかご紹介します。

|
  • print

コメント

コメントを残すには、ここ をクリックして MathWorks アカウントにサインインするか新しい MathWorks アカウントを作成します。