やさいのつくりかた.

目次/第02章

02Rng

同じ種からは、同じ野菜が生える

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

この章の3行まとめ
  • 野菜を1個ずつ違う形にするには乱数が要りますが、Math.random() は使いません。
  • やり直しがきかないからです。いい形が出ても、二度と同じものは作れません。
  • そこで「種(シード)」を渡すといつも同じ順番で数字が出る乱数を、自分で作ります。

野菜を1個ずつ違う形にするには乱数が要ります。でも Math.random() は使いません。 理由は単純で、やり直しがきかないからです。 「さっき出たあのジャガイモ、いい形だったな」と思っても、二度と同じものは出てきません。 バグを見つけても、同じ状況を再現できません。

そこでシード(種)付きの乱数を自分で書きます。整数をひとつ渡すと、 そこから決まりきった順番で数字が出てくる仕組みです。同じシードなら何度でも同じ列が出ます。 「ランダムに見えるけれど、実は完全に予測できる」——だから擬似(ぎじ)乱数と言います。

class Rng {
    constructor(seed) { this.s = seed >>> 0; }
    next() {
        this.s = (this.s + 0x6D2B79F5) | 0;
        let t = Math.imul(this.s ^ (this.s >>> 15), 1 | this.s);
        t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
        return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
    }
    range(a, b) { return a + (b - a) * this.next(); }
    gauss(m, sd) { /* 平均 m・ばらつき sd の正規分布 */ }
}
23本すべてに入っている Rng。中身は掛け算とビット演算をかき混ぜているだけ

>>> や ^ は数字を2進数として混ぜる演算です。 桁をずらして重ねて、掛けて、また混ぜる。カードをシャッフルするのに近い操作で、 「規則性はあるのに人間には読めない」状態を作っています。 0x6D2B79F5 のような数は、混ざり方がよくなるように選ばれた定数です。

実験 — 種をまいてみる

同じ番号を入れれば、何度でもまったく同じ5本が生えます。

つまずきポイント

乱数の使い道は「バラバラにする」ことではなく「ありそうなばらつきを作る」ことです。 太さを完全にランダムにすると、実物には存在しない極端な個体が混ざります。 だからコードでは gauss()(正規分布)を多用します。 平均のまわりに集まり、たまに少し外れる——実物の野菜の分布はこの形です。

やってみよう
  1. 同じシードを2回入れて、まったく同じ形が出ることを確かめる。
  2. 気に入った形が出たシードの数字を、メモしておく。これが「保存」になります。
  3. ニンジン.js の START_SEED(175行目あたり)を自分の誕生日(例 20260801)にして実行する。