読者です 読者をやめる 読者になる 読者になる

ガシャポンの確率論

数学 Ruby


ガシャポン(ガチャポン)でセットをそろえる(フルコンプリートする)のにいくらかかるか(たとえば90%の確率で)、確率(たとえば10回で揃う確率)は? 平均何回で揃うか? という種類の質問は、「たとえば」ということばが象徴するように、個人的な思いがあったりして、なかなかちゃんと議論できません。というかバカと言われようが、納得できなかったりします。

Yahoo知恵袋に「5種類入りののガチャガチャがあり、それを10回分買って5種類全部揃えれる確率を教えてください。」という質問があって
http://detail.chiebukuro.yahoo.co.jp/qa/question_detail.php?qid=1211774567
ベストアンサーが順列組合せを使って説明していて
p(10,5) = 0.5225472
となっています。これはむずかしい。

山本弘のSF基地よりというサイト(http://homepage3.nifty.com/hirorin/gchaponkakuritsu.htm)で、「コンプリートするのにいくらかかる?」というのに、次のような説明があります。

残りの個数、期待値(回)、金額(円)
6、 1.0、 200
5、 1.2、 240
4、 1.5、 300
3、 2.0、 400
2、 3.0、 600
1、 6.0、 1200
合計 14.7、 2940

この例では、6種類のセットをそろえるのに平均約15回(つまり3000円)かかるということで、同じ考え方でYahoo知恵袋の質問の5種類で考えると、1回目は1回、2回目はのこり4個なので4/5の確率=>5/4=>1.25(平均1.25回でゲット)、3回目はのこり3個なので、3/5の確率=>5/3=>1.66、4回目はのこり2個なので、2/5=>5/2=>2.5、5回目はのこり1個なので1/5=>5/1=5.0。合計すると1.0+1.25+1.66+2.5+5.0 => 11.41。となり、平均12回で5種類がそろうことになります。

これが先ほどのYahoo知恵袋の回答10回で確率0.52と感覚が合うかどうか? 山本氏の文章は、「最後の1種類を出すのに平均して6回、1200円も注ぎこまなくてはいけません」というように、平均という言葉の定義が「期待値」と同義になっている。

実際にガシャポンを何度もやってみたり、さいころを振ったりするのは不可能なので、プログラムしてみます。

5種類を[”a”, “b”, “c”, “d”, “e”]とおいて、たとえば2回やってみるとすると場合は[”a”, “a”](”a”が2回続けて出てしまう)から[”e”, “e”]までの5の2乗の場合がありえて、そのうちたとえば ”a”がひとつでも含まれる組合せが何回あるか、というプログラムになります。この場合は25回中9回にありましたので、確立は9/25 = 0.36。山本さんの文章に沿うと最後の1個は平均5回かかることになってしまいますが、むろん100回やって当たらない場合もあるし、1回で当たってしまう場合もある。5回やるとすると、5の5乗(3125)の場合中、2101に”a”がありますので、ここだけで言えば確率0.67232で大きく0.5をオーバーしています。これは”a”が当たらない確立の5乗と考えた方がわかりやすくて、rubyですと、ruby -e’puts (4.0/5)**5) => 0.32768で合計すると1になります。

10回分買って5種類全部揃えれる確率というのは5の10乗(9765625)の場合について、[”a”, “b”, “c”, “d”, “e”]が全部含まれる場合の数の比率ですので、これはたいへん。[”a”, “a”, “a”, “a”, “a”, “a”, “a”, “a”, “a”, “a”]から[”e”, “e”, “e”, “e”, “e”, “e”, “e”, “e”, “e”, “e”]のすべての場合について、順序を問わず、[”a”, “b”, “c”, “d”, “e”]が全部含まれる場合を数えていく。rubyだと配列の引き算で結果がゼロ[]の場合とか、配列のメソッドでuniqとsizeを使って、ダブりをとってサイズが5のものの数を数えるとかができます。いずれにしてもベラボーな時間がかかるので配列を使わず1〜5**10について数字で調べていくのがいいかもしれません。この結果5103000回コンプリートしていましたので、5103000/9765625 => 0.5225472とYahoo知恵袋と同じ結果になりました。たぶん合っているのでしょう。

次の課題は90%の確率で揃うためには何回やらなければならないか?
フツー平均何回で揃うのか?
この二つの課題は実はおんなじでフツーというのがその人にとって何%により、何回で揃うという言い方が可能になるのだと思います。90%に置くとちょっと時間的に無理そうなので、70%として、種類数を2から始めます。
ruby gashapon.rb 2 3 => 6 / 8 =>0.75
ruby gashapon.rb 3 6 => 540 / 729 => 0.740740740740741
ruby gashapon.rb 4 9 => 186480 / 262144 => 0.71136474609375
4種類のセットならば9回やって初めて70%を超える確率でコンプリートする。

さて、5種類のセットで12回くらいで70%を超えるかどうか、ですが、パソコンで1時間経っても計算中なのでプログラムを見直し中です。

一応11回では60%でした。
ruby gashapon.rb 5 11 => 29607600 / 48828125 => 60.6363648

ちなみにgashapon.rbは現在下記のとおり(使っていない部分もあります)。

#! /usr/bin/env ruby
# gashapon.rb num1 num2
# the first number is the number of goal set
# the second number is the number of tries
# for example the set is made of 2 pieces a and b, and if you take 2 times,
# the possibilities are [a a], [a b], [b a], [b b] in which [a b] and [b a] is the success
# namely gashapon(2, 2) is 0.5

def check_argv
  exit if ARGV.length != 2
end

def target(num)
  ary = %w(a b c d e f g h i j k)
  return ary[0..(num- 1)]
end

def num2ary(number, base, digit)
  ary = 
  digit = digit - 1
  while digit > 0
    ary.push(target(base)[number / base ** digit])
    if (number / base ** digit) > 0
      number = number - (base ** digit)*(number / base ** digit)
    end
    digit = digit - 1
  end
  ary.push(target(base)[number])
end

def cases(base, digit)
  i = 0
  ary = 
  while i < (base ** digit)
   ary.push(num2ary(i, base, digit))
    i = i + 1
  end
  return ary
end

def num_cases(base, digit)
  return base ** digit
end

def num_hitcases(case_ary, target_ary)
  num = 0
  case_ary.each {|ary|
    if target_ary - ary == []
    num  = num + 1
    end
  }
  return num
end
def num_hit(base, digit)
  i = 0
  num = 0
  while i < (base ** digit)
   num = num + 1 if num2ary(i, base, digit).uniq.length == base
    i = i + 1
  end
  return num
end

check_argv
base = ARGV[0].to_i
digit = ARGV[1].to_i
p child = num_hit(base, digit)
p mother = num_cases(base,digit)
p child.to_f / mother