JavaScript でシードを使用して乱数を生成する

Habdul Hazeez 2023年10月12日
  1. シードと SFC32 を使用して乱数を生成する
  2. Seed と Mulberry32 を使用して乱数を生成する
  3. Seed と Xoshiro128** を使用して乱数を生成する
  4. シードと JSF を使用して乱数を生成する
  5. seedrandom.js を使用して乱数を生成する
JavaScript でシードを使用して乱数を生成する

この記事では、シードを使用して PRNG から乱数を生成する方法について説明します。 一方、PRNG のシードのエントロピーが高いことを確認するのがベスト プラクティスです。

したがって、ハッシュ関数を使用してシードを生成します。 その後、シードを PRNG に渡します。

シードと SFC32 を使用して乱数を生成する

SFC32 または Simple Fast Counter は、PractRand からの高速な PRNG (ほとんどが C) であり、128 ビット状態の JavaScript で実装されており、非常に高速です。 SFC32 では、乱数を生成するために少なくとも 1つのシードが必要です。

MurmurHash3 の JavaScript 実装であるハッシュ関数を使用して、このシードを生成します。これには、シードを生成するための初期文字列が必要です。 その結果、文字列を渡します。

次のコードでは、シードを生成し、乱数を返す SFC32 に渡します。

コード:

// Define the Murmur3Hash function
function MurmurHash3(string) {
  let i = 0;
  for (i, hash = 1779033703 ^ string.length; i < string.length; i++) {
    let bitwise_xor_from_character = hash ^ string.charCodeAt(i);
    hash = Math.imul(bitwise_xor_from_character, 3432918353);
    hash = hash << 13 | hash >>> 19;
  }
  return () => {
    // Return the hash that you can use as a seed
    hash = Math.imul(hash ^ (hash >>> 16), 2246822507);
    hash = Math.imul(hash ^ (hash >>> 13), 3266489909);
    return (hash ^= hash >>> 16) >>> 0;
  }
}

function SimpleFastCounter32(seed_1, seed_2, seed_3, seed_4) {
  return () => {
    seed_1 >>>= 0;
    seed_2 >>>= 0;
    seed_3 >>>= 0;
    seed_4 >>>= 0;
    let cast32 = (seed_1 + seed_2) | 0;
    seed_1 = seed_2 ^ seed_2 >>> 9;
    seed_2 = seed_3 + (seed_3 << 3) | 0;
    seed_3 = (seed_3 << 21 | seed_3 >>> 11);
    seed_4 = seed_4 + 1 | 0;
    cast32 = cast32 + seed_4 | 0;
    seed_3 = seed_3 + cast32 | 0;
    return (cast32 >>> 0) / 4294967296;
  }
}

let generate_seed = MurmurHash3('String for the Seed Key');
let random_number = SimpleFastCounter32(generate_seed(), generate_seed());
console.log(random_number());
console.log(random_number());

出力:

0.837073584087193
0.3599331611767411

Seed と Mulberry32 を使用して乱数を生成する

Mulberry32 も PRNG ですが、SFC32 よりもコード構造が単純です。 少なくともシードが必要な SFC32 とは対照的です。

MurmurHash3 を使用して、文字列を使用してシードを生成します。 次の例では、for ループと Mulberry32 を使用して 5つの乱数を生成します。

コード:

// Define the Murmur3Hash function
function MurmurHash3(string) {
  let i = 0;
  for (i, hash = 1779033703 ^ string.length; i < string.length; i++) {
    let bitwise_xor_from_character = hash ^ string.charCodeAt(i);
    hash = Math.imul(bitwise_xor_from_character, 3432918353);
    hash = hash << 13 | hash >>> 19;
  }
  return () => {
    // Return the hash that you can use as a seed
    hash = Math.imul(hash ^ (hash >>> 16), 2246822507);
    hash = Math.imul(hash ^ (hash >>> 13), 3266489909);
    return (hash ^= hash >>> 16) >>> 0;
  }
}

