ベータ分布乱数

ベータ分布乱数とは、一様乱数を「ベータ分布」と呼ばれる確率分布に変換する手法です。

ベータ分布のパラメータを調整すると「当たるときは極端に連続して当たり、ハマるときはトコトンハマる」という波(偏り)を1発の乱数だけで表現できます。

仕組み 編集

  • 通常の一様乱数(0〜1)を2つ以上取得する。
  • 数式(累積分布関数の近似など)を用いて、両極端(0の近辺と1の近辺)に値が集中する波を作る。

演出 編集

乱数値が0の近辺なら「超高確率(即連)」、1の近辺なら「大ハマリ」となり、通常の1/319のようなフラットな抽選ではあり得ない「連チャンの塊」が自然に生まれます。

実装例: Z80 編集

ベータ分布を愚直に浮動小数点数で計算(対数や分数など)しようとすると、Z80の計算能力では破綻します。しかし当時は「ルックアップテーブル(LUT)」を使って解決していました。

大当たり値を「0」と「255」に設定しておきます。テーブル(グラフ)の形状が両端でガクッと落ち込んでいるため、元の乱数が多少ズレても、変換後は「0」や「255」に吸い込まれるように着地します。

;---------------------------------------------------
; テーブル参照による偏り乱数の取得
; 入力: Aレジスタ = 元の一様乱数 (0〜255)
;       ※元の一様乱数は、Z80のRレジスタ(リフレッシュカウンタ)などを利用
; 出力: Aレジスタ = 偏り補正された乱数
;---------------------------------------------------
GET_BIASED_RAND:
    LD   H, HIGH(BIASED_TABLE) ; テーブルの上位アドレスをセット
    LD   L, A                  ; 一様乱数を下位アドレスにセット
    LD   A, (HL)               ; ROMテーブルから偏った値を読み出す
    RET

;---------------------------------------------------
; ROMデータ配置 (メモリ上の256バイトのエリア)
; 両端(0付近と255付近)に値が超高確率で集中するベータ分布を模したデータ例
;---------------------------------------------------
ORG 0x2000                     ; テーブルの配置アドレス(例)
BIASED_TABLE:
    ; 入力が 0〜63 のときは、出力が「0」付近に集中(超高確率ゾーン)
    DB 0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 5, 8, ...
    
    ; 入力が 64〜191 のときは、出力がなだらかに変化(通常・ハマリゾーン)
    DB 15, 20, 30, 45, 60, 80, 100, 120, 150, 180, ...
    
    ; 入力が 192〜255 のときは、出力が「255」付近に集中(即連戻りゾーン)
    DB 240, 250, 252, 254, 255, 255, 255, 255, ...

実装例: C# 編集

using System;

public class RomTableRandom
{
    private readonly Random _sourceRand = new Random();
    private readonly byte[] _biasedTable = new byte[256];

    public RomTableRandom()
    {
        GenerateBiasedTable();
    }

    /// <summary>
    /// 両端(0付近と255付近)に値が集中するベータ分布(U型)のテーブルを擬似生成
    /// </summary>
    private void GenerateBiasedTable()
    {
        for (int i = 0; i < 256; i++)
        {
            double t = i / 255.0;
            // 独自の数式でU字型のカーブを作り、0〜255にマッピング
            // 0近辺と255近辺の面積が非常に広くなるように補正
            double biasedT = Math.Sin((t - 0.5) * Math.PI) * 0.5 + 0.5; 
            biasedT = Math.Pow(biasedT, 3); // 3乗してさらに歪み(偏り)を強化
            
            _biasedTable[i] = (byte)(biasedT * 255);
        }
    }

    /// <summary>
    /// テーブルを参照して偏った乱数を返す
    /// </summary>
    public byte NextBiased()
    {
        // 0〜255の一様乱数をベースにする
        int index = _sourceRand.Next(0, 256);
        // テーブル(ROM)から偏った値を取得
        return _biasedTable[index];
    }
}