値をランダムに分配するにはどうすればいいのか という記事をみて、そういえば前にこんなことを考えていたなーということを思い出したので書いた。
背景
ゲームのユニットのパラメタをランダムに生成したいことはよくある。しかしこのやり方には注意が必要だ。
例えば10000ptを割り振りたいときに、「ランダムにパラメタを選んで1増やす。これを10000回繰り返す」とすると各パラメタの値は期待値に落ち着く。
パラメタ数が5種類なら、各パラメタは全体的に2000前後になってしまう。
やりたいこと
各パラメタの値がそれなりに分散して欲しい。
やり方
中学か高校の数学の「場合の数」で習ったような方法を使う。
10,000個のみかんを5人で分けたい場合、みかんを一列に並べランダムに4枚の仕切りをさして各人が区切られた区間のみかんをもらう。
この方法だと少なくともみかんの数が増えても、もらえるみかんの数が期待値に収束することは無い。
コード
distribute_1
が期待値に収束するやり方。distribute_2
がしないやり方。
random_parameter.rb
# 検証用のヘルパ関数
def sum_value(hash)
ret = 0
hash.each do |k, v|
ret += v
end
ret
end
def distribute_1(max, params)
ary = Array.new(params.size, 0)
max.times do
index = rand(ary.size)
ary[index] += 1
end
params.zip(ary).to_h
end
def distribute_2(max, params)
randoms = Array.new(params.size - 1){ rand(max) }.sort
table = [0, *randoms, max]
params.map.with_index { |key, index| [key, table[index + 1] - table[index]] }.to_h
end
max = 10_000
params = [:atk, :def, :dex, :mag, :lak]
puts "---- type 1"
table = distribute_1(max, params)
p table
puts "total = #{sum_value table}"
puts "---- type 2"
table = distribute_2(max, params)
p table
puts "total = #{sum_value table}"
出力
$ ruby random_parameter.rb
---- type 1
{:atk=>1994, :def=>2009, :dex=>1969, :mag=>2017, :lak=>2011}
total = 10000
---- type 2
{:atk=>1322, :def=>2089, :dex=>465, :mag=>4966, :lak=>1158}
total = 10000
それなりにバラけます。