function Mulberry32(string) {
  return () => {
    let for_bit32_mul = string += 0x6D2B79F5;
    let cast32_one = for_bit32_mul ^ for_bit32_mul >>> 15;
    let cast32_two = for_bit32_mul | 1;
    for_bit32_mul = Math.imul(cast32_one, cast32_two);
    for_bit32_mul ^= for_bit32_mul +
        Math.imul(for_bit32_mul ^ for_bit32_mul >>> 7, for_bit32_mul | 61);
    return ((for_bit32_mul ^ for_bit32_mul >>> 14) >>> 0) / 4294967296;
  }
}

let generate_seed = MurmurHash3('String for the Seed Key');
let random_number = Mulberry32(generate_seed());

for (let i = 0; i < 5; i++) {
  console.log(random_number());
}

出力:

0.13532060221768916
0.8630009586922824
0.53870237339288
0.5237146227154881
0.8748106376733631

Seed と Xoshiro128** を使用して乱数を生成する

Vagna 教授と Blackman は、Xoshiro128** ジェネレーターを開発しました。 xorshift128Xorshift PRNG のファミリーであり、最速の PRNG です。

SFC32 と同様に、Xoshiro128** は、乱数を生成する前に少なくともシードを取得できます。 次のスニペットでは、必要なシードを MurmiurHash3 で作成しました。

コード:

// Define the Murmur3Hash function
function MurmurHash3(string) {
  let i = 0;
  for (i, hash = 1779033703 ^ string.length; i < string.length; i++) {
    let bitwise_xor_from_character = hash ^ string.charCodeAt(i);
    hash = Math.imul(bitwise_xor_from_character, 3432918353);
    hash = hash << 13 | hash >>> 19;
  }
  return () => {
    // Return the hash that you can use as a seed
    hash = Math.imul(hash ^ (hash >>> 16), 2246822507);
    hash = Math.imul(hash ^ (hash >>> 13), 3266489909);
    return (hash ^= hash >>> 16) >>> 0;
  }
}

function Xoshiro128_twostar(seed_1, seed_2, seed_3, seed_4) {
  return () => {
    let t = seed_2 << 9, y = seed_1 * 5;
    y = (y << 7 | y >>> 25) * 9;
    seed_3 ^= seed_1;
    seed_4 ^= seed_2;
    seed_2 ^= seed_3;
    seed_1 ^= seed_4;
    seed_3 ^= t;
    seed_4 = seed_4 << 11 | seed_4 >>> 21;
    return (y >>> 0) / 4294967296;
  }
}

let generate_seed = MurmurHash3('String for the Seed Key');
let random_number = Xoshiro128_twostar(generate_seed(), generate_seed());
console.log(random_number());

出力:

0.6150987280998379

シードと JSF を使用して乱数を生成する

Bob Jenkins は、高速ジェネレーターである Jenkins Small Fast (JSF) ジェネレーターを作成しました。 ただし、≪SFC32≫に比べれば速くはない。

JSF のコードを観察すると、SFC32 との類似性に気付くでしょう。 JSF は乱数を生成する前に複数のシードを取ることができます。

次のコードでは、シードと JSF を使用して 10 個の乱数を生成します。

コード:

// Define the Murmur3Hash function
function MurmurHash3(string) {
  let i = 0;
  for (i, hash = 1779033703 ^ string.length; i < string.length; i++) {
    let bitwise_xor_from_character = hash ^ string.charCodeAt(i);
    hash = Math.imul(bitwise_xor_from_character, 3432918353);
    hash = hash << 13 | hash >>> 19;
  }
  return () => {
    // Return the hash that you can use as a seed
    hash = Math.imul(hash ^ (hash >>> 16), 2246822507);
    hash = Math.imul(hash ^ (hash >>> 13), 3266489909);
    return (hash ^= hash >>> 16) >>> 0;
  }
}

