• ベストアンサー

サイコロの出る目の確立をプログラムで出すには??

最近VBAの勉強を始めた者です。質問は、タイトルの通りサイコロをn個投げた時の和の合計を求めるプログラムを作りたいのですが、 サイコロの個数n 求める目の和x n個のサイコロを投げた時の目の和Pn(x)とおいて Pn(x)=1/6Pn-1(x-y) y=1,2,3,4,5,6 ここからどのようにプログラムを組んだらよいのかわかないので、もし少しでも分かる方がいらっしゃれば教えていただけませんか?? 宜しくお願いします。

質問者が選んだベストアンサー

  • ベストアンサー
回答No.4

一応数学科にいたことがあるのですが、、、公式を覚えておりません。。。 幼稚っぽい方法ですが、n個のサイコロで期待値xを構成するパターン取得プログラムを書いてみました。 ・コレクション変数に、実現パターンを追加(≪取得_パターン()≫関数) ・コレクション変数へ格納された値を、ファイルに出力(≪結果出力_パターン()≫関数) という方法で行っております。 なので、コレクション変数に収まりきれないような、多いn個のサイコロを指定すると、オーバーフローを起こすと思います。 これを回避するには、全てを溜め込むのではなく、取得したら直ぐにファイルなどに吐き出し、溜め込まないようにしなければなりません。≪結果出力_パターン()≫関数を解析してもらえたら、そのヒントになると思います。 ※探し方のコツ 例1.サイコロがn個(A/B/C・・・n)、合計値4の時 /*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/* (1)【n個で、4を成立させるパターンを探す】 ○A=1 残りの合計は3 ○A=2 残りの合計は2 ○A=3 残りの合計は1 ▲A=4 残りの合計は0 ×A=5 残りの合計は-1 ×A=6 残りの合計は-2 ・Aの取り得る範囲は 残りのサイコロ(n-1)=0であれば ⇒ 1~4 残りのサイコロ(n-1)>0であれば ⇒ 1~3 (サンプルの≪取得_範囲()≫関数を参照) /*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/* (2)-1 【A=1を記憶】 (2)-2 【A=2を記憶】 (2)-3 【A=3を記憶】 /*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/* (2)-1より 【n-1個で、3を成立させるパターンを探す】 ○B=1 残りの合計は2 ○B=2 残りの合計は1 ▲B=3 残りの合計は0 ・・・ ・Bの取り得る範囲は 残りのサイコロ(n-2)=0であれば ⇒ 1~3 残りのサイコロ(n-2)>0であれば ⇒ 1~2 (2)-1-1 【B=1を記憶】 【n-2個で、2を成立させるパターンを探す】 (2)-1-2 【B=2を記憶】 【n-2個で、1を成立させるパターンを探す】 /*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/* /*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/* (2)-2より 【n-1個で、2を成立させるパターンを探す】 ○B=1 残りの合計は1 ▲B=2 残りの合計は0 ・・・ ・Bの取り得る範囲は 残りのサイコロ(n-2)=0であれば ⇒ 1~2 残りのサイコロ(n-2)>0であれば ⇒ 1~1 (2)-2-1 【B=1を記憶】 【n-2個で、1を成立させるパターンを探す】 /*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/* ・・・・・・・・・・・・・・・・・・・・・・・・・・・ と続きます。。。。。。 パターンは、個数が多いほど樹木のように膨らみますが、A=1としただけで、期待値の範囲は収束していきます。 サンプルでは【「n」個で、「x」を成立させるパターンを探す】という部分を、再帰法を利用し、≪取得_パターン()≫関数としております。 ※このサンプルは、単純な方法を利用しているので、実際には100点の回答ではありません。 例2.サイコロが2個、合計値7の時 「1・6」もあれば「6・1」もあります サンプルは、どちらも同じ様にこのパターンを見つけていますが、重複するパターンは本来探す必要がないはずです。 例3.サイコロが3個(X/Y/Z)、合計値5の時 X≦Y≦Zという仮定をすると 「1・1・3」 「1・2・2」 が得ることができ、あとはこれらの組み合わせで実現します。 以下のサンプルを利用して、文章の意味を理解してもらえたら嬉しいです。 ※準備 >サイコロの個数n シート1のA1に書いて下さい >求める目の和x シート1のB1に書いて下さい ※構成 ・標準モジュール ・クラスモジュール -----標準モジュール(Module1) START----- Option Explicit '取得結果出力ファイル名 Private Const DEF_結果ファイル As String = "c:\Log1.txt" '--------------------------------------------------- 'メイン '--------------------------------------------------- Sub Main()   Const DEF_FSO_書込 As Integer = 2   Dim lng全個数    As Long   Dim lng期待値    As Long   Dim objStrem    As Object   Dim objパターン   As Class1         '----------------------------------------   '//取得メイン部   '----------------------------------------   Set objパターン = New Class1   'シート1番目のセルA1/A2より、パラメータを取得する   lng全個数 = ThisWorkbook.Worksheets(1).Cells(1, 1) 'A1   lng期待値 = ThisWorkbook.Worksheets(1).Cells(1, 2) 'A2      'メイン実行   Call 取得_パターン(lng期待値, lng全個数, objパターン)         '----------------------------------------   '//取得結果出力処理   '----------------------------------------   Set objStrem = CreateObject("Scripting.FileSystemObject").OpenTextFile(DEF_結果ファイル, DEF_FSO_書込, True)   Call 結果出力_パターン(objStrem, objパターン)   objStrem.Close   Set objStrem = Nothing         MsgBox "終了" End Sub '--------------------------------------------------- '取得結果の出力を行う '--------------------------------------------------- Private Function 結果出力_パターン(p_objStrem As Object, p_objパターン As Class1, Optional p_col出力 As Collection) As Boolean   Dim objパターン   As Class1   Dim bln終了フラグ  As Boolean      '出力コレクションが初期化されていなければ初期化を行う   If p_col出力 Is Nothing Then     Set p_col出力 = New Collection   End If      '子供がいなければ終了する   If p_objパターン.c子供.Count = 0 Then     Exit Function   End If      '自分の子供ループ   For Each objパターン In p_objパターン.c子供     '出目を追加する     p_col出力.Add objパターン.c出目          '自分の子供の存在チェック且つ、存在していれば出力対象に追加     bln終了フラグ = Not 結果出力_パターン(p_objStrem, objパターン, p_col出力)          '子供がいない     If bln終了フラグ Then       '出力コレクションをファイルに出力       Call コレクション出力(p_objStrem, p_col出力)     End If          '今追加した子を削除     p_col出力.Remove p_col出力.Count          'もう子がいないのでループ抜け     If bln終了フラグ Then       Exit For     End If        Next objパターン      結果出力_パターン = True End Function '--------------------------------------------------- 'デバッグ用、コレクションのファイルへの出力 '--------------------------------------------------- Private Function コレクション出力(p_objStrem As Object, p_col出力 As Collection)   Dim i    As Integer   Dim strWk  As String   For i = 1 To p_col出力.Count     If (i <> 1) Then       strWk = strWk & vbTab     End If     strWk = strWk & p_col出力.Item(i)   Next i   p_objStrem.WriteLine strWk End Function '--------------------------------------------------- 'サイコロN個による、期待値が出現するパターンを取得する '--------------------------------------------------- Private Function 取得_パターン(ByVal p_lng期待値 As Long, ByVal p_lng全個数 As Long, ByRef p_objパターン As Class1, Optional ByVal p_lng合算値 As Long = 0, Optional ByVal p_lng使用個数 As Long = 1) As Boolean   Dim lng最小値  As Long   Dim lng最大値  As Long   Dim lng出目   As Long   Dim objパターン As Class1      '使用個数とこれまでの合計値を考慮した、最大/最小値を取得する   If Not 取得_範囲((p_lng期待値 - p_lng合算値), p_lng全個数, (p_lng全個数 - p_lng使用個数), lng最小値, lng最大値) Then     Exit Function   End If      '最小~最大値を順にループさせ、次のサイコロを利用し、期待値を絞り込み、パターンを取得する   For lng出目 = lng最小値 To lng最大値     Set objパターン = New Class1     objパターン.c出目 = lng出目          'パターンが取得できたときだけ、パターンを記憶する     If 取得_パターン(p_lng期待値, p_lng全個数, objパターン, (p_lng合算値 + lng出目), (p_lng使用個数 + 1)) Then       p_objパターン.c子供.Add objパターン     End If   Next lng出目      取得_パターン = True End Function '--------------------------------------------------- '期待値に届くための最大値/最小値を求める '--------------------------------------------------- Private Function 取得_範囲( _        ByVal p_lng期待値 As Long _       , ByVal p_lng全個数 As Long _       , ByVal p_lng残個数 As Long _       , Optional ByRef p_lng最小値 As Long _       , Optional ByRef p_lng最大値 As Long _       ) As Boolean      Dim lng最小値 As Long   Dim lng最大値 As Long      Dim lng残最小値 As Long   Dim lng残最大値 As Long         '最小値/最大値を得る   Call 取得_範囲_単純(1, lng最小値, lng最大値)   '残りの個数から可能な最小値/最大値を得る   Call 取得_範囲_単純(p_lng残個数, lng残最小値, lng残最大値)      '適正な範囲の最小値に修正   If ((lng最小値 + lng残最大値) < p_lng期待値) Then     lng最小値 = p_lng期待値 - lng残最大値   End If   '適正な範囲の最大値に修正   If ((lng最大値 + lng残最小値) > p_lng期待値) Then     lng最大値 = p_lng期待値 - lng残最小値   End If      '最大値と最小値が等しく、サイコロを使い切れなかったら抜ける   If (lng最小値 = lng最大値) And (p_lng残個数 <> 0) Then     '異常終了     Exit Function   End If      'パラメータに、取得した最小値/最大値を返却   p_lng最小値 = lng最小値   p_lng最大値 = lng最大値      '正常終了   取得_範囲 = True End Function 'サイコロの個数による、最大値/最小値を求める Private Sub 取得_範囲_単純( _        ByVal p_lng個数 As Long _       , Optional ByRef p_lng最小値 As Long _       , Optional ByRef p_lng最大値 As Long _     )   Const DEF_出目_小  As Long = 1   Const DEF_出目_大  As Long = 6      p_lng最小値 = p_lng個数 * DEF_出目_小   p_lng最大値 = p_lng個数 * DEF_出目_大 End Sub -----標準モジュール(Module1) END----- -----クラスモジュール(Class1) START----- Option Explicit '出目 Public c出目 As Integer '出目に関連する子供たちを記憶する領域 Public c子供 As Collection 'クラス派生時に、記憶領域を初期化 Private Sub Class_Initialize()   Set c子供 = New Collection End Sub -----クラスモジュール(Class1) END-----

ten99
質問者

お礼

大変長い文を書いて説明していただきありがとうございました。

その他の回答 (3)

  • perse
  • ベストアンサー率74% (113/152)
回答No.3

>サイコロを10個投げてその時に目の数の和が100になる確率 をプログラムで求めるには公式がないと無理でしょう。 Pn(x)=1/6Pn-1(x-y)が公式なのでしょうか? だとしたらy(サイコロの目?)の意味がよくわかりません。 (数学には詳しくないので変なこと聞いてたらすいません) もし、プログラムで疑似的に求めたいのなら以下のようになると思います。 '-------------------------ここから Const Times As Long = 100000 Dim lngSai As Long '振るサイコロの個数 Dim lngTotal As Long '求める合計 Dim lngSum As Long Dim lngCnt As Long Dim i As Long, j As Long Randomize 'サイコロの数と求める合計をセット(この値は例) lngSai = 30 lngTotal = 100 For i = 1 To Times lngSum = 0 For j = 1 To lngSai lngSum = lngSum + Int(Rnd() * 6) + 1 Next j '合計が求めたい合計だったらカウント+1 If lngSum = lngTotal Then lngCnt = lngCnt + 1 DoEvents Next i '30(=lngSai)回振って合計が100(=lngTotal)になる確立 MsgBox lngCnt / Times '-------------------------ここまで

ten99
質問者

お礼

分かりやすく説明していただきましてありがとうございました。

  • OsieteG00
  • ベストアンサー率35% (777/2173)
回答No.2

与式の条件がよくわかりません。 xとPn(x)の違いは何でしょう? xはどのように求めたのでしょうか? 単純に サイコロの個数n n個のサイコロを投げた時の目の和P(n) x個目のサイコロの目をf(x)とすると とおいて P(n)=Σ(X=1,n) f(x) ここで f(x)=1~6の間の整数 ではいけないのでしょうか? これで考えると、キーポイントとなるのは ・for~next文による繰り返し ・乱数の発生 ->rnd関数を使用する。rnd関数は0~1の少数の乱数を発生させる関数。乱数の範囲を変えるには、最大値を掛ければよい。整数にしたければ掛けたものをint関数で少数以下の切捨てを。randomize関数を使用すればなおよし(詳細はヘルプを参照)。 <例> 0~3の整数を発生する乱数 int(rnd()*3) <大体の流れ> SumPx=0 for i=1 to n SumPx=SumPx+(乱数) next i

ten99
質問者

補足

早速、回答して頂きありがとうございます。 私の条件式の説明がわかりにくかったみたいで申し訳ないです。 サイコロの個数nと求める目の和xの値は自分で指定して、その場合の確立を求めたいのです。 例えば、サイコロを10個投げてその時に目の数の和が100になる確率は、どのくらいか??っというものを求めようとしています。   

  • parapa
  • ベストアンサー率15% (42/273)
回答No.1

乱数について調べれば 自ずと答えが出ると思いますよ。

ten99
質問者

お礼

回答ありがとうございます。

関連するQ&A