Python
 Computer >> コンピューター >  >> プログラミング >> Python

Pythonでn個のサイコロの出目の合計がtotalになる組み合わせの数を求めるプログラム


本記事では、サイコロの個数 n、各サイコロの面の数 faces、そして目標となる合計値 total が与えられたとき、n 個のサイコロを振った結果の合計が total と一致する組み合わせが何通りあるかを求める問題を解説します。答えが非常に大きな数になる可能性があるため、結果は 109 + 7 で割った余りとして返します。

例えば、入力が n = 2、faces = 6、total = 8 の場合、出力は 5 になります。これは、2つの6面ダイスで合計8を作る方法が次の5通り存在するためです。

  • (2 と 6)
  • (6 と 2)
  • (3 と 5)
  • (5 と 3)
  • (4 と 4)

解法のアプローチ:動的計画法(DP)

この問題は動的計画法を用いることで効率的に解けます。dp[j] を「これまでに振ったサイコロで合計 j を作る組み合わせの数」と定義し、サイコロを1個ずつ追加しながら表を更新していきます。具体的な手順は以下の通りです。

  • m := 109 + 7(剰余演算用の定数)を設定する
  • サイズ (total + 1) のリスト dp を作成し、すべて 0 で初期化する
  • face を 1 から min(faces, total) まで順に処理し、dp[face] := 1 とする(1個目のサイコロでその出目を出す方法は1通りだけ)
  • i が 0 から n - 2 までの範囲で、以下を繰り返す:
    • j を total から 1 まで逆順に走査する
    • dp[j] を「f が 1 以上 faces 以下で、j - f ≥ 1 を満たすすべての dp[j - f] の総和」で更新する
  • 最後に dp の末尾の要素を m で割った余りを返す

j を逆順に更新することで、同じサイコロを複数回使ってしまうことを防ぎ、1次元配列だけで状態を管理できる点がポイントです。

実装例

それでは、実際のコードを見て理解を深めましょう。

class Solution:
   def solve(self, n, faces, total):
      m = 10 ** 9 + 7
      dp = [0] * (total + 1)

      for face in range(1, min(faces, total) + 1):
         dp[face] = 1
      for i in range(n - 1):
         for j in range(total, 0, -1):
            dp[j] = sum(dp[j - f] for f in range(1, faces + 1) if j - f >= 1)
      return dp[-1] % m
ob = Solution()
n = 2
faces = 6
total = 8
print(ob.solve(n, faces, total))

入力

2,6,8

出力

5

計算量の評価

このアルゴリズムの時間計算量は O(n × total × faces)、空間計算量は O(total) です。サイコロの個数や合計値が大きくなっても、全通りを列挙する指数的な探索に比べれば大幅に効率的であり、競技プログラミングなどでも広く使われる定番のテクニックです。


  1. Pythonで最大k回の連続勝利となるゲーム結果の組み合わせ数を数える方法

    問題の概要2つの整数 n と k が与えられます。n はこれから行う試合の総数、k は許容される連続勝利の上限です。全 n 試合の勝敗(勝ち=W、負け=L)の並びのうち、「連続した勝利が k 回以下」に収まるパターンが何通りあるかを求めます。答えは非常に大きくなる可能性があるため、109 + 7 で割った余りを返します。例えば n = 3、k = 2 の場合、答えは 7 です。条件を満たすパターンは次の 7 通りです。LLL / WLL / LWL / LLW / WWL / LWW / WLW解き方のアプローチこの問題は再帰的な動的計画法(DP)で解くことができます。i 番目の試合を処理する

  2. Pythonでn個のノードから構成できる二分探索木(BST)の数を求める方法

    問題の概要互いに異なるn個のノードが与えられたとき、それらを二分探索木(BST:Binary Search Tree)として配置する方法が何通りあるかを求めることを考えます。二分探索木には「左部分木には常に親より小さい値が、右部分木には常に親より大きい値が格納される」という重要な性質があります。この問題を解くには、カタラン数(Catalan Number)を利用します。カタラン数 C(n) は、n個の異なるキーから構成できる二分探索木の総数を正確に表すことが知られています。計算式は次のとおりです。$$C(n)=\frac{(2n)!}{(n+1)!\times n!}$$例えば、入力が n =