function JenkinsSimpleFast32(seed_1, seed_2, seed_3, seed_4) {
  return () => {
    seed_1 |= 0;
    seed_2 |= 0;
    seed_3 |= 0;
    seed_4 |= 0;
    let t = seed_1 - (seed_2 << 27 | seed_2 >>> 5) | 0;
    seed_1 = seed_2 ^ (seed_3 << 17 | seed_3 >>> 15);
    seed_2 = seed_3 + seed_4 | 0;
    seed_3 = seed_4 + t | 0;
    seed_4 = seed_1 + t | 0;
    return (seed_4 >>> 0) / 4294967296;
  }
}

let generate_seed = MurmurHash3('String for the Seed Key');
let random_number = JenkinsSimpleFast32(generate_seed(), generate_seed());
for (let i = 0; i < 10; i++) {
  console.log(random_number());
}

出力:

0.513338076416403
0.4737987464759499
0.5743723993655294
0.4811882192734629
0.07753282226622105
0.11416710214689374
0.1270705321803689
0.15759771666489542
0.16906401910819113
0.6846413582097739

seedrandom.js を使用して乱数を生成する

seedrandom.js は、David Bau がシード乱数ジェネレータ (RNG) 用に設計したライブラリで、NPM と CDNJS で利用できます。 この記事では、CDNJS を使用します。

Seedrandom.js を使用する場合は、次の点に注意してください。

  1. new Math.seedrandom('seed key') を使用して seedrandom を初期化します。
  2. Seedrandomquick() 関数を使用して、32 ビットの乱数を生成できます。
  3. Seedrandom.js の int32() 関数は、32 ビットの符号付き整数を返します。
  4. 引数なしで seedrandom を呼び出すと、自動シードされた ARC4 ベースの PRNG が作成されます。 自動シード処理では、累積されたローカル エントロピーなどの値が使用されます。
  5. Seedrandom は 2 番目の引数としてオブジェクトを取ることができます。 このオブジェクトは {entropy: true} であり、結果は予測できません。
  6. new キーワードなしで Math.seedrandom を呼び出すと、デフォルトの Math.random() が置き換えられます。 代わりは new Math.seedrandom() です。

この例では、CDNJS から seedrandom.js をインポートしました。 その後、シードを使用して乱数を生成するために使用します。

コード:

<body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/seedrandom/3.0.5/seedrandom.min.js"></script>
    <script>
        let generate_random_number = new Math.seedrandom('Johnson');
        console.log(generate_random_number());
    </script>
</body>

出力:

0.08103389758898699

シードを蓄積されたエントロピーと混ぜることができます。 seedrandom の 2 番目の引数として {entropy: true} を渡す必要があります。

コード:

<body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/seedrandom/3.0.5/seedrandom.min.js"></script>
    <script>
        let generate_random_number = new Math.seedrandom('Antananarivo', { entropy: true });
        for (let i = 0; i < 5; i++) {
            console.log(generate_random_number());
        }
    </script>
</body>

出力:

0.8478730572111559
0.963664252064149
0.6002684820777331
0.4026776455839767
0.7579996916288508

さらに、quick()int32() はランダムな 32 ビット乱数を返します。 前者は float を返し、後者は符号付き整数を返します。

コード:

<body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/seedrandom/3.0.5/seedrandom.min.js"></script>
    <script>
        let generate_random_number = new Math.seedrandom('DavidBau');
        console.log("With quick():", generate_random_number.quick());
        console.log("With int32():", generate_random_number.int32());
    </script>
</body>

出力:

With quick(): 0.249648863915354
With int32(): -550219731
著者: Habdul Hazeez
Habdul Hazeez avatar Habdul Hazeez avatar

Habdul Hazeez is a technical writer with amazing research skills. He can connect the dots, and make sense of data that are scattered across different media.

LinkedIn

関連記事 - JavaScript Number