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
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
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