こっそり聞きたい……7以下とは

このエントリーをはてなブックマークに追加
211混物
面白そうなことをやってるね。
混ぜてくれ…って言うかもうVBAで組んじゃったので貼っちゃう。

Dim iDie() As Integer '個々のダイスの出目。インデックスは目の大きい順。

Public Function PICKOUT(iFac As Integer, lRol As Long, lExt As Long, lSum As Long) As Double
 'iFac=面数, lRol=振った個数, lExt=選ぶ個数, lSum=選んだダイスの出目の合計
 Dim lRmn As Long, i As Long, j As Long
 ReDim iDie(lExt)        'lExt番目以降のダイスについては考慮する必要がないので変数も不要
 If lExt > lRol Or lSum < lExt Or iFac * lExt < lSum Then
  PICKOUT = 0         'ありえないので排除
  Exit Function
 End If

 SortDice iFac, lExt, lSum, 0    '初期状態として、lSumを全iDieに対しSortDiceで分配
 PICKOUT = Calc(iFac, lRol, lExt)  'まず完全に左詰めの状態で一回計算
 Do While iDie(1) > iDie(lExt) + 1 '最大の出目と最小の出目の差が1以下なら終了
 For i = lExt - 1 To 1 Step -1   '出目を最小値側からサーチ
  If iDie(i) > iDie(lExt) + 1 Then '差が2以上ある最初のダイスを取る
   iDie(i) = iDie(i) - 1     'その出目を1減らす
   For j = 1 To I         '今度は最大値側から順に
    lRmn = lRmn + iDie(j)    '出目を合計していく
   Next
   SortDice iDie(i), lExt - i, lSum - lRmn, i 'i番目以降のダイスの出目を詰める
   lRmn = 0            '何度も使いまわすので一旦クリア
   Exit For            '出目の再配分が終わったら直ちに次の処理へ
  End If
 Next
 PICKOUT = PICKOUT + Calc(iFac, lRol, lExt) '上で決定された組み合わせで場合の数を算出し加算
 Loop
 Erase iDie()
End Function
212混物:03/03/04 22:13 ID:???
Private Sub SortDice(iFac As Integer, ByVal lExt As Long, ByVal lSum As Long, lLft As Long)
'lLft以降の範囲で出目の合計値を分配。インデックスが小さいもの優先
 Dim i As Integer

 For i = 1 To lExt        'lSum分の合計値を、1番目からlExt番目に割り振る
  lExt = lExt - 1        '1個を取り出す
  If iFac > lSum - lExt Then   '出目の合計値がもう余ってない場合
   iDie(lLft + i) = lSum - lExt '以後全てに"1"を割り振るとして振れるだけ投入
   lSum = lExt         '上述の結果、配分できる残りの値は残りのダイス数に等しい
  Else              'さもなくば―つまり十分余裕があれば
   iDie(lLft + i) = iFac    'ダイスの最大値=面数を割り当てる
   lSum = lSum - iFac      '割り当てた分合計値から差し引く
  End If
 Next
End Sub
213混物:03/03/04 22:22 ID:???
Private Function Calc(iFac As Integer, lRol As Long, lExt As Long) As Double
 Dim lNum() As Long, dFxd As Double
 Dim iMin As Integer, iRmn As Integer, i As Integer, j As Integer
 ReDim lNum(1 To iFac)        '出目がインデックスに等しいダイスの個数を収める配列

 For i = 1 To lExt
  lNum(iDie(i)) = lNum(iDie(i)) + 1 'i番目のダイスの出目を調べてカウントする
 Next
 iMin = 1
 Do Until lNum(iMin) > 0       '一個以上出ているうちの最小の出目を調べる
  iMin = iMin + 1
 Loop
 iRmn = lRol             '振ったダイスの総数。lRolは後で使うのでコピー
 dFxd = 1               '出目がiMinであるダイスを除いた場合の数
 For i = iFac To iMin + 1 Step -1
  dFxd = dFxd * Comb(iRmn, lNum(i))
  iRmn = iRmn - lNum(i)
 Next
 For i = 0 To lRol - lExt       '出目がiMinのダイスはlExtを越えても良いため別扱い
  Calc = Calc + dFxd * Comb(iRmn, lNum(iMin) + i) * (iMin - 1) ^ (lRol - lExt - i)
 Next
End Function
214混物:03/03/04 22:24 ID:???
Private Function Comb(ByVal n As Long, k As Long) As Double '組み合わせの計算
 Dim i As Integer
 If k > n / 2 Then k = n - k
 n = n + 1
 Comb = 1
 For i = 1 To k
  Comb = Comb * (n - i) / i '整数ではあるが、桁が多いので倍精度浮動小数使用
 Next
End Function

'以上。ExcelでVisualBasicEditor使って標準モジュール挿入して貼り付けてください。
'PICKOUTという関数が追加されます(引数は面数、振った個数、選ぶ個数、出目、の4つ)。
'あんまりでかい数字入れると計算終わらなくなっちゃうので注意。