Pythonで等価なドミノペアの数を効率的に数える方法
ドミノのリストが与えられているとします。各ドミノは2つの数字を持っています。2つのドミノ D[i] = [a, b] と D[j] = [c, d] は、「a = c かつ b = d」または「a = d かつ b = c」が成り立つ場合に等しいとみなされます。つまり、ドミノは反転しても同一視できるということです。
ここでの課題は、0 <= i < j < ドミノの総数 を満たし、D[i] が D[j] と等価であるようなペア (i, j) の個数を求めることです。
例えば、ドミノのリストが [[1, 2], [2, 1], [3, 4], [6, 5]] の場合、[1, 2] と [2, 1] が反転関係にあるため、出力は 1 になります。
解法のアプローチ
この問題を解くには、以下の手順に従います。
- answer = 0 として初期化する
- ドミノリスト内の各ペア p に対して:
- ペア p をソートして正規化する([2, 1] → [1, 2])
- 各正規化済みドミノの出現頻度を辞書 D に記録する
- 辞書 D の各値 b に対して:
- answer := answer + (b × (b − 1)) ÷ 2 を加算する
- answer を返す
なぜ b × (b − 1) ÷ 2 なのか?
同じキー(ソート済みペア)を持つドミノが b 個存在する場合、その中から2つを選ぶ組み合わせの総数は二項係数 C(b, 2) = b × (b − 1) ÷ 2 で表されます。この公式を使うことで、すべての等価ペアを効率的に数え上げることができます。
実装例
理解を深めるために、以下のPythonコードを見てみましょう。
class Solution(object):
def numEquivDominoPairs(self, dominoes):
d = {}
ans = 0
for i in dominoes:
i.sort()
i = tuple(i)
if i not in d:
d[i] = 1
else:
d[i] += 1
for b in d.values():
ans += ((b * (b - 1)) // 2)
return ans
ob1 = Solution()
print(ob1.numEquivDominoPairs([[1,2],[2,1],[3,4],[5,6],[4,3]]))入力
[[1,2],[2,1],[3,4],[5,6],[4,3]]
出力
2
この例では、[1, 2] と [2, 1]、そして [3, 4] と [4, 3] の2組が等価なペアとなるため、結果は 2 になります。
計算量の分析
このアルゴリズムの時間計算量は O(n log n)(n 個のペアそれぞれをソートするため)、空間計算量は O(n) です。すべてのペアを総当たりで比較する O(n²) の素朴なアプローチと比較して、大幅に効率的である点が大きなメリットです。
-
Pythonで整数が回文数(パリンドローム)かどうかを判定する方法
整数が与えられたとき、それが回文数(パリンドローム)であるかどうかを判定する方法を解説します。回文数とは、前から読んでも後ろから読んでも同じ並びになる数値のことです。例えば「454」は逆順にしても「454」となるため回文数です。一方、「-565」を逆順にすると「565-」となり、マイナス記号の位置が変わるため元の数と一致せず、回文数にはなりません。解法の考え方この問題は非常にシンプルに解けます。手順は以下の通りです。1. 数値をstr()で文字列に変換する2. Pythonのスライス記法[::-1]を使って文字列を反転させる3. 元の文字列と反転した文字列を比較し、一致すればTrue、一致しな
-
Pythonで階乗を計算する3つの方法|forループ・再帰・math.factorial()の使い方
階乗(factorial)の計算は、データ分析をはじめとする数学的な処理において、Pythonでよく求められる操作の一つです。階乗とは、正の整数 n に対して、1から n までのすべての整数を掛け合わせた値のことです(例:5! = 1 × 2 × 3 × 4 × 5 = 120)。この記事では、Pythonで階乗を求める3つの方法を、コード例と実行結果とともにわかりやすく解説します。方法1:forループを使うforループで1から目的の数値まで順番に処理し、各ステップで掛け算を繰り返していく方法です。以下のプログラムでは、ユーザーに数値の入力を促し、ループ処理の前にint()で入力値を整数に変換