Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

加重乱数(重み付き乱数)を生成する方法【Ruby実装例つき】

通常、乱数は「一様分布(uniform distribution)」に従います。これは、どの数値が選ばれる確率もすべて等しいという意味です。

しかし、特定の数値が他の数値よりも高い確率で選ばれるようにしたい場合は、別の戦略が必要になります。それが加重乱数ジェネレーター(重み付き乱数生成器)です。

実際の応用例としては、次のようなものが挙げられます。

  • ゲーム内のルートテーブル:敵が異なるアイテムを、それぞれ異なるドロップ率で落とす仕組み
  • 抽選会:抽選券を多く持っている人ほど、当選する確率が高くなる仕組み

シンプルな方法:配列を使う戦略

抽選会の例で考えてみると、すぐに思いつく解決策があります。「抽選券」1枚ごとにアイテムのコピーを1つ格納した配列を生成すればよいのです。

たとえば、Johnが4枚の抽選券を購入し、Davidは1枚しか購入しなかった場合、JohnはDavidの4倍の当選確率を持つことになります。

動作する実装例はこちらです

users  = { john: 4, david: 1 }
raffle = []

users.map do |name, tickets|
  tickets.times { raffle << name }
end

p raffle
# [:john, :john, :john, :john, :david]

p raffle.sample
# :john

このコードでは、購入した抽選券の枚数だけその人の名前をリストに追加し、そこからランダムに1つの名前を選んでいます。リストに多く登場する名前ほど選ばれる確率が高くなる、という仕組みです。

このアプローチの魅力は、非常にシンプルである点と、一度リストを作成してしまえば当選者の抽出が非常に高速に行える点にあります。

重みの合計値を使う方法

もうひとつ、メモリ効率に優れた方法もあります。ただしトレードオフとして、乱数の抽出速度はやや遅くなります。

考え方はシンプルです。「1から全重みの合計値までの範囲で乱数を1つ選び、その数値以下になる重みが見つかるまでループ処理を行う」というものです。

コードはこちらです

def random_weighted(weighted)
  max    = sum_of_weights(weighted)
  target = rand(1..max)

  weighted.each do |item, weight|
    return item if target <= weight
    target -= weight
  end
end

def sum_of_weights(weighted)
  weighted.inject(0) { |sum, (item, weight)| sum + weight }
end

このコードは、キーにアイテム、値に重みを持つハッシュを受け取ります。メソッドの呼び出し方は次のとおりです。

random_weighted(cats: 5, dogs: 1)
# :cats

このメソッドが期待どおりに動作しているか確認するには、何度も実行して結果の分布を観察するとよいでしょう。

検証用のコード例

counts = Hash.new(0)

def pick_number
  random_weighted(cats: 2, dogs: 1)
end

1000.times { counts[pick_number] += 1 }
p counts

このスクリプトを何度か実行し、出力された比率が想定どおり(cats : dogs = 2 : 1)になっているか確認してみてください。

まとめ

より高度なアルゴリズムも存在しますが、今回紹介した2つの手法だけでも十分に実用に耐えるものです。この記事がお役に立てば幸いです。ぜひ友人にもシェアしていただけると、今後も記事を書き続ける大きな励みになります!

  1. C#でランダムな文字列を生成する方法を初心者向けに解説

    C#でランダムな文字列を生成したい場面は意外と多くあります。パスワードの一時発行やテストデータの作成など、実は標準ライブラリだけでも簡単に実装可能です。本記事では、RandomクラスとStringBuilderを組み合わせて、ランダムな英大文字(A〜Z)からなる文字列を作成する方法を、手順ごとにわかりやすく解説します。 1. StringBuilderで文字列の受け皿を用意する まず、生成した文字を順番に連結していくための入れ物として、StringBuilderのインスタンスを作成します。 StringBuilder str = new StringBuilder(); 2. Randomクラ

  2. Pythonで乱数を生成する方法を徹底解説!randomモジュールの基本と使い方

    Pythonの標準ライブラリには、乱数を生成するための機能がまとめられた random モジュールが用意されています。このモジュールを利用することで、浮動小数点数や整数の乱数、シーケンスからのランダムな要素の取得などを簡単に実現できます。ここでは、よく使われる主要な関数をサンプルコードとともに紹介します。random():0以上1未満の浮動小数点数を生成もっとも基本的な関数が random() です。この関数は、0以上1未満の範囲のランダムな浮動小数点数を返します。>>> import random>>> random.random()0.520470277