たべもののつくりかた.

目次/第02章

02Rng

同じ種からは、同じ料理ができる

seeded pseudo random — シード付き擬似乱数

この章の3行まとめ
  • 全14本とも Math.random() を使っていません。代わりに自分で乱数を書いています。
  • 「種(シード)」という整数をひとつ渡すと、何度実行しても同じ並びの数が出てきます。
  • 同じ形が二度と作れないと、直すことも、比べることもできないからです。

ウィンナーを8本焼くとします。8本とも同じ太さ・同じ曲がり・同じ焼きムラなら、 それは工場の製品ではなくコピー&ペーストに見えます。だから乱数で揺らします。 ここまではふつうです。問題はその次で、ふつうの乱数だと二度と同じ8本が作れません。

3本目だけ焼き色が変で直したい。ところが Run するたびに全部変わるので、 直したのか、たまたま別の個体が出ただけなのか分かりません。 だからこのプロジェクトは Math.random() を1回も使わず、 種を渡すと毎回同じ数列を返す乱数を自分で書いています。

// 1. Rng : シード付き擬似乱数(mulberry32)
const Rng = (seed) => {
    let s = seed >>> 0;
    return () => {
        s = (s + 0x6D2B79F5) | 0;
        let t = Math.imul(s ^ (s >>> 15), 1 | s);
        t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
        return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
    };
};
14本すべてに、ほぼこの形で入っている。名前は mulberry32

中身は「かき混ぜて、上の桁と下の桁をぶつけて、また混ぜる」だけです。 規則はあるのに、出てくる数の並びに規則が見えない。だから擬似乱数といいます。 大事なのは仕組みより性質のほうで、入れた種が同じなら、出てくる数列も同じ。 これが「同じ料理をもう一度作れる」ということです。

実験 — 種を変える / 種を固定したまま作り直す

—

ウィンナー8本の長さ・太さ・曲がり・焼きムラを、乱数から作っています。 「もう一度作る」を押したとき、Rng なら同じ8本、Math.random() なら毎回別の8本になります。

種は、1個ではなく何個も要る

実際のコードでは、種をひとつだけ持つのではなく用途ごとに分けています。 たこ焼き.js なら、生地の形の乱数・焼きムラの乱数・ソースの垂れの乱数が別々の種から出ます。 分けておかないと、「ソースの量を変えたら生地の形まで変わってしまった」ということが起きるからです。

// 個体ごとに種をずらす。同じ列でも i が違えば別の個体になる
const rnd  = Rng(seed + i * 1013);      // 形
const rndB = Rng(seed + i * 1013 + 77); // 焼き色
種に「ずらす数」を足して枝分かれさせる。14本に共通のやり方
つまずきポイント

種を足し算だけで作ると、ぶつかることがあります。 seed + i と seed + j + 1 のような作り方をすると、 別々のはずの個体がたまたま同じ種になり、双子が生まれます。 掛ける数を大きめの素数(1013 や 9176 など)にしておくのはそのためです。

正規分布のほうが「らしい」ことがある

rnd() は 0〜1 のあいだを平らに出します。長さを ±10% 振りたいときはこれでよいのですが、 「たいていは真ん中くらい、たまに極端」という揺れ方をさせたいときは、 平らな乱数だと極端なものが多すぎます。そこで ガウス(正規分布)を使います。 クッキーの反りや、団子の練りむらがこれです。

// Box-Muller 法。平らな乱数2つから、釣鐘型の乱数を作る
const gauss = (m, sd) =>
    m + sd * Math.sqrt(-2 * Math.log(Math.max(1e-9, rnd())))
          * Math.cos(6.28318 * rnd());
クッキー.js の反り、月見団子.js の練りむらはこちらを使う
やってみよう
  1. 上の実験で種を 7 から 8 に変え、8本すべてが変わることを確かめる。
  2. Math.random() に切り替えて「もう一度作る」を3回押し、毎回変わることを確かめる。
  3. ウィンナー.js を開いて Rng という語を検索し、何か所で新しい乱数を作っているか数える。
  4. 「同じ種なら同じ結果」でないと困る場面を、料理以外で3つ考えてみる。